节点在线、应用在线、配置在线使用令牌查询
大石头 authored at 2021-12-16 19:49:30
2.74 KiB
Stardust
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NewLife.Log;

namespace StarGateway
{
    public interface IHostedService
    {
        Task StartAsync(CancellationToken cancellationToken);

        Task StopAsync(CancellationToken cancellationToken);
    }

    public class Host
    {
        #region 服务集合
        public IList<IHostedService> Services { get; } = new List<IHostedService>();

        public void Add<TService>() where TService : IHostedService, new() => Services.Add(new TService());

        public void Add(IHostedService service) => Services.Add(service);
        #endregion

        #region 开始停止
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            foreach (var item in Services)
            {
                await item.StartAsync(cancellationToken).ConfigureAwait(false);
            }
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            XTrace.WriteLine("正在优雅关闭服务……");

            // 反向顺序停止服务(先启动的后停止)
            for (var i = Services.Count - 1; i >= 0; i--)
            {
                var item = Services[i];
                try
                {
                    using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                    cts.CancelAfter(10_000); // 每个服务最多10秒

                    await item.StopAsync(cts.Token).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    XTrace.WriteLine("服务 [{0}] 停止超时,强制关闭", item.GetType().Name);
                }
                catch (Exception ex)
                {
                    XTrace.WriteException(ex);
                }
            }
        }
        #endregion

        #region 运行大循环
        private TaskCompletionSource<Object> _life;
        public void Run() => RunAsync().GetAwaiter().GetResult();

        public async Task RunAsync()
        {
            XTrace.WriteLine("Starting......");

            using var source = new CancellationTokenSource();

            _life = new TaskCompletionSource<Object>();

            AppDomain.CurrentDomain.ProcessExit += (s, e) => _life.TrySetResult(null);
            Console.CancelKeyPress += (s, e) => _life.TrySetResult(null);

            await StartAsync(source.Token);
            XTrace.WriteLine("Application started. Press Ctrl+C to shut down.");

            await _life.Task;

            XTrace.WriteLine("Application is shutting down...");

            await StopAsync(source.Token);

            XTrace.WriteLine("Stopped!");
        }
        #endregion
    }
}