[CI] [SKIP TESTS] Add detailed Probe-Agent connection diagnostics
This commit is contained in:
1 parent
0fd38b2367
commit
a7ebf61551
16 files changed
+376
-51
No files matched your search
@@ -56,6 +56,10 @@ public sealed class ProbeSession : DataSession
|
||||
|
||||
public string ProbeId => Info.PeerId;
|
||||
|
||||
public override string DiagnosticPeerId => ProbeId;
|
||||
|
||||
public string? CloseReason { get; set; }
|
||||
|
||||
public string IpVersion => Info.Remote is IPEndPoint endpoint && endpoint.Address.AddressFamily == AddressFamily.InterNetworkV6 && !endpoint.Address.IsIPv4MappedToIPv6 ? IpVersions.V6 : IpVersions.V4;
|
||||
|
||||
public string PathKey => ProbeId + "~" + IpVersion;
|
||||
|
||||
@@ -112,6 +112,7 @@ public sealed class PeerHub
|
||||
public async Task OnSessionAsync(PeerSessionInfo info, IRpcTransport transport, CancellationToken ct)
|
||||
{
|
||||
var probe = new ProbeSession(info) { ConnectedAtUtcMs = _time.GetUtcNow().ToUnixTimeMilliseconds() };
|
||||
var log = new ContextNodeLog(_log, ("probe", probe.ProbeId), ("ip_version", probe.IpVersion), ("control_id", Guid.NewGuid().ToString("N")));
|
||||
var rpc = new RpcSession<PeerContext>(transport, _router, new PeerContext(probe, _clock), _time);
|
||||
probe.Rpc = rpc;
|
||||
|
||||
@@ -151,6 +152,8 @@ public sealed class PeerHub
|
||||
ProbeSession? previous = _sessions.Replace(probe);
|
||||
if (previous?.Rpc is { } old)
|
||||
{
|
||||
previous.CloseReason = "replaced";
|
||||
log.Info("replacing previous probe control session");
|
||||
await old.CloseAsync("replaced", CancellationToken.None);
|
||||
}
|
||||
|
||||
@@ -159,8 +162,8 @@ public sealed class PeerHub
|
||||
|
||||
using CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
Task run = rpc.RunAsync(linked.Token);
|
||||
Task watchdog = WatchIdleAsync(probe, rpc, linked.Token);
|
||||
_log.Info("probe connected", ("probe", info.PeerId), ("version", info.Version ?? ""), ("remote", probe.RemoteAddress ?? ""));
|
||||
Task watchdog = WatchIdleAsync(probe, rpc, log, linked.Token);
|
||||
log.Info("probe connected", ("phase", "control_session"), ("version", info.Version ?? ""), ("remote", info.Remote));
|
||||
await _uplink.EmitEventAsync(AgentEventKinds.ProbeConnected, info.PeerId, Describe(probe));
|
||||
await PushAsync(probe, initial, linked.Token);
|
||||
try
|
||||
@@ -171,6 +174,12 @@ public sealed class PeerHub
|
||||
{
|
||||
// 停止
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
probe.CloseReason ??= "control_error";
|
||||
log.Warn("probe control session failed", ("exception", ex.GetType().Name), ("err", ex.Message));
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sessions.Remove(probe);
|
||||
@@ -178,7 +187,10 @@ public sealed class PeerHub
|
||||
await watchdog;
|
||||
await rpc.DisposeAsync();
|
||||
await _tasks.CloseSessionAsync(probe);
|
||||
_log.Info("probe disconnected", ("probe", info.PeerId));
|
||||
log.Info("probe disconnected", ("phase", "control_session"),
|
||||
("reason", probe.CloseReason ?? (ct.IsCancellationRequested ? "service_stopping" : "peer_closed")),
|
||||
("duration_ms", _time.GetUtcNow().ToUnixTimeMilliseconds() - probe.ConnectedAtUtcMs),
|
||||
("datagram_bound", probe.DatagramConnection is not null), ("data_remote", probe.DatagramRemoteEndPoint));
|
||||
OnProbeLost?.Invoke(probe);
|
||||
await _uplink.EmitEventAsync(AgentEventKinds.ProbeDisconnected, info.PeerId, Describe(probe));
|
||||
}
|
||||
@@ -188,8 +200,9 @@ public sealed class PeerHub
|
||||
/// Probe 每 30 秒发一次时钟同步请求,所以会话里至少每 30 秒有一帧:超过 <see cref="IdleLimit"/> 没有收到任何东西,
|
||||
/// 说明 Probe 已经静默死亡(断电、进程被冻结),关闭会话,让 Backend 看到 probe_disconnected,而不是一直显示在线。
|
||||
/// </summary>
|
||||
private async Task WatchIdleAsync(ProbeSession probe, RpcSession<PeerContext> rpc, CancellationToken ct)
|
||||
private async Task WatchIdleAsync(ProbeSession probe, RpcSession<PeerContext> rpc, INodeLog log, CancellationToken ct)
|
||||
{
|
||||
long lastSummary = _time.GetTimestamp();
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromSeconds(10), _time);
|
||||
try
|
||||
{
|
||||
@@ -197,10 +210,18 @@ public sealed class PeerHub
|
||||
{
|
||||
if (rpc.IdleFor > IdleLimit)
|
||||
{
|
||||
_log.Warn("probe session is silent, closing it", ("probe", probe.ProbeId), ("idle_s", (long)rpc.IdleFor.TotalSeconds));
|
||||
probe.CloseReason = "idle_timeout";
|
||||
log.Warn("probe session is silent, closing it", ("phase", "control_session"), ("idle_s", (long)rpc.IdleFor.TotalSeconds), ("limit_s", IdleLimit.TotalSeconds));
|
||||
await rpc.CloseAsync("idle", CancellationToken.None);
|
||||
return;
|
||||
}
|
||||
if (_time.GetElapsedTime(lastSummary) >= TimeSpan.FromSeconds(60))
|
||||
{
|
||||
lastSummary = _time.GetTimestamp();
|
||||
log.Info("probe session summary", ("phase", "control_session"), ("remote", probe.Info.Remote),
|
||||
("control_idle_s", (long)rpc.IdleFor.TotalSeconds), ("datagram_bound", probe.DatagramConnection is not null),
|
||||
("data_remote", probe.DatagramRemoteEndPoint));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -225,6 +246,7 @@ public sealed class PeerHub
|
||||
{
|
||||
if (probe.ProbeId == probeId && probe.Rpc is { } rpc)
|
||||
{
|
||||
probe.CloseReason = "disconnected";
|
||||
await rpc.CloseAsync("disconnected", CancellationToken.None);
|
||||
disconnected = true;
|
||||
}
|
||||
@@ -240,6 +262,7 @@ public sealed class PeerHub
|
||||
{
|
||||
if (!allowed.Contains(probe.ProbeId) && probe.Rpc is { } rpc)
|
||||
{
|
||||
probe.CloseReason = "revoked";
|
||||
_log.Info("probe is no longer allowed, closing its session", ("probe", probe.ProbeId));
|
||||
_ = rpc.CloseAsync("revoked", CancellationToken.None).AsTask();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user