feat(runtime): track node lifecycle and QUIC link state
This commit is contained in:
1 parent
238046d959
commit
97ab3cc740
37 files changed
+864
-35
No files matched your search
@@ -20,9 +20,12 @@ public static class LinkEndpoints
|
||||
group.MapPut("/links/{probe_id}/{agent_id}", SetAsync)
|
||||
.Api("links_set", "Force a link on or off, or give it back to the automatic setting (enabled: null)", Tag).Returns<LinkDto>().ProducesProblems(400, 401, 403, 404)
|
||||
.Requires(Capability.FleetWrite);
|
||||
group.MapPost("/links/{probe_id}/{agent_id}/retry", RetryAsync)
|
||||
.Api("links_retry", "Enable and immediately retry the Probe-to-Agent connection", Tag).Returns<LinkRetryDto>().ProducesProblems(401, 403, 404)
|
||||
.Requires(Capability.FleetWrite);
|
||||
}
|
||||
|
||||
private static async Task<IResult> ListAsync(AgentService agents, ProbeService probes, LinkService links, CancellationToken ct)
|
||||
private static async Task<IResult> ListAsync(AgentService agents, ProbeService probes, LinkService links, ProbeQuicRuntime quic, TimeProvider time, CancellationToken ct)
|
||||
{
|
||||
LinkSnapshot snapshot = await links.SnapshotAsync(ct);
|
||||
IReadOnlyList<AgentView> agentViews = await agents.ListAsync(ct);
|
||||
@@ -31,14 +34,14 @@ public static class LinkEndpoints
|
||||
{
|
||||
foreach (AgentView agent in agentViews.OrderBy(a => a.Agent.Name, StringComparer.Ordinal))
|
||||
{
|
||||
items.Add(ToDto(snapshot, probe, agent));
|
||||
items.Add(ToDto(snapshot, probe, agent, quic, time));
|
||||
}
|
||||
}
|
||||
|
||||
return TypedResults.Json(new LinkMatrixDto(snapshot.Auto, items), ApiJsonContext.Default.LinkMatrixDto);
|
||||
}
|
||||
|
||||
private static async Task<IResult> SetAsync(HttpContext http, AgentService agents, ProbeService probes, LinkService links, string probe_id, string agent_id, SetLinkRequest request, CancellationToken ct)
|
||||
private static async Task<IResult> SetAsync(HttpContext http, AgentService agents, ProbeService probes, LinkService links, ProbeQuicRuntime quic, TimeProvider time, string probe_id, string agent_id, SetLinkRequest request, CancellationToken ct)
|
||||
{
|
||||
if (await probes.GetAsync(probe_id, ct) is not { Revoked: false } probe)
|
||||
{
|
||||
@@ -52,10 +55,71 @@ public static class LinkEndpoints
|
||||
|
||||
await links.SetAsync(probe_id, agent_id, request.Enabled, ct);
|
||||
await agents.AfterAgentSetChangedAsync(ct); // 重新下发:Probe 的 Agent 列表、Agent 允许的 Probe
|
||||
return TypedResults.Json(ToDto(await links.SnapshotAsync(ct), probe, agent), ApiJsonContext.Default.LinkDto);
|
||||
if (request.Enabled == true)
|
||||
{
|
||||
await agents.NotifyProbeLinkRetryAsync(new NodeId(probe_id), agent.Agent.Id, ct);
|
||||
}
|
||||
return TypedResults.Json(ToDto(await links.SnapshotAsync(ct), probe, agent, quic, time), ApiJsonContext.Default.LinkDto);
|
||||
}
|
||||
|
||||
private static LinkDto ToDto(LinkSnapshot snapshot, ProbeView probe, AgentView agent) =>
|
||||
new(probe.Probe.Id, probe.Probe.Name, agent.Agent.Id.Value, agent.Agent.Name, snapshot.IsLinked(probe.Probe.Id, agent.Agent.Id.Value), snapshot.OverrideOf(probe.Probe.Id, agent.Agent.Id.Value),
|
||||
agent.Probes.Any(p => p.Id == probe.Probe.Id));
|
||||
private static async Task<IResult> RetryAsync(HttpContext http, AgentService agents, ProbeService probes, LinkService links, ProbeQuicRuntime quic, TimeProvider time, string probe_id, string agent_id, CancellationToken ct)
|
||||
{
|
||||
if (await probes.GetAsync(probe_id, ct) is not { Revoked: false } probe)
|
||||
{
|
||||
return Problems.Create(http, MoShiroErrors.ProbeNotFound, "probe not found");
|
||||
}
|
||||
if (await agents.GetAsync(new NodeId(agent_id), ct) is not { } agent)
|
||||
{
|
||||
return Problems.Create(http, MoShiroErrors.AgentNotFound, "agent not found");
|
||||
}
|
||||
|
||||
await links.SetAsync(probe_id, agent_id, true, ct);
|
||||
await agents.AfterAgentSetChangedAsync(ct);
|
||||
bool queued = await agents.NotifyProbeLinkRetryAsync(new NodeId(probe_id), agent.Agent.Id, ct);
|
||||
LinkDto link = ToDto(await links.SnapshotAsync(ct), probe, agent, quic, time);
|
||||
return TypedResults.Json(new LinkRetryDto(queued, link), ApiJsonContext.Default.LinkRetryDto, statusCode: StatusCodes.Status202Accepted);
|
||||
}
|
||||
|
||||
private static LinkDto ToDto(LinkSnapshot snapshot, ProbeView probe, AgentView agent, ProbeQuicRuntime quicRuntime, TimeProvider time)
|
||||
{
|
||||
bool linked = snapshot.IsLinked(probe.Probe.Id, agent.Agent.Id.Value);
|
||||
ProbeQuicLinkSnapshot? quicSnapshot = quicRuntime.Get(probe.Probe.Id, agent.Agent.Id.Value, IpVersions.V4);
|
||||
ProbeQuicLinkHealth? health = quicSnapshot?.Health;
|
||||
bool fresh = quicSnapshot is not null && time.GetUtcNow().ToUnixTimeMilliseconds() - quicSnapshot.AtMs <= 30_000;
|
||||
string state;
|
||||
string? reason = null;
|
||||
if (!linked) { state = "disabled"; reason = "线路策略已关闭"; }
|
||||
else if (probe.RuntimeStatus != NodeRuntimeStatus.Running) { state = "probe_unavailable"; reason = $"Probe 状态:{probe.RuntimeStatus}"; }
|
||||
else if (agent.RuntimeStatus != NodeRuntimeStatus.Running) { state = "agent_unavailable"; reason = $"Agent 状态:{agent.RuntimeStatus}"; }
|
||||
else if (health is null || !fresh)
|
||||
{
|
||||
state = "waiting_report";
|
||||
reason = health is null ? "Probe 尚未上报此线路的 QUIC 拨号状态" : "Probe 的线路状态上报已过期";
|
||||
}
|
||||
else if (health.Connected && agent.Probes.Any(p => p.Id == probe.Probe.Id))
|
||||
{
|
||||
state = "connected";
|
||||
}
|
||||
else if (health.State == ProbeLinkAttemptState.RetryWait)
|
||||
{
|
||||
state = "retry_wait";
|
||||
reason = health.LastError is { Length: > 0 } error ? $"QUIC 连接失败:{error}" : "QUIC 连接失败,等待自动重试";
|
||||
}
|
||||
else if (agent.Probes.All(p => p.Id != probe.Probe.Id))
|
||||
{
|
||||
state = "control_disconnected";
|
||||
reason = health.LastError ?? "Probe 尚未建立到 Agent 的测量控制会话";
|
||||
}
|
||||
else
|
||||
{
|
||||
state = "quic_connecting";
|
||||
reason = health.LastError ?? "控制会话已建立,正在建立 QUIC 测量会话";
|
||||
}
|
||||
bool connected = state == "connected";
|
||||
return new LinkDto(probe.Probe.Id, probe.Probe.Name, agent.Agent.Id.Value, agent.Agent.Name, linked,
|
||||
snapshot.OverrideOf(probe.Probe.Id, agent.Agent.Id.Value), connected, probe.RuntimeStatus, agent.RuntimeStatus,
|
||||
state, reason, health?.LastError, health?.RetryAttempt ?? 0, health?.RetryInMs);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record LinkRetryDto(bool Queued, LinkDto Link);
|
||||
Reference in new issue
Block a user