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;
}
}