Files
MoShiro/backend/src/MoShiro.Backend.Application/Agents/HostStatusService.cs
T
justme ad5efc62ff agent: unify listening and dialing with directed mutual measurements
Replace Probe with Agent across node runtime, Fleet contracts, backend state, API and storage. Report incoming sessions and outgoing link health through each Agent uplink, and retain directed link controls and retry.

Update protocol fixtures and end-to-end coverage for the unified model; accept requested cancellation during the Fleet timeout test cleanup.
2026-10-21 11:55:36 +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 每隔几秒上报的主机资源占用快照。最新值放在内存里并实时推给订阅者;每个节点每分钟存一个样本做历史。
/// 快照里没有 CPU 型号、主机名、网卡名和 IP(节点根本不采集它们)。
/// </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);
}