refactor!: rename the project from Monitor to MoShiro [CI]
ci/woodpecker/push/main-amd64-build Pipeline was successful

Namespaces, assemblies, solution, binaries, images, systemd units, config paths, environment variables, protocol methods and the Linux user are all renamed. Breaking: existing databases, node identities and installed units must be recreated.
This commit is contained in:
justme committed 2026-10-07 02:03:25 +08:00
1 parent 27ac7fb787
commit 2e146d4da7
309 files changed
+1565 -1572

No files matched your search

@@ -0,0 +1,49 @@
using Fleet.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MoShiro.Backend.Gateway;
/// <summary>
/// 宿主开始停止时先关闭所有 Agent 的长连接(Close 握手,不发 revoked:Agent 会重连)。
/// 否则 Kestrel 的有序停止要等满 30 秒的超时,才会放弃这些永远不会自己结束的 WebSocket。
/// </summary>
public sealed class ConnectionShutdown : IHostedService
{
private readonly IServiceProvider _services;
private readonly IHostApplicationLifetime _lifetime;
private Task? _closing;
public ConnectionShutdown(IServiceProvider services, IHostApplicationLifetime lifetime)
{
_services = services;
_lifetime = lifetime;
}
public Task StartAsync(CancellationToken cancellationToken)
{
// ApplicationStopping 在有序停止的最开始触发,早于 Kestrel 等待连接结束:这里不阻塞,关闭在后台进行,StopAsync 再等它做完
_lifetime.ApplicationStopping.Register(() => _closing = CloseAsync());
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (_closing is { } closing)
{
await closing.WaitAsync(TimeSpan.FromSeconds(5), cancellationToken);
}
}
private async Task CloseAsync()
{
try
{
await _services.GetRequiredService<IFleetConnections>().CloseAllAsync("server shutting down");
}
catch (Exception ex) when (ex is ObjectDisposedException or InvalidOperationException)
{
// 停止过程中,服务已经在释放:Kestrel 的超时兜底
}
}
}