using System.Diagnostics.CodeAnalysis; using Fleet.Rpc; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace Fleet.Server; /// Fleet 服务端的接入点:产品在这里挂上自己的状态、收件箱、事件和 RPC 方法。 public sealed class FleetServerBuilder { private readonly List, IServiceProvider>> _routeConfigurators = []; private MethodTable? _methods; private Func? _roleOf; internal FleetServerBuilder(IServiceCollection services) { Services = services; } public IServiceCollection Services { get; } internal IReadOnlyList, IServiceProvider>> RouteConfigurators => _routeConfigurators; internal MethodTable Methods => _methods ?? throw new InvalidOperationException("call WithMethods(...) when adding the Fleet server: the method table is required"); internal Func? RoleOf => _roleOf; /// /// 方法声明表(框架的加产品的,见 )和节点的角色解析(例如按节点 ID 前缀区分 Agent 和 Probe)。必须设置: /// 路由器据此核对每个处理函数的方向、类型和角色,并拒绝没有声明或不该由节点发起的方法。 /// public FleetServerBuilder WithMethods(MethodTable methods, Func roleOf) { _methods = methods; _roleOf = roleOf; return this; } public FleetServerBuilder AddState<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TProvider>() where TProvider : class, IStateProvider { Services.AddSingleton(); return this; } public FleetServerBuilder AddInbox<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TInbox>() where TInbox : class, IInbox { Services.AddSingleton(); return this; } public FleetServerBuilder OnNodeEvent<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSink>() where TSink : class, INodeEventSink { Services.AddSingleton(); return this; } public FleetServerBuilder OnEnrolled<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THook>() where THook : class, IEnrollmentHook { Services.AddSingleton(); return this; } /// 注册产品的 RPC 方法(moshiro.* 等)。处理函数通过 在调用时解析服务,避免构造期的循环依赖。 public FleetServerBuilder MapMethods(Action, IServiceProvider> configure) { _routeConfigurators.Add(configure); return this; } } public static class FleetServerExtensions { /// 需要事先在 DI 中注册 。 public static FleetServerBuilder AddFleetServer(this IServiceCollection services, Action? configure = null) { var options = new FleetServerOptions(); configure?.Invoke(options); services.AddSingleton(options); var builder = new FleetServerBuilder(services); services.AddSingleton(provider => { ILogger logger = provider.GetRequiredService().CreateLogger("Fleet.Rpc"); Func? roleOf = builder.RoleOf; var router = new RpcRouter( RpcSide.Server, builder.Methods, callerRole: roleOf is null ? null : context => roleOf(context.Node), onUnhandled: (method, ex) => FleetLog.HandlerFailed(logger, ex, method)); foreach (Action, IServiceProvider> configurator in builder.RouteConfigurators) { configurator(router, provider); } return new FleetRouter(router, roleOf); }); services.AddSingleton(provider => new FleetLinkHost( provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService(), provider.GetRequiredService>(), provider.GetServices(), provider.GetServices(), provider.GetServices(), provider.GetRequiredService(), provider.GetService())); services.AddSingleton(provider => provider.GetRequiredService()); services.AddSingleton(provider => provider.GetRequiredService()); return builder; } /// 映射 WebSocket 端点。调用方需要先 app.UseWebSockets()。 public static IEndpointRouteBuilder MapFleetLink(this IEndpointRouteBuilder app, string path) { app.Map(path, async (HttpContext http) => { if (!http.WebSockets.IsWebSocketRequest) { http.Response.StatusCode = StatusCodes.Status400BadRequest; return; } using System.Net.WebSockets.WebSocket socket = await http.WebSockets.AcceptWebSocketAsync(); await http.RequestServices.GetRequiredService().RunConnectionAsync(socket, http.RequestAborted); }); return app; } }