ASP.NET Core如何定时从DB/缓存更新配置并兼容本地配置?
现有配置与初始化代码
appsettings.json 配置
// settings for allowing access from other services "AuthSetting": { "CookieAuthSetting": { "KeyForAccessingFromServiceZ": { "ServiceName": "ServiceZ", "DeveloperName": "dev1", }, "IPAuthSetting": { "IPWhiteList": ["aaa.aaa.aaa.aaa", "bbb.bbb.bbb.bbb"], "IPBlackList": ["ccc.ccc.ccc.ccc"] } } }, // key for access to other service "Neighbors": { "ServiceY": "KeyforAccessToY" }
Startup.cs 初始化代码
services.Configure<AuthSetting>(Configuration.GetSection(nameof(AuthSetting))); services.Configure<Neighbors>(Configuration.GetSection(nameof(Neighbors)));
实现方案
1. 定时刷新认证配置
使用BackgroundService实现定时任务,定期从远端存储拉取最新配置并更新应用内的配置实例。
核心代码
后台刷新服务类
public class AuthConfigRefreshService : BackgroundService { private readonly IOptionsMonitor<AuthSetting> _authSettingMonitor; private readonly IOptionsMonitor<Neighbors> _neighborsMonitor; private readonly IAuthConfigRepository _authConfigRepo; private readonly PeriodicTimer _timer; public AuthConfigRefreshService( IOptionsMonitor<AuthSetting> authSettingMonitor, IOptionsMonitor<Neighbors> neighborsMonitor, IAuthConfigRepository authConfigRepo) { _authSettingMonitor = authSettingMonitor; _neighborsMonitor = neighborsMonitor; _authConfigRepo = authConfigRepo; _timer = new PeriodicTimer(TimeSpan.FromMinutes(5)); // 可通过配置文件调整刷新间隔 } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested && await _timer.WaitForNextTickAsync(stoppingToken)) { try { // 从数据库/缓存/RPC拉取最新配置 var latestAuth = await _authConfigRepo.GetLatestAuthSettingAsync(); var latestNeighbors = await _authConfigRepo.GetLatestNeighborsAsync(); // 触发配置更新,依赖IOptionsMonitor的组件会自动获取最新值 _authSettingMonitor.OnChange(_ => latestAuth); _neighborsMonitor.OnChange(_ => latestNeighbors); } catch (Exception ex) { // 记录刷新失败日志,不中断服务 // _logger.LogError(ex, "Auth config refresh failed"); } } } public override void Dispose() { _timer.Dispose(); base.Dispose(); } }
注册服务
在Program.cs/Startup.cs中注册后台服务和仓储:
services.AddHostedService<AuthConfigRefreshService>(); services.AddScoped<IAuthConfigRepository, AuthConfigRepository>();
仓储接口示例
public interface IAuthConfigRepository { Task<AuthSetting> GetLatestAuthSettingAsync(); Task<Neighbors> GetLatestNeighborsAsync(); } // 实现类根据实际存储介质编写(比如Redis、EF Core、内部RPC调用) public class AuthConfigRepository : IAuthConfigRepository { // 构造函数注入数据库/缓存客户端 public AuthConfigRepository(IConfiguration config) { // 初始化客户端 } public async Task<AuthSetting> GetLatestAuthSettingAsync() { // 从远端拉取逻辑,比如从Redis获取反序列化,或者数据库查询 return await Task.FromResult(new AuthSetting()); } public async Task<Neighbors> GetLatestNeighborsAsync() { // 同理实现远端拉取逻辑 return await Task.FromResult(new Neighbors()); } }
2. 优先加载本地配置,兼容远端故障
启动时优先绑定本地appsettings.json的配置,再尝试拉取远端配置;若远端拉取失败,直接保留本地配置,不影响服务启动。
核心代码(Program.cs 示例)
var builder = WebApplication.CreateBuilder(args); // 1. 加载本地配置 var localAuth = new AuthSetting(); builder.Configuration.GetSection(nameof(AuthSetting)).Bind(localAuth); var localNeighbors = new Neighbors(); builder.Configuration.GetSection(nameof(Neighbors)).Bind(localNeighbors); // 2. 尝试拉取远端配置,失败则跳过 try { var authRepo = new AuthConfigRepository(builder.Configuration); var remoteAuth = await authRepo.GetLatestAuthSettingAsync(); var remoteNeighbors = await authRepo.GetLatestNeighborsAsync(); // 合并远端与本地配置(冲突处理逻辑见下文) MergeAuthConfigs(localAuth, remoteAuth); MergeNeighborConfigs(localNeighbors, remoteNeighbors); } catch (Exception ex) { // 记录日志,继续使用本地配置 // builder.Logging.LogError(ex, "Failed to load remote auth config, falling back to local"); } // 3. 注册合并后的配置 builder.Services.Configure<AuthSetting>(_ => localAuth); builder.Services.Configure<Neighbors>(_ => localNeighbors); // 其他服务注册... var app = builder.Build(); // 管道配置... app.Run();
3. 配置合并:本地优先加载,冲突时远端覆盖
实现自定义合并逻辑,确保本地配置先加载,远端配置覆盖同名条目、新增条目追加。
合并逻辑示例
// 合并AuthSetting private void MergeAuthConfigs(AuthSetting local, AuthSetting remote) { if (remote == null) return; // CookieAuthSetting:远端条目覆盖本地同名,新增条目添加 if (remote.CookieAuthSetting != null) { foreach (var entry in remote.CookieAuthSetting) { local.CookieAuthSetting[entry.Key] = entry.Value; } } // IPAuthSetting:远端配置直接覆盖本地(按需求,冲突以远端为准) if (remote.IPAuthSetting != null) { local.IPAuthSetting.IPWhiteList = remote.IPAuthSetting.IPWhiteList ?? local.IPAuthSetting.IPWhiteList; local.IPAuthSetting.IPBlackList = remote.IPAuthSetting.IPBlackList ?? local.IPAuthSetting.IPBlackList; } } // 合并Neighbors private void MergeNeighborConfigs(Neighbors local, Neighbors remote) { if (remote == null) return; foreach (var serviceEntry in remote) { local[serviceEntry.Key] = serviceEntry.Value; } }
内容的提问来源于stack exchange,提问作者Panic
相关产品推荐
相关产品推荐

