XiHan Framework 处于预览阶段

查看获取方式
跳转到内容

配置模型与选项 ​

这一页讲应用与框架怎样读配置:框架沿用哪套配置模型、XiHan:<领域> 配置节怎样命名、应用自己的选项怎样绑定与校验、哪些值能在运行中更新。读完你能给应用加一组自己的选项,并知道框架各包从哪个配置节读取什么。前置知识是装配入口:你需要知道 AddXiHan 在什么时候执行、做了什么。

为什么需要 ​

一个应用总有一些值不能写死在代码里:端口、连接地址、超时、容量上限、功能开关。它们在开发机、测试环境和生产集群里各不相同,还要能在不改代码的前提下调整。.NET 自带 Microsoft.Extensions.Configuration,已经把文件、环境变量、命令行这些来源统一成一棵键值树;如果框架再造一套配置模型,应用就要学两套写法、维护两份来源。

XiHan.Framework 因此不另造配置模型:应用与框架都读同一个 IConfiguration。框架只约定两件事:它自己的配置节一律挂在 XiHan:<领域> 下;各包的选项按键显式读取而不经反射绑定,所以在 Native AOT 下照常工作。应用自己的选项照旧用 IOptions<T> / IOptionsMonitor<T>。

配置模型 ​

概念说明
IConfiguration.NET 的配置树;appsettings.json、appsettings.<环境>.json、环境变量、命令行都是它的来源
配置节树上的一个子树,层级以 : 分隔,例如 XiHan:Kernel
框架选项各包里带 ConfigurationSection 常量的 record(KernelOptions、ResilienceOptions、NacosOptions 等),由静态方法 FromConfiguration(IConfiguration) 逐键读取
应用选项应用自己的类或 record,用 IOptions<T> / IOptionsMonitor<T> 绑定
Configuration.Abstractions只定义两个 Provider 契约(远程配置客户端与密钥库)以及把它们接进 IConfigurationBuilder 的配置源,见 远程配置与密钥

框架没有「默认配置包」:不装任何配置相关的包,appsettings.json 与环境变量就是全部来源。

框架选项的读取时机 ​

特性在装配时读取一次选项并登记为单例。也就是说 AddXiHan 返回之后再改 XiHan:* 下的键,要重启才对框架生效。命名存储实例是例外,它的定义在首次取用时才从 XiHan:ObjectStorage:Stores:<name> 读取,见分片、预签名与命名实例。

AddXiHan 在调用时就求解能力图谱、解析资源绑定、装配全部特性,所以远程配置、密钥这类配置源必须在 AddXiHan 之前加入 builder.Configuration,否则框架的选项与绑定读不到它们。

配置节命名 ​

框架的配置节一律以 XiHan:<领域> 开头,领域词取自规范术语表(Kernel、Persistence、Caching、Localization、ObjectStorage、Configuration 等)。实现包的配置在领域节下再加一段实现名,例如 XiHan:Caching:Redis、XiHan:ObjectStorage:FileSystem;领域节本身放抽象层的选项,例如 XiHan:Localization:DefaultLocale。

同一个键有三种等价写法:

写法例子
JSON{ "XiHan": { "Kernel": { "StopTimeoutSeconds": 25 } } }
配置键XiHan:Kernel:StopTimeoutSeconds
环境变量XiHan__Kernel__StopTimeoutSeconds=25

几条约定:

  • 时长类的键带单位后缀(StopTimeoutSeconds、RefreshIntervalSeconds、MultipartExpiryHours),在代码里对应 TimeSpan 属性。
  • 值不合法(负数、无法解析的枚举或布尔值)时 FromConfiguration 抛出 ArgumentException,应用在启动期失败,不会带着错误配置运行。
  • 应用自己的配置节用自己的名字,不放进 XiHan 下。下面的示例用 Sample。
  • 特性清单的 Contributes.Config 声明它读取的配置的最近公共节:只读自己子节的写子节(Caching.Hybrid 写 XiHan:Caching:Hybrid),同时读领域节下公共选项的写领域节(Caching.Redis 还读 XiHan:Caching:Idempotency,写 XiHan:Caching),见特性与清单与规范 RFC-0002。

最小示例 ​

我们给应用加一个 Sample:Banner 选项,并用一个端点返回它的当前值。先写选项类:

csharp
// BannerOptions.cs
namespace Shop;

public sealed class BannerOptions
{
    public const string Section = "Sample";

    public string Banner { get; set; } = "Hello from XiHan";
}

再在 Program.cs 里绑定并使用:

csharp
// Program.cs
using Microsoft.Extensions.Options;
using Shop;
using XiHan.Framework.Hosting.AspNetCore;
using XiHan.Framework.Profiles.Standard;

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<BannerOptions>(builder.Configuration.GetSection(BannerOptions.Section));

builder.AddXiHan(xihan => xihan.UseStandardProfile());

var app = builder.Build();
app.UseXiHan();

// 读 CurrentValue 而不是在启动时缓存,配置源重载后返回新值
app.MapGet("/banner", (IOptionsMonitor<BannerOptions> banner) => banner.CurrentValue.Banner);

app.Run();

appsettings.json:

json
{
  "Sample": {
    "Banner": "Hello from appsettings.json"
  },
  "XiHan": {
    "Kernel": {
      "StopTimeoutSeconds": 25
    }
  }
}

运行后 curl localhost:5000/banner 返回 Hello from appsettings.json。设置环境变量 Sample__Banner="Hello from env" 再启动,返回的就是环境变量的值:环境变量覆盖文件。XiHan:Kernel:StopTimeoutSeconds 由内核的 KernelOptions.FromConfiguration 读取,决定每个特性停止的超时,应用代码不用碰它。

应用自己的选项 ​

IOptions 与 IOptionsMonitor ​

注入类型何时取值适用
IOptions<T>首次取用时绑定一次,之后不变启动后不再变化的值
IOptionsMonitor<T>CurrentValue 每次返回当前值,配置源重载后更新;OnChange 可注册回调需要热更新的值,见 配置目录与热更新
IOptionsSnapshot<T>每个服务作用域绑定一次一次调用内保持一致、跨调用可变

校验 ​

选项值不合法时最好在启动期暴露。.NET 的选项构建器支持声明校验,并能要求在启动时执行:

csharp
builder.Services.AddOptions<BannerOptions>()
    .Bind(builder.Configuration.GetSection(BannerOptions.Section))
    .Validate(static options => !string.IsNullOrWhiteSpace(options.Banner), "Sample:Banner must not be blank.")
    .ValidateOnStart();

这与框架选项的做法一致:配置错误属于编程与部署错误,应用启动失败,而不是带着坏值运行。这类消息面向开发与运维,用英文,不走本地化,区分方式见结果与错误处理。

Native AOT 下的绑定 ​

Configure<T>(IConfiguration) 默认按反射绑定。要以 Native AOT 发布,在项目文件里打开配置绑定源生成器,绑定代码在编译期生成,IOptionsMonitor<T> 的重载也照常工作:

xml
<PropertyGroup>
  <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

也可以像框架一样按键读取、不绑定。AOT 的完整要求见 JSON 序列化与 Native AOT。

配置节一览 ​

下表列出各包在代码里读取的配置节(ConfigurationSection 常量与 FromConfiguration 读取的键)。XiHan:Providers:<能力 ID> 指定某个能力由哪份清单提供,原理见能力图谱与 Provider;XiHan:Bindings 见 资源绑定。

配置节所属包用途
XiHan:ProvidersKernel<能力 ID> → 清单 ID,指定能力的提供者
XiHan:BindingsKernel资源绑定
XiHan:KernelKernelStartTimeoutSeconds、StopTimeoutSeconds、FailurePolicy、UseBuiltInMiddleware
XiHan:Kernel:ResilienceKernelResilience 阶段的默认策略,Operations:<操作 ID> 按操作覆盖,见弹性与限流
XiHan:Persistence:InMemoryPersistence.InMemoryMaxEntitiesPerType
XiHan:Persistence:EntityFrameworkCorePersistence.EntityFrameworkCoreEnsureCreated、HealthProbeIntervalSeconds
XiHan:Persistence:SqlSugarPersistence.SqlSugarInitTables、CommandTimeoutSeconds、Replicas:*(从库的权重与健康探测)、HealthProbeIntervalSeconds
XiHan:Persistence:SnowflakePersistence.Abstractions雪花标识:WorkerId、Epoch、LeaseTimeToLiveSeconds、ClockDriftToleranceMilliseconds
XiHan:MultiTenancyMultiTenancy.AbstractionsDefaultTenant、RequireTenant、TenantClaim、ValidateAgainstStore
XiHan:MultiTenancy:InMemoryMultiTenancy.InMemoryMaxTenants
XiHan:MultiTenancy:TenantsMultiTenancy.InMemory从配置装载的租户(DisplayName、IsEnabled、Properties)
XiHan:MultiTenancy:DatabaseMultiTenancy.DatabaseCacheSize、CacheTimeToLiveSeconds、MaxListedTenants
XiHan:SecuritySecurity.AbstractionsRequireAuthentication、RoleClaim
XiHan:Security:OutboundKernel出站地址策略:AllowLoopback、AllowPrivateNetwork、AllowedNetworks、AllowedHosts
XiHan:Security:InMemorySecurity.InMemory开发用主体与授予(Principals、Grants)
XiHan:Security:DatabaseSecurity.DatabaseCacheSize、CacheTimeToLiveSeconds
XiHan:Security:JwtSecurity.JwtIssuer、Audience、Secret、ClockSkewSeconds、NameClaim
XiHan:Security:OpenIdConnectSecurity.OpenIdConnectAuthority、Audience、元数据地址与刷新间隔
XiHan:Security:Ip2RegionSecurity.Ip2Regionxdb 数据文件路径 V4Database、V6Database
XiHan:Caching:InMemoryCaching.InMemoryMaxEntries、DefaultTimeToLiveSeconds
XiHan:Caching:RedisCaching.RedisConfiguration、KeyPrefix、DefaultTimeToLiveSeconds、PublishChanges、HealthProbeIntervalSeconds
XiHan:Caching:HybridCaching.HybridLocalCapacity、LocalTimeToLiveSeconds
XiHan:Caching:RemoteCaching.RemoteTimeoutMilliseconds
XiHan:Caching:IdempotencyCaching.Abstractions幂等记录的 TimeToLiveSeconds、InProgressTimeToLiveSeconds
XiHan:MessagingMessaging.AbstractionsMaxAttempts、DeadLetterSuffix、InstanceId
XiHan:Messaging:OutboxMessaging.Abstractions发件箱的 PollIntervalSeconds、BatchSize、MaxAttempts、RetryDelaySeconds、MaxRetryDelaySeconds、DispatchedRetentionSeconds
XiHan:Messaging:InMemoryMessaging.InMemoryMaxQueuedMessages
XiHan:Messaging:RabbitMQMessaging.RabbitMQConfiguration、QueuePrefix、PrefetchCount、HealthProbeIntervalSeconds
XiHan:Messaging:KafkaMessaging.KafkaBootstrapServers、GroupId、TopicPrefix、HealthProbeIntervalSeconds、RedeliveryDelaySeconds、Properties:<key>
XiHan:SchedulingScheduling.Abstractions调度引擎与领导选举:轮询、租约、并发、错过阈值、历史保留
XiHan:Scheduling:InMemoryScheduling.InMemoryMaxRuns
XiHan:Scheduling:DatabaseScheduling.DatabaseHealthProbeIntervalSeconds
XiHan:Scheduling:KubernetesScheduling.KubernetesLease 选举:Lease、Namespace、Holder、ApiServer
XiHan:WorkflowWorkflow.Abstractions引擎:锁超时、子流程深度、表达式缓存、保留天数
XiHan:Workflow:InMemoryWorkflow.InMemory各存储容量、轮询周期与批大小
XiHan:Workflow:DatabaseWorkflow.Database到期书签的轮询与批大小、租约时长、HolderId
XiHan:LocalizationLocalization.AbstractionsDefaultLocale、Locales
XiHan:Localization:DatabaseLocalization.DatabaseRefreshIntervalSeconds、MaxTranslations
XiHan:ObjectStorage:FileSystemObjectStorage.FileSystemRootPath、MaxTotalBytes、MaxBlobBytes、MultipartExpiryHours
XiHan:ObjectStorage:S3ObjectStorage.S3ServiceUrl、Region、AccessKey、SecretKey、ForcePathStyle 等
XiHan:ObjectStorage:StoresObjectStorage.Abstractions命名存储实例:<name>:Provider 加该 Provider 的设置
XiHan:ObjectStorage:StoreHealthProbeIntervalSecondsObjectStorage.Abstractions命名实例健康探测的间隔(单个键)
XiHan:ObjectStorage:SignedUrlsHosting.AspNetCore宿主代收发预签名地址的 BaseUrl、Secret
XiHan:Configuration:NacosConfiguration.NacosNacos 服务地址、分组、命名空间、登录与超时
XiHan:AuditingAuditing.AbstractionsSelection、IncludeSuccesses
XiHan:Auditing:InMemoryAuditing.InMemoryCapacity、LogEvents
XiHan:Auditing:DatabaseAuditing.DatabaseQueueCapacity、BatchSize、FlushIntervalSeconds、LogEvents
XiHan:Observability:HostObservability.HostSampleIntervalSeconds
XiHan:Observability:OpenTelemetryObservability.OpenTelemetry服务名、追踪、指标、日志、采样与 OTLP 导出
XiHan:AIAI.AbstractionsProvider 表、工具名、护栏、知识库与提示词
XiHan:Protocols:HttpProtocols.HttpRoutePrefix,Routes:<操作 ID> 覆盖推导的路由
XiHan:Protocols:GrpcProtocols.GrpcPackage,Methods:<操作 ID> 覆盖推导的方法名
XiHan:Protocols:McpProtocols.McpServerName、ServerVersion、Instructions,Tools:<操作 ID>
XiHan:Protocols:CloudEventsProtocols.CloudEventsInbound、Outbound、Topics:<事件类型>、Webhooks:<主题>
XiHan:Plugins:OutOfProcessPlugins.OutOfProcess插件目录、授予、握手与启停超时、健康与重启限制
XiHan:Plugins:WasmPlugins.Wasm插件目录、授予、挂载、内存与栈上限、调用超时

各键的类型与缺省值以对应包页为准。

常见问题 ​

改了 appsettings.json 里的 XiHan:* 键,为什么运行中的应用没反应 ​

框架选项在 AddXiHan 装配时读取一次并登记为单例,改 XiHan:* 下的键要重启。只有读 IConfiguration 或 IOptionsMonitor<T> 的应用代码会随文件变化更新。

应用的配置节能放在 XiHan 下吗 ​

不要。XiHan:<领域> 保留给框架与 Provider 包,领域词来自规范术语表;应用放进去会与将来新增的包冲突。用应用自己的名字做顶层节。

配置值写错了会怎样 ​

框架选项的 FromConfiguration 对负数、无法解析的枚举与布尔值抛出 ArgumentException,启动失败并在消息里给出键名。应用选项用 ValidateOnStart() 得到同样的效果。

怎样知道某个键的缺省值 ​

以对应包的参考页为准,各页的「配置」小节列出键、类型与缺省;上面的一览表只列键名与用途。

远程配置里的键能覆盖 appsettings 吗 ​

能,前提是它在 AddXiHan 之前加进 builder.Configuration:后加入的配置源覆盖先加入的。完整顺序见 配置目录与热更新的「配置源的优先级」。

相关包与下一步 ​

Released under The MIT License