using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using MoShiro.Protocol; namespace MoShiro.Backend.Application; /// /// 定时去镜像仓库取各组件(Agent、Backend 自己)最新的签名清单:CI 把清单登记给面板那一步是尽力而为的(网络不通只警告), /// 面板自己也去仓库取,Backend 的更新通知和节点的更新建议就不依赖那一步。取回来的清单要用发布公钥验过签才登记。 /// public sealed partial class ReleasePoller : BackgroundService { private static readonly TimeSpan s_startupDelay = TimeSpan.FromSeconds(20); private static readonly TimeSpan s_interval = TimeSpan.FromMinutes(5); private static readonly string[] s_components = [ReleaseService.AgentComponent, ReleaseService.BackendComponent]; private readonly ReleaseService _releases; private readonly TimeProvider _time; private readonly ILogger _log; public ReleasePoller(ReleaseService releases, TimeProvider time, ILogger log) { _releases = releases; _time = time; _log = log; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { await Task.Delay(s_startupDelay, _time, stoppingToken); using var timer = new PeriodicTimer(s_interval, _time); do { foreach (string component in s_components) { try { await _releases.GetOrFetchAsync(component, stoppingToken); } catch (Exception ex) when (ex is not OperationCanceledException) { LogFailed(ex, component); } } } while (await timer.WaitForNextTickAsync(stoppingToken)); } catch (OperationCanceledException) { // 后端正在停止 } } [LoggerMessage(Level = LogLevel.Warning, Message = "could not check the registry for the latest {Component} release")] private partial void LogFailed(Exception exception, string component); }