web: one line per node pair and RTT/loss on the link board
A pair of nodes now has a single line, dialed by the non-listening side or by the smaller id when both listen, so RTT is measured once. The board draws it undirected and colours it by RTT and loss; a manual setting applies to the pair.
This commit is contained in:
1 parent
e0f6667cd7
commit
48ffa56285
6 files changed
+172
-72
No files matched your search
@@ -9,7 +9,7 @@ using MoShiro.Protocol;
|
||||
namespace MoShiro.Backend.Api;
|
||||
|
||||
/// <summary>
|
||||
/// 线路开关:哪些(拨号方,监听方)要连。自动连接的总开关是设置 <c>fleet.auto_link</c>(不监听的节点自动拨所有监听节点);
|
||||
/// 线路开关:哪些(拨号方,监听方)要连,一对节点一条线路。自动连接的总开关是设置 <c>fleet.auto_link</c>(不监听的节点拨监听节点;两个都监听时 id 较小的一方拨);
|
||||
/// 这里看每一对的结果,并且手动强制开、关或取消手动设置。把一个节点拖到另一个监听节点上建立线路,就是 <c>PUT</c> 一个 <c>enabled: true</c>。
|
||||
/// </summary>
|
||||
public static class LinkEndpoints
|
||||
@@ -34,11 +34,21 @@ public static class LinkEndpoints
|
||||
LinkSnapshot snapshot = await links.SnapshotAsync(ct);
|
||||
AgentView[] nodes = [.. (await agents.ListAsync(ct)).Where(n => n.Agent.Status == NodeStatus.Active).OrderBy(n => n.Agent.Name, StringComparer.Ordinal)];
|
||||
var items = new List<LinkDto>();
|
||||
foreach (AgentView dialer in nodes)
|
||||
for (int i = 0; i < nodes.Length; i++)
|
||||
{
|
||||
foreach (AgentView listener in nodes.Where(n => n.Agent.Listens && n.Agent.Id != dialer.Agent.Id))
|
||||
for (int j = i + 1; j < nodes.Length; j++)
|
||||
{
|
||||
items.Add(ToDto(snapshot, dialer, listener, runtime, time));
|
||||
// 一对节点一条线路:连着的那个方向,没连就是默认方向;两个都不监听的一对没有线路
|
||||
(AgentView Dialer, AgentView Listener)? pair =
|
||||
snapshot.IsLinked(nodes[i].Agent, nodes[j].Agent) ? (nodes[i], nodes[j])
|
||||
: snapshot.IsLinked(nodes[j].Agent, nodes[i].Agent) ? (nodes[j], nodes[i])
|
||||
: LinkSnapshot.IsDefaultDialer(nodes[i].Agent, nodes[j].Agent) ? (nodes[i], nodes[j])
|
||||
: LinkSnapshot.IsDefaultDialer(nodes[j].Agent, nodes[i].Agent) ? (nodes[j], nodes[i])
|
||||
: null;
|
||||
if (pair is { } p)
|
||||
{
|
||||
items.Add(ToDto(snapshot, p.Dialer, p.Listener, runtime, time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +158,7 @@ public static class LinkEndpoints
|
||||
|
||||
bool inBackoff = health?.Control.State == ControlState.Backoff;
|
||||
return new LinkDto(dialer.Agent.Id.Value, dialer.Agent.Name, listener.Agent.Id.Value, listener.Agent.Name, linked,
|
||||
snapshot.OverrideOf(dialer.Agent.Id.Value, listener.Agent.Id.Value), state == "connected", dialer.RuntimeStatus, listener.RuntimeStatus,
|
||||
snapshot.PairOverride(dialer.Agent.Id.Value, listener.Agent.Id.Value), state == "connected", dialer.RuntimeStatus, listener.RuntimeStatus,
|
||||
state, reason, (inBackoff ? health!.Control.LastError : health?.Task.Quic.LastError)?.ToString(),
|
||||
health is null ? 0 : inBackoff ? health.Control.RetryAttempt : health.Task.Quic.RetryAttempt,
|
||||
health is null ? null : inBackoff ? health.Control.RetryInMs : health.Task.Quic.RetryInMs);
|
||||
|
||||
@@ -11,20 +11,57 @@ public sealed class LinkSnapshot
|
||||
_overrides = overrides;
|
||||
}
|
||||
|
||||
/// <summary>自动连接:开着时,所有节点自动拨其他监听节点;关着时,只有手动开启的才连。两个监听节点默认双向互连。</summary>
|
||||
/// <summary>自动连接:开着时,每一对节点自动连成一条线路;关着时,只有手动开启的才连。</summary>
|
||||
public bool Auto { get; }
|
||||
|
||||
/// <summary>手动设置:<c>true</c> 强制连、<c>false</c> 强制不连、<c>null</c> 没设置(跟随自动连接)。</summary>
|
||||
/// <summary>手动设置:<c>true</c> 强制连、<c>false</c> 强制不连、<c>null</c> 没设置(跟随自动连接)。只看这一个方向。</summary>
|
||||
public bool? OverrideOf(string dialerId, string listenerId) => _overrides.TryGetValue((dialerId, listenerId), out bool value) ? value : null;
|
||||
|
||||
/// <summary>这一对最终连不连:手动设置优先,没有就看自动连接。</summary>
|
||||
public bool IsLinked(AgentRecord dialer, AgentRecord listener) =>
|
||||
dialer.Id != listener.Id && (OverrideOf(dialer.Id.Value, listener.Id.Value) ?? Auto);
|
||||
/// <summary>这一对节点(不分方向)的手动设置:当前方向有设置就用它,否则看反方向。</summary>
|
||||
public bool? PairOverride(string dialerId, string listenerId) => OverrideOf(dialerId, listenerId) ?? OverrideOf(listenerId, dialerId);
|
||||
|
||||
/// <summary>
|
||||
/// 自动连接时这一对由谁拨:只有一方监听,就由不监听的一方拨;两方都监听,由 id 较小的一方拨。
|
||||
/// 一对节点只有一条线路、一个连接、一份 RTT;上下行都在这条连接上测,不再各拨一次。
|
||||
/// </summary>
|
||||
public static bool IsDefaultDialer(AgentRecord dialer, AgentRecord listener) =>
|
||||
listener.Listens && (!dialer.Listens || string.CompareOrdinal(dialer.Id.Value, listener.Id.Value) < 0);
|
||||
|
||||
/// <summary>
|
||||
/// 这一对最终连不连、是不是由这个方向拨:手动设置优先(<c>true</c> 的方向就是拨号方,两个方向都设了 <c>true</c> 时按默认方向),
|
||||
/// 对端不监听时反方向拨不了;有 <c>false</c> 就整对不连;没有设置就看自动连接和默认方向。
|
||||
/// </summary>
|
||||
public bool IsLinked(AgentRecord dialer, AgentRecord listener)
|
||||
{
|
||||
if (dialer.Id == listener.Id || !listener.Listens)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool? forward = OverrideOf(dialer.Id.Value, listener.Id.Value);
|
||||
bool? reverse = OverrideOf(listener.Id.Value, dialer.Id.Value);
|
||||
if (forward == true)
|
||||
{
|
||||
return reverse != true || !dialer.Listens || IsDefaultDialer(dialer, listener);
|
||||
}
|
||||
|
||||
if (reverse == true && dialer.Listens)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (forward == false || reverse == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Auto && IsDefaultDialer(dialer, listener);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 哪些(拨号方,监听方)要连成线路。由面板决定、拨号方主动拨出:自动连接开着时,所有节点拨其他监听节点(默认),
|
||||
/// 关着时只连手动选中的;每一对还可以手动强制开或关,每个方向的线路独立设置。手动设置存在设置表里(<c>link.<拨号方>.<监听方></c>,值 1 或 0,空表示没设置)。
|
||||
/// 哪些(拨号方,监听方)要连成线路。由面板决定、拨号方主动拨出:每一对节点只有一条线路,自动连接开着时都连(谁拨见 <see cref="LinkSnapshot.IsDefaultDialer"/>),
|
||||
/// 关着时只连手动选中的;每一对还可以手动强制开或关,并且用 <c>true</c> 选定由哪一方拨。手动设置存在设置表里(<c>link.<拨号方>.<监听方></c>,值 1 或 0,空表示没设置),一对节点最多保留一条。
|
||||
/// 判断结果分别用在拨号方的拨号目标、监听方允许的拨号方(<c>peers</c>)和线路目录里;改动之后要调用 <see cref="AgentService.AfterAgentSetChangedAsync"/> 重新下发。
|
||||
/// </summary>
|
||||
public sealed class LinkService
|
||||
@@ -54,9 +91,12 @@ public sealed class LinkService
|
||||
return new LinkSnapshot(all.GetValueOrDefault(SettingsService.AutoLink) != "false", overrides);
|
||||
}
|
||||
|
||||
/// <summary>手动设置一对线路:<c>true</c> 强制连、<c>false</c> 强制不连、<c>null</c> 取消手动设置。</summary>
|
||||
public ValueTask SetAsync(string dialerId, string listenerId, bool? enabled, CancellationToken ct) =>
|
||||
_settings.SetAsync($"{Prefix}{dialerId}.{listenerId}", enabled is null ? "" : enabled.Value ? "1" : "0", ct);
|
||||
/// <summary>手动设置一对线路:<c>true</c> 强制连并由 <paramref name="dialerId"/> 拨、<c>false</c> 强制不连、<c>null</c> 取消手动设置。反方向的旧设置同时清掉。</summary>
|
||||
public async ValueTask SetAsync(string dialerId, string listenerId, bool? enabled, CancellationToken ct)
|
||||
{
|
||||
await _settings.SetAsync($"{Prefix}{listenerId}.{dialerId}", "", ct);
|
||||
await _settings.SetAsync($"{Prefix}{dialerId}.{listenerId}", enabled is null ? "" : enabled.Value ? "1" : "0", ct);
|
||||
}
|
||||
|
||||
/// <summary>节点被删除:清掉所有涉及它的手动设置。</summary>
|
||||
public async ValueTask ForgetNodeAsync(string nodeId, CancellationToken ct)
|
||||
|
||||
Reference in new issue
Block a user