Files
MRCC/launcher/PCL-CE/PCL.Core/App/IoC/LifecycleService.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

403 lines
17 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace PCL.Core.App.IoC;
partial class Lifecycle
{
private static readonly ConcurrentDictionary<string, LifecycleServiceInfo> _RunningServiceInfoMap = [];
private static readonly ConcurrentStack<ILifecycleService> _StartedServiceStack = [];
private static readonly Dictionary<string, ILifecycleService> _ManualServiceMap = [];
private static readonly HashSet<ILifecycleService> _DeclaredStoppedServices = [];
private static readonly ConcurrentDictionary<string, Exception> _ServiceLastExceptionMap = [];
/// <summary>
/// 所有正在运行的服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)
/// </summary>
public static ICollection<string> RunningServices => _RunningServiceInfoMap.Keys;
/// <summary>
/// 服务项启动前触发的事件。<br/>
/// 该事件会在与服务项初始化相同的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string>? ServiceStarting;
/// <summary>
/// 服务项启动后触发的事件。<br/>
/// 该事件会在与服务项初始化相同的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string>? ServiceStarted;
/// <summary>
/// 服务项停止前触发的事件。特别地,主动声明停止 (<see cref="LifecycleContext.DeclareStopped"/>) 的服务不会触发该事件。<br/>
/// 该事件会在与服务项停止相同的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string>? ServiceStopping;
/// <summary>
/// 服务项停止后触发的事件。特别地,主动声明停止 (<see cref="LifecycleContext.DeclareStopped"/>) 的服务不会触发该事件。<br/>
/// 该事件会在与服务项停止相同的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string>? ServiceStopped;
/// <summary>
/// 服务项主动声明停止 (<see cref="LifecycleContext.DeclareStopped"/>) 触发的事件。<br/>
/// 该事件会在与服务项初始化相同的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string>? ServiceDeclaredStopped;
/// <summary>
/// 服务项抛出异常时触发的事件。<br/>
/// 该事件会在服务抛出异常的线程中执行。请注意,该事件可能在多个不同线程中被同时调用。
/// </summary>
public static event Action<string, Exception>? ServiceUnhandledException;
private static string _ServiceName(ILifecycleService service, LifecycleState? state = null)
{
#if DEBUG
var info = GetServiceInfo(service.Identifier);
if (info is not null) state = info.StartState;
var stateText = (state is null) ? "" : $"{state}/";
return $"{service.Name} ({stateText}{service.Identifier})";
#else
return service.Name;
#endif
}
private static Task _StartServiceTask(ILifecycleService service, bool manual = false)
{
ILifecycleLogService? logService = null;
// 检测日志服务
if (service is ILifecycleLogService ls)
{
if (_logService is not null) throw new InvalidOperationException("日志服务只能有一个");
logService = ls;
}
var state = manual ? LifecycleState.Manual : CurrentState;
var name = _ServiceName(service, state);
// 确保不存在重复的标识符
lock (_ManualServiceMap) {
if (_ManualServiceMap.ContainsKey(service.Identifier) && IsServiceRunning(service.Identifier))
{
Context.Warn($"{name} 标识符重复,已跳过");
return Task.CompletedTask;
}
// 先找个东西占着防止异步加载中检测逻辑失效
_RunningServiceInfoMap[service.Identifier] = _SystemServiceInfo;
}
// 运行服务项并添加到正在运行列表
return service.SupportAsync ? Task.Run(AsyncCall) : AsyncCall();
async Task AsyncCall()
{
try
{
Context.Trace($"正在启动 {name}");
ServiceStarting?.Invoke(service.Identifier);
await service.StartAsync().ConfigureAwait(!service.SupportAsync);
var serviceInfo = new LifecycleServiceInfo(service, state);
Context.Debug($"{name} 启动成功");
if (_DeclaredStoppedServices.Contains(service))
{
_DeclaredStoppedServices.Remove(service);
ServiceDeclaredStopped?.Invoke(service.Identifier);
Context.Trace($"{name} 已中止");
}
else
{
// 若该服务未声明自己已结束运行,将其添加到正在运行列表
_StartedServiceStack.Push(service);
_RunningServiceInfoMap[service.Identifier] = serviceInfo;
ServiceStarted?.Invoke(service.Identifier);
}
}
catch (Exception ex)
{
Context.Warn($"{name} 启动失败,尝试停止", ex);
// 存储异常并停止服务
_UpdateLastException(service, ex);
_StopService(service, false);
}
// 若日志服务已启动则清空日志缓冲
if (logService is null) return;
lock (_PendingLogs)
{
foreach (var item in _PendingLogs) _PushLog(item, logService);
_PendingLogs.Clear();
_logService = logService;
}
}
}
private static Type[] _GetServiceTypes(LifecycleState state) => LifecycleServiceTypes.GetServiceTypes(state);
private static ILifecycleService _CreateService(Type type)
{
var fullname = type.FullName;
try
{
SystemContext.Trace($"正在实例化 {fullname}");
var instance = (ILifecycleService)Activator.CreateInstance(type, true)!;
var supportAsyncText = instance.SupportAsync ? "异步" : "同步";
SystemContext.Trace($"实例化完成: {instance.Name} ({instance.Identifier}), 启动方式: {supportAsyncText}");
return instance;
}
catch (Exception ex)
{
SystemContext.Fatal($"注册服务项实例化失败: {fullname}", ex);
throw;
}
}
private static void _LogStateCount(TimeSpan count, LifecycleState state)
{
Context.Debug($"状态 {state} 共用时 {Math.Round(count.TotalMilliseconds)} ms");
}
private static void _InitializeAndStartStateServices(LifecycleState state)
{
var types = _GetServiceTypes(state);
if (types.Length == 0) return; // 跳过空列表
var asyncInstances = new List<ILifecycleService>();
// 运行非异步启动服务并存储异步启动服务
foreach (var service in types)
{
var instance = _CreateService(service);
if (instance.SupportAsync) asyncInstances.Add(instance);
else _StartServiceTask(instance).ConfigureAwait(false).GetAwaiter().GetResult();
if (_hasRequestedStopLoading) return; // 若请求停止加载则提前结束
}
// 运行异步启动服务并等待所有服务启动完成
Task.WaitAll(asyncInstances.Select(instance => _StartServiceTask(instance)).ToArray());
}
private static void _StartStateFlow(LifecycleState start, LifecycleState? end = null, bool count = true)
{
var index = (int)start;
var endIndex = end is null ? index : (int)end;
while (index <= endIndex)
{
DateTime? countStart = count ? DateTime.Now : null; //开始计时
var state = (LifecycleState)index;
_NextState(state);
_InitializeAndStartStateServices(state);
if (countStart is { } s)
{
var countSpan = DateTime.Now - s; // 结束计时
_LogStateCount(countSpan, state);
}
index++;
}
}
private static void _StartWorker(LifecycleState state, LifecycleState? wait = null, bool count = true)
{
new Thread(() =>
{
_StartStateFlow(state, count: count);
if (wait is { } w) WaitForState(w);
})
{ IsBackground = true, Name = $"Lifecycle/{state}" }.Start();
}
private static void _RemoveRunningInstance(ILifecycleService service)
{
_RunningServiceInfoMap.TryRemove(service.Identifier, out var removed);
removed?.MarkAsStopped();
}
private static readonly ConcurrentBag<Task> _StoppingServiceTasks = [];
private static void _WaitStoppingServiceTasks()
{
Task.WaitAll(_StoppingServiceTasks.ToArray());
}
private static void _StopService(ILifecycleService service, bool async, bool manual = false)
{
var name = _ServiceName(service, manual ? LifecycleState.Manual : CurrentState);
if (async) _StoppingServiceTasks.Add(Task.Run(Stop));
else Stop().ConfigureAwait(false).GetAwaiter().GetResult();
return;
async Task Stop()
{
try
{
Context.Trace($"正在停止 {name}");
ServiceStopping?.Invoke(service.Identifier);
await service.StopAsync().ConfigureAwait(!async);
ServiceStopped?.Invoke(service.Identifier);
Context.Debug($"{name} 已停止");
}
catch (Exception ex)
{
// 若出错则存储异常并忽略
Context.Warn($"停止 {name} 时出错,已跳过", ex);
_UpdateLastException(service, ex);
}
// 从正在运行列表移除
_RemoveRunningInstance(service);
}
}
private static void _UpdateLastException(ILifecycleService service, Exception ex)
{
_ServiceLastExceptionMap[service.Identifier] = ex;
ServiceUnhandledException?.Invoke(service.Identifier, ex);
}
/// <summary>
/// 检查指定标识符的服务项是否正在运行
/// </summary>
/// <param name="identifier">服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)</param>
/// <returns>服务项是否正在运行</returns>
public static bool IsServiceRunning(string identifier) => _RunningServiceInfoMap.ContainsKey(identifier);
/// <summary>
/// 根据标识符获取正在运行的服务项的相关信息
/// </summary>
/// <param name="identifier">服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)</param>
/// <returns>服务项信息</returns>
public static LifecycleServiceInfo? GetServiceInfo(string? identifier)
{
if (identifier is null) return null;
_RunningServiceInfoMap.TryGetValue(identifier, out var info);
return info;
}
/// <summary>
/// 获取服务项初始化或结束逻辑的最后一次异常。
/// </summary>
/// <param name="identifier">服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)</param>
/// <returns>异常实例,若未出现异常则为 <c>null</c></returns>
public static Exception? GetServiceLastException(string identifier)
{
var result = _ServiceLastExceptionMap.TryGetValue(identifier, out var ex);
return result ? ex : null;
}
/// <summary>
/// 手动请求启动一个周期为 <see cref="LifecycleState.Manual"/> 的服务项。
/// </summary>
/// <param name="identifier">服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)</param>
/// <param name="async">是否异步启动,默认遵循服务项自身的声明</param>
/// <returns>是否成功请求启动,若该服务项正在运行或周期不是 <see cref="LifecycleState.Manual"/> 则无法启动</returns>
public static bool StartService(string identifier, bool? async = null)
{
_ManualServiceMap.TryGetValue(identifier, out var service);
if (service is null || IsServiceRunning(identifier)) return false;
async ??= service.SupportAsync;
if (async == true) Task.Run(() => _StartServiceTask(service, true));
else _StartServiceTask(service, true);
return true;
}
/// <summary>
/// 手动请求停止一个周期为 <see cref="LifecycleState.Manual"/> 的服务项。
/// </summary>
/// <param name="identifier">服务项标识符(即 <see cref="ILifecycleService.Identifier"/> 属性)</param>
/// <param name="async">是否异步停止,默认为 <c>true</c></param>
/// <returns>是否成功请求停止,若该服务项未运行或周期不是 <see cref="LifecycleState.Manual"/> 则无法停止</returns>
public static bool StopService(string identifier, bool async = true)
{
_ManualServiceMap.TryGetValue(identifier, out var service);
if (service is null || !IsServiceRunning(identifier)) return false;
_StopService(service, async, true);
return true;
}
/// <summary>
/// 运行自定义服务项。该服务项将使用当前生命周期状态作为启动状态,若无特殊需求请尽可能不要使用,而是直接注册服务项。
/// </summary>
/// <param name="service">服务项实例</param>
/// <returns>是否成功请求运行,若标识符与正在运行的服务或已注册的手动服务冲突则无法运行。</returns>
public static bool StartCustomService(ILifecycleService service)
{
if (IsServiceRunning(service.Identifier) || _ManualServiceMap.ContainsKey(service.Identifier)) return false;
_StartServiceTask(service);
return true;
}
}
/// <summary>
/// 用于特定生命周期的服务模型。<br/>
/// 实现特殊的子接口 <see cref="ILifecycleLogService"/> 以声明自己是日志服务。
/// </summary>
public interface ILifecycleService
{
/// <summary>
/// 全局唯一标识符,统一使用纯小写字母与 “-” 的命名格式,如 <c>logger</c> <c>yggdrasil-server</c> 等。
/// </summary>
public string Identifier { get; }
/// <summary>
/// 友好名称,如 “日志” “验证服务端” 等,将会用于记录日志等场合。
/// </summary>
public string Name { get; }
/// <summary>
/// 声明该服务是否支持异步启动。
/// 每个生命周期均会依次同步启动不支持异步启动的服务,然后依次异步启动支持异步启动的服务,启动的执行顺序遵循声明的优先级。<br/>
/// 支持异步启动对启动器整体启动速度有一定帮助,在允许的情况下应尽最大可能支持。
/// </summary>
public bool SupportAsync { get; }
/// <summary>
/// 启动该服务。应由生命周期管理自动调用,若无特殊情况,请勿手动调用。
/// </summary>
public Task StartAsync();
/// <summary>
/// 停止该服务。应由生命周期管理自动调用,若无特殊情况,请勿手动调用。
/// </summary>
public Task StopAsync();
}
/// <summary>
/// 生命周期服务项的信息记录
/// </summary>
public record LifecycleServiceInfo
{
private readonly ILifecycleService _service;
public string Identifier => _service.Identifier;
public string Name => _service.Name;
public bool CanStartAsync => _service.SupportAsync;
public LifecycleState StartState { get; }
/// <summary>
/// 服务开始运行的时间。初始值为调用 <c>Start()</c> 方法的时刻,在 <c>Start()</c> 方法结束之后会更新一次。
/// </summary>
public DateTime StartTime { get; init; } = DateTime.Now;
/// <summary>
/// 附带启动状态的完整标识符。
/// </summary>
public string FullIdentifier => $"{StartState}/{Identifier}";
/// <summary>
/// 服务是否正常运行,若已停止则该值为 <c>false</c>,否则为 <c>true</c>。
/// </summary>
public bool IsStopped { get; private set; } = false;
/// <summary>
/// 将该服务标记为已停止,将不会在程序退出流程中调用该服务的 <c>Stop()</c> 方法。
/// </summary>
public void MarkAsStopped() => IsStopped = true;
/// <summary>
/// 本 record 应由生命周期管理自动构造,若无特殊情况,请勿手动调用。
/// </summary>
/// <param name="service">生命周期服务项实例</param>
/// <param name="startState">启动的生命周期状态</param>
public LifecycleServiceInfo(ILifecycleService service, LifecycleState startState)
{
_service = service;
StartState = startState;
StartTime = DateTime.Now;
}
}