using System.Collections.Concurrent; using Fleet.Core; using Monitor.Protocol; namespace Monitor.Backend.Application; /// Agent 眼中的一个已连接 Probe(Agent 在事件和心跳里上报)。 public sealed record ConnectedProbeInfo(string Id, string? Version, string? Remote, long SinceMs, PlatformInfo? Platform = null); /// Agent 的运行时状态(只在内存中):连着哪些 Probe、最近的进程内计数、端点变化次数。 public sealed class AgentRuntimeState { private readonly object _gate = new(); private int _endpointChanges; private IReadOnlyList _probes = []; /// 当前连着这个 Agent 的 Probe(每个 Probe 一项)。 public IReadOnlyList Probes => Volatile.Read(ref _probes); public bool ProbeConnected => Probes.Count > 0; public bool IsConnected(string probeId) => Probes.Any(p => p.Id == probeId); /// 事件 probe_connected:事件里带着 Probe 的版本和地址;心跳里的完整快照随后会再校正一次。 public void AddProbe(ConnectedProbeInfo info) { lock (_gate) { Volatile.Write(ref _probes, [.. _probes.Where(p => p.Id != info.Id), info]); } } public void RemoveProbe(string id) { lock (_gate) { Volatile.Write(ref _probes, [.. _probes.Where(p => p.Id != id)]); } } /// 心跳里的完整快照:覆盖事件推算出的结果。 public void ReplaceProbes(IEnumerable probes) { lock (_gate) { Volatile.Write(ref _probes, [.. probes.OrderBy(p => p.SinceMs).ThenBy(p => p.Id, StringComparer.Ordinal)]); } } public void ClearProbes() => ReplaceProbes([]); /// 最新的主机资源占用快照,以及收到它的时间。 public HostSnapshot? Host { get; set; } public long HostReceivedAtMs { get; set; } public AgentHeartbeatExtra? LastHeartbeat { get; set; } public long LastHeartbeatAtMs { get; set; } public int EndpointChanges => Volatile.Read(ref _endpointChanges); public void CountEndpointChange() => Interlocked.Increment(ref _endpointChanges); } public sealed class AgentRuntime { private readonly ConcurrentDictionary _states = new(); public AgentRuntimeState Get(NodeId agent) => _states.GetOrAdd(agent, _ => new AgentRuntimeState()); public bool TryGet(NodeId agent, out AgentRuntimeState state) => _states.TryGetValue(agent, out state!); public void Remove(NodeId agent) => _states.TryRemove(agent, out _); /// 某个 Probe 现在连着哪些 Agent。 public IReadOnlyList<(NodeId Agent, ConnectedProbeInfo Probe)> ConnectionsOf(string probeId) { var result = new List<(NodeId, ConnectedProbeInfo)>(); foreach ((NodeId agent, AgentRuntimeState state) in _states) { foreach (ConnectedProbeInfo probe in state.Probes) { if (probe.Id == probeId) { result.Add((agent, probe)); } } } return result; } /// Agent 断开时,它对 Probe 会话的认知也随之失效。 public void MarkOffline(NodeId agent) { if (_states.TryGetValue(agent, out AgentRuntimeState? state)) { state.ClearProbes(); } } }