16:选项数据热更新:让服务感知配置的变化-开发实战
.NET Core开发实战前文传送门:
- 第1课:课程介绍
- 第2课:内容综述
- 第3课:.NET Core的现状、未来以及环境搭建
- 第4课:Startup:掌握ASP.NET Core的启动过程
- 第5课:依赖注入:良好架构的起点(上)
- 第5课:依赖注入:良好架构的起点(中)
- 第5课:依赖注入:良好架构的起点(下)
- 第6课:作用域与对象释放行为(上)
- 第6课:作用域与对象释放行为(下)
- 第7课:用Autofac增强容器能力(上)
- 第7课:用Autofac增强容器能力(下)
- 第8课:配置框架:让服务无缝适应各种环境
- 第9课:命令行配置提供程序
- 第10课:环境变量配置提供程序
- 第11课:文件配置提供程序
- 第12课:配置变更监听
- 第13课:配置绑定:使用强类型对象承载配置数据
- 第14课:自定义配置数据源:低成本实现定制化配置方案
- 第15课:选项框架:服务组件集成配置的最佳实践
16 | 选项数据热更新:让服务感知配置的变化
选项框架还有两个关键类型:
1、IOptionsMonitor
2、IOptionsSnapshot
场景:
1、范围作用域类型使用 IOptinsSnapshot
2、单例服务使用 IOptionsMonitor
通过代码更新选项:
IPostConfigureOptions
延续上一节的代码,但是做一些特殊处理,之前注册 Order 服务用的是单例模式,这里改为 Scoped 模式
public void ConfigureServices(IServiceCollection services) { services.Configure<OrderServiceOptions>(Configuration.GetSection("OrderService")); //services.AddSingleton<IOrderService, OrderService>(); services.AddScoped<IOrderService, OrderService>(); services.AddControllers(); }
service 还是使用 IOptions
public class OrderService : IOrderService { IOptions<OrderServiceOptions> _options; public OrderService(IOptions<OrderServiceOptions> options) { _options = options; } public int ShowMaxOrderCount() { return _options.Value.MaxOrderCount; } }
启动程序,输出如下:
orderService.ShowMaxOrderCount:200
修改配置
{ "OrderService": { "MaxOrderCount": 2000 } }
启动程序,输出如下:
orderService.ShowMaxOrderCount:200
输出值还是200
那么如何能够读到新的配置呢?
只需要把 IOptions 换成 IOptionsSnapshot 即可
{ "OrderService": { "MaxOrderCount": 2000 } }
这是因为我们的服务注册的是 Scoped 模式,并且使用 Snapshot 来读取配置,每次请求都会重新计算并读取配置
那如果我们的服务是单例的时候怎么办呢?
把服务注册改为单例模式
services.AddSingleton<IOrderService, OrderService>();
这里需要使用另一个接口,把 Snapshot 改为 Monitor
IOptionsMonitor<OrderServiceOptions> _options; public OrderService(IOptionsMonitor<OrderServiceOptions> options) { ... }
Monitor 与 Snapshot 的定义略微有些不同,它获取值是需要用 CurrentValue 字段
public int ShowMaxOrderCount() { return _options.CurrentValue.MaxOrderCount; }
启动程序,修改配置文件,刷新浏览器,可以看到输出了修改后的数据,也就是说单例对象同时也能读取到最新的配置
如果说我想知道配置的值发生变化并且通知到我的 Options 怎么做呢?
首先看一下 Monitor 的定义
namespace Microsoft.Extensions.Options { public interface IOptionsMonitor<out TOptions> { TOptions CurrentValue { get; } TOptions Get(string name); IDisposable OnChange(Action<TOptions, string> listener); } }
它有一个 OnChange 方法,也就是说可以监听它的变更
public OrderService(IOptionsMonitor<OrderServiceOptions> options) { _options = options; _options.OnChange(option => { Console.WriteLine($"配置更新了,最新的值是:{_options.CurrentValue.MaxOrderCount}"); }); }
启动程序,修改配置,可以看到输出配置变化,也就是说可以在单例模式下监听到 Options 的变化
通常情况下,在设计服务的时候,会在 ConfigureServices 添加配置注入、服务注入,但是当配置多起来的时候,注入代码就会非常多
那么如何使代码结构更加良好?
实际上可以把服务注册的代码放在静态扩展方法里,使得 ConfigureServices 更加简洁
namespace Microsoft.Extensions.DependencyInjection { public static class OrderServiceExtensions { public static IServiceCollection AddOrderService(this IServiceCollection services,IConfiguration configuration) { services.Configure<OrderServiceOptions>(configuration); services.AddSingleton<IOrderService, OrderService>(); return services; } } }
这样在 Startup 的注册就变得更为简单了
services.AddOrderService(Configuration.GetSection("OrderService"));
我们在设计系统的时候会涉及大量的 service,所以我们可以把 service 的注册提炼在扩展方法里,不同的模块用不同的扩展方法隔开,使模块之间更加清晰,代码的结构也更加的清晰
那么实际上我们在设计服务的时候,还有一些特殊的述求,比如说把配置读取出来之后,还需要在内存里面进行一些特殊的处理,我们就可以使用动态配置的方式
动态配置的方式是在我们的 Configure 的代码之后,调用 PostConfigure 的方法,这里需要配置 OrderServiceOptions
{ public static class OrderServiceExtensions { public static IServiceCollection AddOrderService(this IServiceCollection services,IConfiguration configuration) { services.Configure<OrderServiceOptions>(configuration); services.PostConfigure<OrderServiceOptions>(options => { options.MaxOrderCount += 20; }); services.AddSingleton<IOrderService, OrderService>(); return services; } } }
启动程序,可以看到输出动态增加了20

原文出处:微信公众号【郑子铭 DotNet NB】
原文链接:https://mp.weixin.qq.com/s/8DXqW00SJYnR3R1hZIN0Jg
本文观点不代表Dotnet9立场,转载请联系原作者。