Files
MoShiro/backend/src/MoShiro.Backend.Application/Agents/HostStatusService.cs
T
justme 83e5120377 refactor!: rename the project from Monitor to MoShiro
Namespaces, assemblies, projects and directories, the solution, binaries,
images, systemd units, config paths, environment variables, protocol method
names, the media type and the raw chunk magic all move to MoShiro/moshiro.
Existing data and deployments are not compatible and must be recreated.
Also fix the architecture tests after the solution layout change and make
the update tests independent of their order.
2026-10-07 01:49:04 +08:00

68 lines
2.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Fleet.Core;
using MoShiro.Protocol;
namespace MoShiro.Backend.Application;
/// <summary>
/// <c>moshiro.host</c>:Agent 每隔几秒上报的主机资源占用快照。最新值放在内存里并实时推给订阅者;每个 Agent 每分钟存一个样本做历史。
/// 快照里没有 CPU 型号、主机名、网卡名和 IP(Agent 根本不采集它们)。
/// </summary>
public sealed class HostStatusService
{
private static readonly TimeSpan s_stale = TimeSpan.FromSeconds(15);
private readonly AgentRuntime _runtime;
private readonly IHostStore _store;
private readonly LiveHub _live;
private readonly TimeProvider _time;
private readonly Dictionary<NodeId, long> _lastSavedMinute = [];
private readonly object _gate = new();
public HostStatusService(AgentRuntime runtime, IHostStore store, LiveHub live, TimeProvider time)
{
_runtime = runtime;
_store = store;
_live = live;
_time = time;
}
public async ValueTask OnHostAsync(NodeId agent, HostSnapshot snapshot, CancellationToken ct)
{
long now = _time.GetUtcNow().ToUnixTimeMilliseconds();
snapshot.AtUtcMs = now; // 以 Backend 收到的时间为准:节点的时钟不可信
AgentRuntimeState state = _runtime.Get(agent);
state.Host = snapshot;
state.HostReceivedAtMs = now;
_live.Publish(new HostSignal(agent, snapshot));
long minute = now - (now % 60_000);
bool save;
lock (_gate)
{
save = !_lastSavedMinute.TryGetValue(agent, out long last) || last != minute;
if (save)
{
_lastSavedMinute[agent] = minute;
}
}
if (save)
{
await _store.SaveAsync(agent, minute, snapshot, ct);
}
}
/// <summary>最新快照;超过 15 秒没有更新就是过期(Agent 离线或者没有开启上报)。</summary>
public (HostSnapshot? Host, bool Stale) Latest(NodeId agent)
{
if (!_runtime.TryGet(agent, out AgentRuntimeState? state) || state.Host is null)
{
return (null, true);
}
return (state.Host, _time.GetUtcNow().ToUnixTimeMilliseconds() - state.HostReceivedAtMs > s_stale.TotalMilliseconds);
}
public ValueTask<IReadOnlyList<HostSample>> HistoryAsync(NodeId agent, long fromMs, long toMs, CancellationToken ct) => _store.ReadAsync(agent, fromMs, toMs, ct);
}