zoukankan      html  css  js  c++  java
  • .net core中的Options重新加载机制

      Options是.net core提出的一种辅助配置机制,即选项。

      目前,我们可以使用的Options有五种(源码):

      IOptionsFactory<>:Options的创建工厂(Singleton),所有的Options均使用它仅限创建,可以使用名称创建指定名称的Options

      IOptions<>:常用的一种Options模式,不可指定名称,且会缓存Options,全局将使用这个缓存的Options,没有重新加载机制,不可修改。(ps:可使用反射进行修改)

      IOptionsSnapshot<>:和IOptionsFactory<>差不多,内部使用IOptionsFactory<>来创建,但是IOptionsSnapshot<>内部有缓存,且它的作用于是Scoped,做web开发的时候,如果不想自己实现重新加载,不妨使用IOptionsSnapshot<>。

      IOptionsMonitor<>和IOptionsMonitorCache<>:IOptionsMonitorCache<>是IOptionsMonitor<>的缓存,读取IOptionsMonitor<>时,会先从IOptionsMonitorCache<>缓存中读取,没有则使用IOptionsFactory<>创建并加入到IOptionsMonitorCache<>缓存中去,因此当配置发生改变时,我们需要重新加载才能得到新的Options,所谓重新加载,就是Options中的数据重新执行Configure以及PostConfigure中的方法。

      在继续下面的内容之前,我们应该明确,只有IOptionsMonitor<>会涉及到重新加载,那么何时需要重新加载?也就是何时触发这个重新加载?不同的系统做法会不一样,有的使用一条消息总线,有的使用消息的发布订阅,有的使用接口的手动触发,本文介绍采用定时器来模拟说明:

      一、使用IOptionsMonitorCache<>

      上面介绍到,IOptionsMonitorCache<>是IOptionsMonitor<>缓存,自然可以通过IOptionsMonitorCache<>来清除缓存来实现Options的重新创建了,如:

      假如数据源是一个类的数据,然后使用定时器定时刷新,刷新后需要清除IOptionsMonitorCache<>缓存:  

        //表示Options的数据来源
        public class OptionsSource
        {
            public static DateTime Time { get; private set; }
    
            public OptionsSource(IServiceProvider serviceProvider)
            {
                Timer timer = new Timer();
                timer.Interval = 10000;//10秒钟刷新一次Time
                timer.Elapsed += new ElapsedEventHandler((o, e) =>
                {
                    Time = DateTime.Now;
    
                    //清除缓存
                    var options = serviceProvider.GetService(typeof(IOptionsMonitorCache<DemoOptions>)) as IOptionsMonitorCache<DemoOptions>;
                    options.Clear();//或者使用TryRemove删除某个名称的Options
                });
                timer.Start();//启动
            }
        }

      接着在Startup中注册Options:  

        public void ConfigureServices(IServiceCollection services)
        {
            //注册Options
            services.Configure<DemoOptions>(options =>
            {
                options.Time = OptionsSource.Time;
            });
            services.AddSingleton<OptionsSource>();//数据源
    
            //其他服务注入代码
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.ApplicationServices.GetRequiredService<OptionsSource>();//获取一次,启动定时器
    
            //其他代码
        }

      接下来可以使用IOptionsMonitor<>获取Options,会发现每隔10秒,数据会刷新一次,比如有一个接口读取Options,每10秒调用试试:  

        [HttpGet]
        public object GetOptions()
        {
            var options = HttpContext.RequestServices.GetService(typeof(IOptionsMonitor<DemoOptions>)) as IOptionsMonitor<DemoOptions>;
            return options.CurrentValue;
        }

      二、使用IOptionsChangeTokenSource<>

      IOptionsChangeTokenSource<>是IOptionsMonitor<>用于触发重新加载Options的接口,它有一个Name属性,表示重新加载某个指定名称的Options,还有一个GetChangeToken方法,用于获取一个IChangeToken,由这个IChangeToken来触发何时应该重新加载Options,比如:

      我们先实现IOptionsChangeTokenSource<>接口:

        public class MyOptionsChangeTokenSource<TOptions> : IOptionsChangeTokenSource<TOptions> where TOptions : class
        {
            ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
    
            public MyOptionsChangeTokenSource(string name)
            {
                Name = name ?? Options.DefaultName;
            }
    
            public string Name { get; }
    
            public IChangeToken GetChangeToken()
            {
                return _reloadToken;
            }
            public void OnReload()
            {
                var previousToken = System.Threading.Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
                previousToken.OnReload();
            }
        }

      其中OnReload()方法即触发重新加载,何时触发呢?假如配置的数据源来自某个类的数据:  

        //表示Options的数据来源
        public class OptionsSource
        {
            public static DateTime Time { get; private set; }
    
            public OptionsSource(IOptionsChangeTokenSource<DemoOptions> optionsChangeTokenSource)
            {
                Timer timer = new Timer();
                timer.Interval = 10000;//10秒钟刷新一次Time
                timer.Elapsed += new ElapsedEventHandler((o, e) =>
                {
                    Time = DateTime.Now;
    
                    if (optionsChangeTokenSource is MyOptionsChangeTokenSource<DemoOptions> source)
                    {
                        source.OnReload();//触发MyOptionsChangeTokenSource<>从而重新加载Options
                    }
                });
                timer.Start();//启动
            }
        }

      接着在Startup中注册Options:  

        public void ConfigureServices(IServiceCollection services)
        {
            //注册Options
            services.Configure<DemoOptions>(options =>
            {
                options.Time = OptionsSource.Time;
            });
            services.AddSingleton<OptionsSource>();//数据源
            services.AddSingleton<IOptionsChangeTokenSource<DemoOptions>>(new MyOptionsChangeTokenSource<DemoOptions>(""));
    
            //其他代码
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.ApplicationServices.GetRequiredService<OptionsSource>();//获取一次,启动定时器
    
            //其他代码
        }

      接下来可以使用IOptionsMonitor<>获取Options也会有重新加载的效果了。

      上面的例子中,数据源来自某个类,但是开发过程中,我们的Options中的数据往往来自IConfiguration配置中,这时就更简单了,框架已经给我们准备好了一个ConfigurationChangeTokenSource<>类,当我们使用IConfiguration来配置Options时会自动注入这个类,我们当然也可以手动注入,意思就是说,当对应的IConfiguration中的数据发生改变时,它会触发清除对应的IOptionsMonitorCache<>缓存从而导致重新加载,比如:

      appsettings.json中有下面的json片段:  

      {
        "Demo": {
          "Value": 1
        }
      }

      程序启动时会自动加载appsettings.json文件中的配置到IConfiguration中,所以我们可以直接从IConfiguration配置Options:  

        public void ConfigureServices(IServiceCollection services)
        {
            //方式一:
            //这个会添加ConfigurationChangeTokenSource<>类,程序启动后,修改appsettings.json会触发Options的重新加载
            services.Configure<DemoOptions>(Configuration.GetSection("Demo"));
    
            //方式二:
            //这种方式不会添加ConfigurationChangeTokenSource<>类,但是可以手动添加
            services.Configure<DemoOptions>(options =>
            {
                Configuration.GetSection("Demo").Bind(options);
            });
            services.AddSingleton<IOptionsChangeTokenSource<DemoOptions>>(new ConfigurationChangeTokenSource<DemoOptions>(Options.DefaultName, Configuration.GetSection("Demo")));
    
            //其他代码
        }

      如上所述,添加Options的Configure方法有很多,但是只用 OptionsConfigurationServiceCollectionExtensions 类中的Configure方法才会自动添加ConfigurationChangeTokenSource<>类,即Configure方法参数中有IConfiguration参数。

      结语

      Options的重新加载,如果看看源码就会很清晰,但是更多的,希望在读取Options的时候多注意一下吧:

      1、如果配置永远不会发生改变,或者改变或需要重启才能生效(这种配置往往是在启动阶段用到),那么尽量使用IOptions<>

      2、如果要实现配置修改后会自动加载Options,推荐使用OptionsMonitor<>,但是注意,要自行实现何时需要重新加载,其次可以使用IOptionsSnapshot<>,因为它是一个Scoped作用域,最次是IOptionsFactory<>,每次都是重新创建可能会导致一些性能的损失

    一个专注于.NetCore的技术小白
  • 相关阅读:
    Emoji表情编解码库XXL-EMOJI
    代码生成平台Xxl-Code-Generator
    分布式单点登录框架XXL-SSO
    分布式爬虫框架XXL-CRAWLER
    Java对象和Excel转换工具XXL-EXCEL
    API管理平台XXL-API
    分布式缓存管理平台XXL-CACHE
    不知不觉已经写了多年代码,贴一下12年写的代码,表喷哈
    memcached安装、使用
    MySQL和Redis数据一致性问题
  • 原文地址:https://www.cnblogs.com/shanfeng1000/p/15095236.html
Copyright © 2011-2022 走看看