XiHan Framework 处于预览阶段

查看获取方式
跳转到内容

生命周期与健康 ​

本章说明特性与应用的九态生命周期、启动与停止的顺序和超时、失败策略,以及健康条目与探针端点。适用于需要在启动时建立连接、停止时释放资源,或要把应用接入 Kubernetes 探针的开发者。

完整 API 见 Kernel 包。

九个状态 ​

特性、插件与应用共用同一个状态枚举 LifecycleState(原语层):

状态含义
Declared清单已声明,尚未求解
Resolved已进入能力图谱
ConfiguredConfigure 已执行
Started正在启动(IHostedFeature.StartAsync 执行中)
Ready可服务
Degraded可服务但有缺陷:缺少可选依赖,或某个健康条目报告降级
Draining正在停止,不再接收新调用
Stopped已停止,终态
Failed失败

合法的迁移只有这些(LifecycleStateExtensions.CanTransitionTo):

text
Declared → Resolved → Configured → Started → Ready ⇄ Degraded
                                      │        │        │
                                      └────────┴────────┴──→ Draining → Stopped
除 Stopped 与 Failed 外的任何状态 → Failed;Failed → Resolved(重新装配)

Ready 与 Degraded 统称可服务(IsServing())。应用处于可服务状态时才接收调用,否则 App.InvokeAsync 拒绝,宿主返回 KERNEL_UNAVAILABLE_001(HTTP 503)。

启动与停止 ​

AddXiHan 在容器里注册一个宿主服务,随 .NET 通用宿主启动和停止应用:

  1. 启动按能力图谱的拓扑顺序逐个启动特性:进入 Started,对实现了 IHostedFeature 的特性调用 StartAsync,完成后进入 Ready;声明的可选依赖缺失时进入 Degraded。
  2. 每个 StartAsync 受启动超时约束,超时或抛出异常即特性失败(KERNEL_HOSTING_002)。
  3. 停止按逆拓扑顺序进行:依赖者先停,被依赖者后停,停止期间依赖仍可用。每个特性进入 Draining,调用 StopAsync,完成后进入 Stopped。
  4. 停止超时记为 KERNEL_HOSTING_003,抛出异常记为失败;两者都不中断其余特性的停止。

只在启动时建立连接、停止时释放的特性实现 IHostedFeature:

csharp
using Microsoft.Extensions.DependencyInjection;
using XiHan.Framework.Kernel;
using XiHan.Framework.Kernel.Hosting;
using XiHan.Framework.Primitives;
using XiHan.Framework.Primitives.Manifests;

namespace Shop.Reports;

public sealed class ReportsFeature : IHostedFeature
{
    private ReportWarmer? _warmer;

    public Manifest Manifest { get; } = new()
    {
        Id = "xihan.shop.reports",
        Version = "1.0.0",
        Primitives = 1,
        Stability = Stability.Preview,
        Kind = ManifestKind.Capability,
        Requires = [new CapabilityRequirement(KernelCapabilities.Hosting, KernelCapabilities.Version)],
        Entry = typeof(ReportsFeature).FullName,
    };

    public void Configure(FeatureContext context) => context.Services.AddSingleton<ReportWarmer>();

    // 根容器已构建;取消令牌同时受宿主取消与启动超时约束
    public async Task StartAsync(IServiceProvider services, CancellationToken cancellationToken)
    {
        _warmer = services.GetRequiredService<ReportWarmer>();
        await _warmer.WarmUpAsync(cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _warmer = null;
        return Task.CompletedTask;
    }
}

public sealed class ReportWarmer
{
    public Task WarmUpAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

不需要启动与停止逻辑的特性只实现 IFeature,它们在启动阶段直接从 Started 进入 Ready。

内核选项 ​

配置节 XiHan:Kernel(KernelOptions):

键类型缺省说明
StartTimeoutSeconds正数30每个特性 StartAsync 的超时
StopTimeoutSeconds正数30每个特性 StopAsync 的超时;部署到 Kubernetes 时与 terminationGracePeriodSeconds 对齐
FailurePolicyTerminateStartup / IsolateFeatureTerminateStartup特性启动失败时的处理
UseBuiltInMiddleware布尔true是否登记内核内置中间件(Observe、截止时间、Resilience、Validate)
json
{
  "XiHan": {
    "Kernel": {
      "StartTimeoutSeconds": 30,
      "StopTimeoutSeconds": 30,
      "FailurePolicy": "TerminateStartup"
    }
  }
}

非法值(非正数、拼错的枚举名)在构建应用时抛出 ArgumentException。代码里可用 AppBuilder.UseOptions(new KernelOptions { ... }) 覆盖配置。

失败策略 ​

策略特性启动失败时应用状态
TerminateStartup抛出 FrameworkException(KERNEL_HOSTING_002),宿主启动失败、进程退出Failed
IsolateFeature该特性记为 Failed,其余特性继续启动其余都可服务时为 Degraded,探针可区分

应用状态由各特性状态聚合:有失败且策略为终止 → Failed;全部停止 → Stopped;任一特性在排空或已停止 → Draining;全部可服务 → Ready(有 Degraded 则 Degraded)。应用就绪后,任一健康条目报告 Degraded 或 Failed 只把应用从 Ready 压成 Degraded,不让应用退出。

失败策略只管特性启动时的失败(连不上外部服务这类运行条件)。装配错误不归它管,不论策略都终止启动:装配完成时的核对(如契约引用了未登记的权限码,KERNEL_VALIDATION_005)在 AppBuilder.Build 里抛出,要等容器建好才能做的核对放在启动核对里。

启动核对 ​

特性在 Configure 里用 FeatureContext.AddStartupCheck(check) 登记要等容器建好才能做的装配核对,最典型的是授权能力核对操作契约引用的策略名(策略是容器里的 IPolicy,授权):

csharp
public void Configure(FeatureContext context)
{
    // 核对不通过时抛出带错误码的 FrameworkException,一次列出全部问题
    context.AddStartupCheck(static services => services.GetRequiredService<ReportCatalog>().EnsureEveryReportHasATemplate());
}
  • 时机:宿主启动应用时,容器建好之后、第一个特性启动之前,按登记顺序各执行一次;重复启动时通过过的核对不再执行。
  • 失败:任一抛出即终止启动,异常原样抛出(不包成 KERNEL_HOSTING_002),后面的核对与全部特性都不再执行,记一条 Error 日志。
  • 不归失败策略管(ADR-0085):IsolateFeature 下同样终止。核对的是引用了不存在的东西这类装配错误,隔离哪个特性都修不好,只会让应用带着错误降级运行。
  • 连接外部服务、预热缓存这类可能因运行条件失败的工作不要放进启动核对,放在 IHostedFeature.StartAsync 里,由失败策略决定是终止还是隔离。

健康条目 ​

特性之外的健康信息由 IHealthContributor 提供:外部连接是否可用、内存存储是否接近容量、插件进程是否存活。内核从容器里取出全部 IHealthContributor,每次读健康快照时调用 Check(),返回的 ContributorStatus 进入快照的 Contributions:

ContributorStatus 成员说明
Name条目名,约定 <领域>.<机制>.<对象>,如 caching.redis.connection
StateReady、Degraded 或 Failed
Error不健康时的错误
Description检查的是什么
CheckedAt / Duration检查时刻与耗时
Data名字到标量(字符串、布尔、数值或空)的字典,如已用量与上限

Check() 必须立即返回。连接类检查用内核的 PeriodicHealthContributor:它在后台按间隔执行探针、缓存最近一次结果,Check() 只读缓存;探针失败或抛异常记为 KERNEL_UNAVAILABLE_005 并报 Degraded,恢复后报 Ready。框架的 Provider 都按这个方式实现,探测间隔由各自配置节的 HealthProbeIntervalSeconds 决定(缺省 15 秒)。

同步计算的条目直接实现接口:

csharp
using XiHan.Framework.Kernel.Hosting;
using XiHan.Framework.Primitives;

namespace Shop.Reports;

public sealed class ExportQueue
{
    public const int Limit = 1000;

    public int Count { get; set; }
}

public sealed class ExportQueueHealth(ExportQueue queue) : IHealthContributor
{
    public IReadOnlyList<ContributorStatus> Check() =>
    [
        new ContributorStatus("shop.exports.queue", queue.Count < ExportQueue.Limit ? LifecycleState.Ready : LifecycleState.Degraded)
        {
            Description = "Pending report exports",
            Data = new Dictionary<string, object?> { ["count"] = queue.Count, ["limit"] = ExportQueue.Limit },
        },
    ];
}

在特性的 Configure 里登记:context.Services.AddSingleton<IHealthContributor, ExportQueueHealth>(),并把条目名写进清单的 Contributes.Health。框架包的架构测试强制两边一致;应用不受此测试约束,但建议同样声明,graph 命令会把它列出来。

探针端点 ​

Hosting.AspNetCore 的 app.MapXiHanHealth() 映射三个端点,它们不经过内核管道,应用未就绪时也能应答:

端点通过条件用途
/health/startup每个特性的状态都不早于 Started(启动流程已走到每个特性,含失败的)startupProbe
/health/ready应用处于 Ready 或 DegradedreadinessProbe
/health/live应用不是 FailedlivenessProbe

通过返回 200,否则返回 503。响应体列出应用状态、每个特性与每个健康条目:

json
{
  "status": "pass",
  "state": "Ready",
  "features": [
    { "id": "xihan.localization.inmemory", "state": "Ready" },
    { "id": "xihan.protocols.http", "state": "Ready" }
  ],
  "contributions": []
}

健康条目带说明、检查时刻、耗时与数据时,以 description、checkedAt、durationMs、data 字段输出,没有的省略。前缀可改:app.MapXiHanHealth("/internal/health")。

不经 HTTP 时,从容器取 App 调用 App.Health() 得到同样的 HealthSnapshot;命令行宿主没有探针端点,由进程退出码表达结果。

相关 ​

  • 能力包与清单
  • 部署与打包:探针、停机宽限与 terminationGracePeriodSeconds
  • 插件:插件进程的状态与重启
  • 规范:RFC-0002《清单》中的健康项、RFC-0001《原语》中的生命周期

Released under The MIT License