跳到主要内容
版本:3.1

HTTP 系统参数(Aegis.SystemSettings.Http)

Aegis.SystemSettings.Http 让应用通过 HTTP 定期获取管理后台已发布的系统参数。业务通过配置类读取当前值,参数发布后自动更新;网络暂时不可用时,继续使用最近一次有效配置。

如何引入​

1. 安装并启用组件​

在 .NET 8 宿主项目中安装 NuGet 包:

dotnet add package Aegis.SystemSettings.Http

在 Component.deps.json 的组件清单中加入 SystemSettings.Http,保留已有的其他组件:

{
"Components": {
"Load": ["SystemSettings.Http"]
}
}

Http 组件已包含核心读取服务,无需另外启用 SystemSettings 组件或添加中间件。

2. 配置系统编码和接口地址​

在 appsettings.json 或宿主已加载的配置文件中添加:

{
"SystemSettings": {
"SystemCode": "OP-Nurse",
"Http": {
"Endpoint": "https://config.example.com/api/SystemSettings/GetPublishedSnapshot"
}
}
}

接入需要填写两项:

配置项填写内容
SystemSettings:SystemCode当前应用默认读取的系统编码,与管理后台登记的系统编码一致。示例中读取 OP-Nurse 的参数
SystemSettings:Http:Endpoint管理后台参数拉取接口的完整地址,包含协议、域名或 IP、端口(如有)和接口路径

将示例中的 OP-Nurse 替换为实际系统编码,将 Endpoint 替换为客户端能够访问的接口地址。若接口通过网关发布,填写网关对外提供的完整路径。不能只填写管理后台的站点地址或页面地址。

应用已有 SystemSettings:SystemCode 配置时,保留该配置,并在同级增加 Http 节即可。Mode 默认为 Http,轮询间隔等可选项均有默认值,无需为首次接入额外配置。

组件概览​

能力使用方式
参数读取通过 IOptionsMonitor<T> 读取配置类,或使用 ISystemSettings 动态读取
自动更新应用启动后持续拉取,采用管理后台已发布的值
院区配置支持全院区默认值和指定院区覆盖
跨系统读取在配置类的 Attribute 中声明其他系统
故障恢复自动保存服务器缓存,网络异常时继续读取已有有效配置

最小可运行路径​

1. 准备并发布参数​

在管理后台的 OP-Nurse 系统下创建分组 Registration,添加整数参数 TimeoutSeconds,将全院区默认值设为 20 并发布。

系统编码、分组编码和参数编码分别对应客户端的 SystemCode、GroupCode 和属性名。保存或导入后的草稿需要发布,客户端才会取得新值。

2. 声明配置类​

创建 RegistrationSettings.cs:

using Aegis.SystemSettings.Bindings;

/// <summary>
/// 挂号参数,支持全院区默认值和指定院区覆盖。
/// </summary>
[SystemSettingsBinding(GroupCode = "Registration")]
public sealed class RegistrationSettings
{
/// <summary>
/// 挂号操作允许等待的秒数。
/// </summary>
public int TimeoutSeconds { get; set; } = 60;
}

属性初始值 60 用于未提供该参数时的默认值,已发布的参数值会覆盖它。未在 Attribute 中指定 SystemCode 时,使用应用配置的主系统编码。配置类放在宿主或已纳入框架扫描的程序集内,由组件自动发现并绑定。

3. 在业务中读取当前值​

创建 RegistrationService.cs,通过构造函数注入 IOptionsMonitor<RegistrationSettings>:

using Microsoft.Extensions.Options;

/// <summary>
/// 提供当前生效的挂号规则。
/// </summary>
/// <param name="settings">挂号参数监视器。</param>
public sealed class RegistrationService(IOptionsMonitor<RegistrationSettings> settings)
{
/// <summary>
/// 获取全院区默认的挂号超时。
/// </summary>
/// <returns>当前生效的默认超时秒数。</returns>
public int GetTimeout()
{
return settings.CurrentValue.TimeoutSeconds;
}

/// <summary>
/// 获取指定院区当前生效的挂号超时。
/// </summary>
/// <param name="hospitalCode">院区编码。</param>
/// <returns>采用院区覆盖后的超时秒数;未配置覆盖时使用默认值。</returns>
public int GetTimeoutByHospitalCode(string hospitalCode)
{
return settings.Get(hospitalCode).TimeoutSeconds;
}
}

在宿主的服务注册阶段注册该业务服务:

using Microsoft.Extensions.DependencyInjection;

services.AddScoped<RegistrationService>();

启动应用并完成首次拉取后,GetTimeout() 返回已发布的 20。若 A 院区配置并发布了 30,则 GetTimeoutByHospitalCode("A") 返回 30;未配置覆盖的院区仍返回 20。

业务应在每次使用参数时调用 CurrentValue 或 Get(hospitalCode)。这样,管理后台发布新值后,后续业务操作即可读取更新后的配置;不要长期保存某次取得的配置对象,该对象不会原地更新。动态读取及更多绑定方式见系统参数读取与绑定。

跨系统读取​

同一个应用需要读取其他系统时,在配置类的 Attribute 中填写目标系统编码。例如主系统为 OP-Nurse,同时读取 Settle 系统的折扣参数:

using Aegis.SystemSettings.Bindings;

/// <summary>
/// 结算系统公布的折扣参数。
/// </summary>
[SystemSettingsBinding(SystemCode = "Settle", GroupCode = "Discount")]
public sealed class BillingDiscountSettings
{
/// <summary>
/// 自费结算使用的折扣比例。
/// </summary>
public decimal SelfPayDiscountRate { get; set; } = 1m;
}

按同样方式注入 IOptionsMonitor<BillingDiscountSettings> 即可读取。SDK 会自动拉取 OP-Nurse 和 Settle,无需再配置系统编码列表。

跨系统声明在启动时确定。配置类所在程序集需要参与扫描;使用 AddBinding<T>、AddSystemSettingsBinding<T> 显式绑定,或通过 SetDefaultSystemCode 指定默认系统时,相关系统也会加入拉取范围。程序集扫描方法见核心包说明。

ISystemSettings.ForSystem("Settle") 只切换读取的系统,不会新增拉取范围。仅使用动态接口读取其他系统时,也需要提前通过绑定声明该系统。

配置项说明​

HTTP 配置位于 SystemSettings:Http;除 Endpoint 外,其余配置均可省略。需要调整请求频率时再配置轮询上下限。

配置项默认值说明
Endpoint无,必填参数拉取接口的完整 HTTP/HTTPS 地址
ModeHttp使用 HTTP 持续拉取,可省略
PollingIntervalMinSeconds2正常轮询等待下限,单位秒,范围 2~86400
PollingIntervalMaxSeconds5正常轮询等待上限,单位秒,范围 2~86400
RequestTimeoutSeconds10单次请求及响应读取超时,单位秒,范围 1~60
MaxSnapshotBytes16777216响应和缓存文件大小上限,默认 16 MiB,范围 1 KiB~256 MiB

例如,将正常轮询调整为每次等待 15~30 秒:

{
"SystemSettings": {
"SystemCode": "OP-Nurse",
"Http": {
"Endpoint": "https://config.example.com/api/SystemSettings/GetPublishedSnapshot",
"PollingIntervalMinSeconds": 15,
"PollingIntervalMaxSeconds": 30
}
}
}

轮询上下限应一起配置。两者相同表示固定间隔;只填写其中一项时,另一项仍使用默认值。例如仅将下限设为 15、未填写上限,会因 15 > 5 恢复为默认区间。

数值越界时,SDK 会将有效正数调整到支持范围。轮询任一端点无法解析、不是有限正数、为对象/数组/null,或上下限倒置时,整个区间恢复为 2~5 秒。超时和大小上限的无效值恢复各自默认值。修正结果记录在启动日志中。

这些连接与调度选项在启动时读取,修改后需要重启应用。管理后台的业务参数发布后通过轮询自动更新,无需重启应用。

启动、更新与故障恢复​

拉取与生效时机​

每次应用启动,SDK 都随机等待 2~5 秒后发起首次请求。之后在每次请求结束时重新计算等待时间,再发起下一次请求;默认仍为 2~5 秒。自定义轮询上下限只影响后续请求,首拉区间保持不变。

发布到生效之间包含轮询等待和请求耗时;接口异常时还会延长等待。每个实例独立随机调度,可分散请求时间,但不能保证实例间完全没有并发。实例数量或配置体积较大时,可适当增加轮询间隔。

服务器缓存与启动恢复​

SDK 自动将有效服务端配置保存到 AppContext.BaseDirectory/SystemSettings/server.json。该目录位于应用程序集所在目录,无需自行创建配置文件。部署时为每个实例保留独立、可写的目录,更新应用版本时保留已有缓存。

场景应用行为
启动时已有完整有效的服务器缓存先恢复配置完成启动,再在后台执行首拉
启动时没有可用配置等待首拉取得有效配置;首拉失败则启动失败
运行期间接口不可用或响应无效继续使用最近一次有效配置,自动重试
接口恢复成功拉取后采用新值,恢复正常轮询间隔
缓存文件保存失败新值仍可能在当前进程生效;重启只能恢复实际保存的版本,应检查目录权限和磁盘空间

首次部署应保证接口可访问。服务器缓存需要与当前主系统、Endpoint 和拉取范围匹配;更换接口、系统编码或增加跨系统绑定后,不能假定旧缓存仍然可用。有效配置不会因长时间断网自动过期,允许继续使用旧值。

失败时自动延长间隔​

连续请求失败时,SDK 逐步延长等待,并保留随机波动,避免异常期间持续高频请求。默认设置下,第一次失败后等待约 6.3~7.7 秒,第二次约 12.6~15.4 秒;连续失败达到 7 次后,约每 270~330 秒重试。成功一次后恢复正常轮询。

自定义轮询区间的退避计算

设校验后轮询区间的中点为 B、连续失败次数为 n,等待时间按以下规则计算,单位为秒:

D = min(86400, max(300, 4 × B), B × 2^n)
随机区间 = [max(正常轮询下限, 0.9 × D), min(86400, 1.1 × D)]

失败等待不会短于正常轮询下限,也不会超过 24 小时。请求超时仅计算实际请求和响应读取时间,不包含等待时间。

扩展点​

为业务参数增加验证​

SDK 在应用新配置前,对已绑定的配置类执行类型转换和已注册的 Options 验证。可以在服务注册阶段为默认院区的挂号参数增加规则:

using Microsoft.Extensions.DependencyInjection;

services.AddOptions<RegistrationSettings>()
.Validate(value => value.TimeoutSeconds > 0, "挂号超时必须大于零。");

类型转换或适用的验证失败时,本次更新被拒绝,继续使用旧配置。上述规则仅针对默认 Options 名称;指定院区使用命名 Options,需要为相应院区注册规则,或使用覆盖全部名称的 IValidateOptions<T>。

仅通过 ISystemSettings.Get<T> 或 Require<T> 动态读取、未由配置类声明类型的参数,在业务读取时才转换类型。转换失败时,Get<T> 返回指定默认值,Require<T> 抛出异常。

配置认证与代理​

SDK 使用名为 Aegis.SystemSettings.Http 的 HttpClient。需要认证或代理时,在服务注册阶段通过 services.AddHttpClient(SystemSettingsHttpOptions.HttpClientName) 为该客户端追加处理器或连接配置。认证信息通过请求头传递,不放入 Endpoint 查询参数。

服务端接口要求​

使用现有管理后台接口时,配置 Endpoint 即可。对接自建接口或调整网关时,需要保持以下请求和响应格式。

SDK 使用 GET,并自动为每个拉取系统添加一个 systemCodes 查询参数;Endpoint 中不需要预先填写这些参数:

GET /api/SystemSettings/GetPublishedSnapshot?systemCodes=OP-Nurse&systemCodes=Settle

接口返回 HTTP 200,响应根节点直接包含完整配置:

{
"formatVersion": 1,
"systems": [
{
"systemCode": "OP-Nurse",
"scopes": [
{
"hospitalCode": "_default",
"items": [
{
"groupCode": "Registration",
"key": "TimeoutSeconds",
"configValue": "20"
}
]
}
]
},
{
"systemCode": "Settle",
"scopes": []
}
]
}
字段要求
formatVersion必填,当前为 1
systems必填,恰好包含本次请求的全部系统;编码大小写与请求一致,不能重复
scopes必填,[] 表示该系统没有配置
hospitalCode必填,默认院区使用 _default,也接受 null 或空字符串;指定院区填写实际编码
items必填,同一院区内分组与参数编码的组合不能重复
groupCode / key必填,非空的分组编码和参数编码
configValue必填,字符串或 JSON null;JSON 对象等复杂值也以字符串传递

接口只返回已发布数据,且每次返回完整结果。SDK 会移除响应中已不存在的参数;遗漏系统、重复字段或返回无效结构会导致整次更新被拒绝。configValue 为 null 时按空字符串读取,不表示删除参数。

响应不要再包装 Result、data 等节点。SDK 不使用 ETag,不能以 304 或 204 表示更新成功。每次有效响应都会尝试保存服务器缓存,只有参数值变化时才发出配置重载通知。

排查与使用边界​

现象检查方向
启动后没有立即发出请求首拉有 2~5 秒随机等待;核对组件是否启用、Endpoint 是否完整
后台已修改,客户端仍是旧值确认已发布;查看请求失败或参数验证日志
跨系统参数读取不到确认 Attribute 中的系统编码、配置类扫描范围以及接口返回的系统集合
更新后业务仍使用旧值在业务操作中重新读取 CurrentValue 或 Get(hospitalCode),避免保存旧配置对象
重启后读取到旧版本检查服务器缓存保存是否失败,以及部署是否覆盖了运行缓存

排查请求过程时,将日志分类 Aegis.SystemSettings.Http 设置为 Debug,可查看请求结果和下一次等待时间。日志中的 Warning 说明配置修正或请求失败,Error 用于文件保存失败、启动失败等需要处理的情况。

还需要区分以下使用边界:

  • SDK 随宿主启动安排首次拉取。有有效缓存时,启动完成不代表已取得服务端最新值。业务应在宿主启动后读取参数;在服务注册阶段提前读取并验证 Options,可能因尚未取得参数而失败。
  • 同名参数仍遵循宿主 IConfiguration 的优先级。若低优先级配置保留值 999、服务端值为 19,读取结果为 19;服务端删除该项后,999 会重新生效。需要完全由管理后台管理的业务参数,应避免在其他配置来源重复定义。
  • Http 包负责获取参数;管理后台负责草稿隔离和发布。导入值 20 尚未发布、当前已发布值为 19 时,客户端继续读取 19,发布后再通过轮询取得 20。

配套阅读​