Files
MoShiro/backend/shared/Monitor.Protocol/Rpc/MonitorRpc.cs
T
2026-10-04 21:46:39 +08:00

441 lines
14 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 System.Text.Json;
using System.Text.Json.Serialization;
using Fleet.Core;
namespace Monitor.Protocol;
/// <summary>monitor.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 <c>.On(...)</c>。</summary>
public static class MonitorMethods
{
public const string TestRun = "monitor.test.run";
public const string TestCancel = "monitor.test.cancel";
public const string ProbeDisconnect = "monitor.probe.disconnect";
public const string TestStatus = "monitor.test.status";
public const string Event = "monitor.event";
public const string Host = "monitor.host";
public const string ProbeAgents = "monitor.probe.agents";
public const string ProbeAgentsChanged = "monitor.probe.agents_changed";
// Probe ↔ Agent(点对点会话)
public const string Clock = "monitor.clock";
public const string TestPhase = "monitor.test.phase";
public const string TestPhaseEnd = "monitor.test.phase_end";
public const string TestAbort = "monitor.test.abort";
}
public static class AgentEventKinds
{
public const string ProbeConnected = "probe_connected";
public const string ProbeDisconnected = "probe_disconnected";
public const string EndpointChanged = "endpoint_changed";
public const string SessionRejected = "session_rejected";
public const string ClockStep = "clock_step";
public const string Gap = "gap";
public const string CpuSaturated = "cpu_saturated";
public static readonly IReadOnlyList<string> All =
[ProbeConnected, ProbeDisconnected, EndpointChanged, SessionRejected, ClockStep, Gap, CpuSaturated];
}
public static class TestStatuses
{
public const string Queued = "queued";
public const string Running = "running";
public const string PhaseDone = "phase_done";
public const string Succeeded = "succeeded";
public const string Failed = "failed";
public const string Aborted = "aborted";
}
public sealed class TestPhase
{
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary<string, JsonElement> Params { get; set; } = [];
[JsonPropertyName("duration_s")]
public int DurationSeconds { get; set; }
}
public sealed class TestRunParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
/// <summary>在哪个 Probe 上运行:必须是当前连着这个 Agent 的 Probe。</summary>
[JsonPropertyName("probe_id")]
public ProbeId ProbeId { get; set; }
[JsonPropertyName("plan")]
public string Plan { get; set; } = "";
[JsonPropertyName("phases")]
public List<TestPhase> Phases { get; set; } = [];
}
public sealed class TestRunResult
{
[JsonPropertyName("accepted")]
public bool Accepted { get; set; }
}
public sealed class TestCancelParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
}
/// <summary>Backend → Agent:断开某个 Probe 的会话(Probe 会按退避重连,除非它已经被吊销)。</summary>
public sealed class ProbeDisconnectParams
{
[JsonPropertyName("probe_id")]
public ProbeId ProbeId { get; set; }
}
public sealed class TestStatusParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; } = "";
[JsonPropertyName("phase")]
public int? Phase { get; set; }
[JsonPropertyName("error_code")]
public string? ErrorCode { get; set; }
}
public sealed class AgentEventParams
{
/// <summary><c>probe_connected</c> 时:这个 Probe 的 id、版本、地址和连接时间(Backend 不用等下一次心跳就能显示)。</summary>
[JsonPropertyName("probe")]
public ConnectedProbe? Probe { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("at_utc_ms")]
public long AtUtcMs { get; set; }
[JsonPropertyName("detail")]
public string? Detail { get; set; }
}
public sealed class AgentEndpoint
{
[JsonPropertyName("agent_id")]
public NodeId AgentId { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("tcp_port")]
public int TcpPort { get; set; }
[JsonPropertyName("udp_ports")]
public List<int> UdpPorts { get; set; } = [];
/// <summary>Agent 面向 Probe 的 TLS 证书指纹(SHA-256,base64)。</summary>
[JsonPropertyName("spki")]
public string Spki { get; set; } = "";
}
public sealed class ProbeAgentsParams
{
/// <summary>Probe 目前持有的列表版本(用于调试;服务端总是返回当前列表)。</summary>
[JsonPropertyName("have_version")]
public long HaveVersion { get; set; }
}
public sealed class ProbeAgentsResult
{
[JsonPropertyName("version")]
public long Version { get; set; }
[JsonPropertyName("agents")]
public List<AgentEndpoint> Agents { get; set; } = [];
}
/// <summary>注册元数据(Monitor 定义,经 Fleet 的 <c>meta</c> 字段带上来)。</summary>
public sealed class AgentEnrollMeta
{
[JsonPropertyName("probe_tcp_port")]
public int ProbeTcpPort { get; set; } = 7443;
[JsonPropertyName("probe_udp_ports")]
public List<int> ProbeUdpPorts { get; set; } = [7443];
[JsonPropertyName("public_host")]
public string? PublicHost { get; set; }
[JsonPropertyName("kinds")]
public List<string> Kinds { get; set; } = [];
}
public sealed class LocalLimits
{
[JsonPropertyName("max_total_mbps")]
public double MaxTotalMbps { get; set; }
[JsonPropertyName("max_total_pps")]
public long MaxTotalPps { get; set; }
[JsonPropertyName("allowed_target_cidrs")]
public List<string> AllowedTargetCidrs { get; set; } = [];
[JsonPropertyName("max_concurrent_tests")]
public int MaxConcurrentTests { get; set; } = 1;
}
/// <summary><c>fleet.hello</c> 的附加字段。</summary>
public sealed class AgentHelloMeta
{
[JsonPropertyName("tls_spki")]
public string? TlsSpki { get; set; }
[JsonPropertyName("probe_tcp_port")]
public int? ProbeTcpPort { get; set; }
[JsonPropertyName("probe_udp_ports")]
public List<int>? ProbeUdpPorts { get; set; }
[JsonPropertyName("local_limits")]
public LocalLimits? LocalLimits { get; set; }
}
/// <summary>
/// 主机资源占用的只读快照(Agent → Backend,每隔几秒一次)。只有汇总的数字:<b>没有 CPU 型号、主机名、网卡名,也没有任何 IP 地址</b>。
/// 读不到的项是 null。
/// </summary>
public sealed class HostSnapshot
{
[JsonPropertyName("cpu_percent")]
public double? CpuPercent { get; set; }
[JsonPropertyName("mem_used")]
public long? MemUsed { get; set; }
[JsonPropertyName("mem_total")]
public long? MemTotal { get; set; }
[JsonPropertyName("disk_used")]
public long? DiskUsed { get; set; }
[JsonPropertyName("disk_total")]
public long? DiskTotal { get; set; }
/// <summary>所有物理网卡的累计收发字节(自开机以来)。</summary>
[JsonPropertyName("net_rx_total")]
public long? NetRxTotal { get; set; }
[JsonPropertyName("net_tx_total")]
public long? NetTxTotal { get; set; }
[JsonPropertyName("net_rx_bps")]
public long? NetRxBps { get; set; }
[JsonPropertyName("net_tx_bps")]
public long? NetTxBps { get; set; }
[JsonPropertyName("uptime_s")]
public long? UptimeSeconds { get; set; }
[JsonPropertyName("at_utc_ms")]
public long AtUtcMs { get; set; }
}
/// <summary>Agent 眼中的一个已连接 Probe。</summary>
public sealed class ConnectedProbe
{
[JsonPropertyName("id")]
public ProbeId Id { get; set; }
[JsonPropertyName("version")]
public string? Version { get; set; }
/// <summary>Probe 的远端地址(经过 NAT 之后的公网 IP:端口)。</summary>
[JsonPropertyName("remote")]
public string? Remote { get; set; }
[JsonPropertyName("since_utc_ms")]
public long SinceUtcMs { get; set; }
}
/// <summary><c>fleet.heartbeat</c> 的附加字段:只有进程内的计数,没有主机信息。</summary>
public sealed class AgentHeartbeatExtra
{
/// <summary>当前连着的 Probe 会话数(等于 <see cref="Probes"/> 的个数)。</summary>
[JsonPropertyName("sessions")]
public int Sessions { get; set; }
/// <summary>当前连着的 Probe:每次心跳都是完整的快照,Backend 靠它纠正错过的 probe_connected / probe_disconnected 事件。</summary>
[JsonPropertyName("probes")]
public List<ConnectedProbe> Probes { get; set; } = [];
[JsonPropertyName("pps_in")]
public long PpsIn { get; set; }
[JsonPropertyName("pps_out")]
public long PpsOut { get; set; }
[JsonPropertyName("dropped_invalid")]
public long DroppedInvalid { get; set; }
[JsonPropertyName("dropped_rate_limited")]
public long DroppedRateLimited { get; set; }
[JsonPropertyName("queue_bytes")]
public long QueueBytes { get; set; }
}
/// <summary>Agent 下发给 Probe 的一条线路任务(期望状态类型 <c>line_task_set</c> 里的一项)。</summary>
public sealed class LineTask
{
[JsonPropertyName("id")]
public TaskId Id { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary<string, JsonElement> Params { get; set; } = [];
/// <summary>数据面用它区分任务;Agent 在本进程生命周期内分配。</summary>
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("port")]
public int Port { get; set; }
}
/// <summary>这条线路的期望状态(Agent → Probe):Probe 按它调和自己一侧的角色。</summary>
public sealed class LineTaskSet
{
public const string StateType = "line_task_set";
[JsonPropertyName("version")]
public long Version { get; set; }
[JsonPropertyName("tasks")]
public List<LineTask> Tasks { get; set; } = [];
/// <summary>Agent 的数据面端口(Probe 把包发到这里)。</summary>
[JsonPropertyName("udp_ports")]
public List<int> UdpPorts { get; set; } = [];
[JsonPropertyName("tcp_ports")]
public List<int> TcpPorts { get; set; } = [];
}
/// <summary><c>monitor.clock</c>:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。</summary>
public sealed class ClockParams
{
[JsonPropertyName("probe_mono_us")]
public long ProbeMonoUs { get; set; }
}
public sealed class ClockResult
{
[JsonPropertyName("agent_mono_us")]
public long AgentMonoUs { get; set; }
}
/// <summary>按需测试的一个阶段(Agent → Probe):Agent 编排时间,Probe 准备好自己一侧的角色之后应答。</summary>
public sealed class TestPhaseStartParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("phase")]
public int Phase { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary<string, JsonElement> Params { get; set; } = [];
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("duration_s")]
public int DurationSeconds { get; set; }
[JsonPropertyName("port")]
public int Port { get; set; }
}
public sealed class TestPhaseStartResult
{
[JsonPropertyName("accepted")]
public bool Accepted { get; set; }
}
/// <summary>阶段结束(Agent → Probe):Probe 收尾并把自己一侧的列块交给 Agent。<c>sent_count</c> 是 Agent 一侧发出的包数(单向数据流用)。</summary>
public sealed class TestPhaseEndParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("phase")]
public int Phase { get; set; }
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("sent_count")]
public long SentCount { get; set; }
}
/// <summary>Probe 收尾之后回复:它交给 Agent 的列块个数(Agent 据此等待全部列块到齐再进入下一阶段)。</summary>
public sealed class TestPhaseEndResult
{
[JsonPropertyName("fragments")]
public int Fragments { get; set; }
}
public sealed class TestAbortParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
}
/// <summary>Probe 一侧的列块放在 RawChunk 头部 params 里的保留键:本块覆盖的序号范围 [from, to)(单向数据流由 Agent 一侧按它取行)。</summary>
public static class FragmentKeys
{
public const string SeqFrom = "_seq_from";
public const string SeqTo = "_seq_to";
}
[JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(LineTaskSet))]
[JsonSerializable(typeof(ClockParams))]
[JsonSerializable(typeof(ClockResult))]
[JsonSerializable(typeof(TestPhaseStartParams))]
[JsonSerializable(typeof(TestPhaseStartResult))]
[JsonSerializable(typeof(TestPhaseEndParams))]
[JsonSerializable(typeof(TestPhaseEndResult))]
[JsonSerializable(typeof(TestAbortParams))]
[JsonSerializable(typeof(TaskSet))]
[JsonSerializable(typeof(TestRunParams))]
[JsonSerializable(typeof(TestRunResult))]
[JsonSerializable(typeof(TestCancelParams))]
[JsonSerializable(typeof(ProbeDisconnectParams))]
[JsonSerializable(typeof(HostSnapshot))]
[JsonSerializable(typeof(TestStatusParams))]
[JsonSerializable(typeof(AgentEventParams))]
[JsonSerializable(typeof(ProbeAgentsParams))]
[JsonSerializable(typeof(ProbeAgentsResult))]
[JsonSerializable(typeof(AgentEnrollMeta))]
[JsonSerializable(typeof(AgentHelloMeta))]
[JsonSerializable(typeof(AgentHeartbeatExtra))]
[JsonSerializable(typeof(RawChunkHeader))]
public sealed partial class MonitorJsonContext : JsonSerializerContext;