栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Ocelot简易教程(七)之配置文件数据库存储插件源码解析

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Ocelot简易教程(七)之配置文件数据库存储插件源码解析

上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储到数据库中。并没有对实现原理进行相应的阐述。今天抽空把实现的原理给大家说道说道。明白原理后,大家就可以自行改写进行扩展来满足自身需要了!
再次感觉张队的审稿,并给出的修改意见!

源码解析过程

大家可以自行分析Ocelot的源码,我通过分析ocelot的源码得出,如果要实现重写配置文件的方式,只需要写一个类来实现IFileConfigurationRepository这个接口即可。

代码如下:

 /// 
    /// yilezhu
    /// 2018.10.22
    /// 实现从SQLSERVER数据库中提取配置信息
    /// 
    public class SqlServerFileConfigurationRepository : 
    IFileConfigurationRepository
    {        
    private readonly IOcelotCache _cache;        
    private readonly IOcelotLogger _logger;        
    private readonly ConfigAuthLimitCacheOptions _option;        
    public SqlServerFileConfigurationRepository(ConfigAuthLimitCacheOptions option, IOcelotCache cache, IOcelotLoggerFactory loggerFactory)        {
            _option = option;
            _cache = cache;
            _logger = loggerFactory.CreateLogger();
        }        
        public Task Set(FileConfiguration fileConfiguration)        
        {
            _cache.AddAndDelete(_option.CachePrefix + "FileConfiguration", fileConfiguration, TimeSpan.FromSeconds(1800), "");            
            return Task.FromResult((Response)new OkResponse());
        }        /// 
        /// 提取配置信息
        /// 
        /// 
        public async Task> Get()
        {            
        var config = _cache.Get(_option.CachePrefix + "FileConfiguration", "");            if (config != null)
            {                
            return new OkResponse(config);
            }            
            #region 提取配置信息
            var file = new FileConfiguration();            
            string glbsql = "select top 1 * from OcelotGlobalConfiguration where IsDefault=1";            //提取全局配置信息
            using (var connection = new SqlConnection(_option.DbConnectionStrings))
            {                
            var result = await connection.QueryFirstOrDefaultAsync(glbsql);                
            if (result != null)
                {                    
                var glb = new FileGlobalConfiguration();
                    glb.baseUrl = result.baseUrl;
                    glb.DownstreamScheme = result.DownstreamScheme;
                    glb.RequestIdKey = result.RequestIdKey;                    if (!String.IsNullOrEmpty(result.HttpHandlerOptions))
                    {
                        glb.HttpHandlerOptions = result.HttpHandlerOptions.ToObject();
                    }                    
                    if (!String.IsNullOrEmpty(result.LoadBalancerOptions))
                    {
                        glb.LoadBalancerOptions = result.LoadBalancerOptions.ToObject();
                    }                    
                    if (!String.IsNullOrEmpty(result.QoSOptions))
                    {
                        glb.QoSOptions = result.QoSOptions.ToObject();
                    }                    
                    if (!String.IsNullOrEmpty(result.ServiceDiscoveryProvider))
                    {
                        glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider.ToObject();
                    }
                    file.GlobalConfiguration = glb;                    //提取路由信息

                    string routesql = "select * from OcelotReRoutes where OcelotGlobalConfigurationId=@OcelotGlobalConfigurationId and IsStatus=1";                    var routeresult = (await connection.QueryAsync(routesql, new { OcelotGlobalConfigurationId=result.Id })).AsList();                    if (routeresult != null && routeresult.Count > 0)
                    {                        
                    var reroutelist = new List();                        foreach (var model in routeresult)
                        {                            
                        var m = new FileReRoute();                            if (!String.IsNullOrEmpty(model.AuthenticationOptions))
                            {
                                m.AuthenticationOptions = model.AuthenticationOptions.ToObject();
                            }                            
                            if (!String.IsNullOrEmpty(model.CacheOptions))
                            {
                                m.FileCacheOptions = model.CacheOptions.ToObject();
                            }                            
                            if (!String.IsNullOrEmpty(model.DelegatingHandlers))
                            {
                                m.DelegatingHandlers = model.DelegatingHandlers.ToObject>();
                            }                            
                            if (!String.IsNullOrEmpty(model.LoadBalancerOptions))
                            {
                                m.LoadBalancerOptions = model.LoadBalancerOptions.ToObject();
                            }                            
                            if (!String.IsNullOrEmpty(model.QoSOptions))
                            {
                                m.QoSOptions = model.QoSOptions.ToObject();
                            }                            
                            if (!String.IsNullOrEmpty(model.DownstreamHostAndPorts))
                            {
                                m.DownstreamHostAndPorts = model.DownstreamHostAndPorts.ToObject>();
                            }                            //开始赋值
                            m.DownstreamPathTemplate = model.DownstreamPathTemplate;
                            m.DownstreamScheme = model.DownstreamScheme;
                            m.Key = model.Key;
                            m.Priority = model.Priority ?? 0;
                            m.RequestIdKey = model.RequestIdKey;
                            m.ServiceName = model.ServiceName;
                            m.Timeout = model.Timeout ?? 0;
                            m.UpstreamHost = model.UpstreamHost;                            if (!String.IsNullOrEmpty(model.UpstreamHttpMethod))
                            {
                                m.UpstreamHttpMethod = model.UpstreamHttpMethod.ToObject>();
                            }
                            m.UpstreamPathTemplate = model.UpstreamPathTemplate;
                            reroutelist.Add(m);
                        }
                        file.ReRoutes = reroutelist;
                    }
                }                else
                {                    throw new Exception("未监测到配置信息");
                }
            }            #endregion
            if (file.ReRoutes == null || file.ReRoutes.Count == 0)
            {                return new OkResponse(null);
            }            return new OkResponse(file);
        }
    }

当然,既然我们已经重新实现了这个接口,那么就得进行相应的DI了。这里我们扩展下IOcelotBuilder方法,代码如下,主要就是进行相应的服务的DI:

/// 
    /// yilezhu
    /// 2018.10.22
    /// 基于Ocelot扩展的依赖注入
    /// 
    public static class ServiceCollectionExtensions
    {        /// 
        /// 添加默认的注入方式,所有需要传入的参数都是用默认值
        /// 
        /// 
        /// 
        public static IOcelotBuilder AddAuthLimitCache(this IOcelotBuilder builder, Action option)        {
            builder.Services.Configure(option);
            builder.Services.AddSingleton(
                resolver => resolver.GetRequiredService>().Value);            #region 注入其他配置信息
            //重写提取Ocelot配置信息,
            builder.Services.AddSingleton(DatabaseConfigurationProvider.Get);            //builder.Services.AddHostedService();
            builder.Services.AddSingleton();            //注入自定义限流配置
            //注入认证信息
            #endregion
            return builder;
        }
    }

接下来就是重写,OcelotBuild里面配置文件的获取方式了。这里我选择的是对IApplicationBuilder进行扩展,因为这样方便做一些其他的事情,比如,重写限流,集成自定义的验证等等。具体代码如下:

   /// 
    /// yilezhu
    /// 2018.10.22
    /// 扩展IApplicationBuilder,新增use方法
    /// 
    public static class OcelotMiddlewareExtensions
    {        /// 
        /// 扩展UseOcelot
        /// 
        /// 
        /// 
        public static async Task UseAhphOcelot(this IApplicationBuilder builder)        {            await builder.UseAhphOcelot(new OcelotPipelineConfiguration());            return builder;
        }        /// 
        /// 重写Ocelot,带参数
        /// 
        /// 
        /// 
        /// 
        public static async Task UseAhphOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)        {            var configuration = await CreateConfiguration(builder);

            ConfigureDiagnosticListener(builder);            
            return CreateOcelotPipeline(builder, pipelineConfiguration);
        }        private static async Task CreateConfiguration(IApplicationBuilder builder)        
        {            // make configuration from file system?
            // earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this
            //var fileConfig = builder.ApplicationServices.GetService>();
            var fileConfig = await builder.ApplicationServices.GetService().Get();            // now create the config
            var internalConfigCreator = builder.ApplicationServices.GetService();            
            var internalConfig = await internalConfigCreator.Create(fileConfig.Data);            //Configuration error, throw error message
            if (internalConfig.IsError)
            {
                ThrowToStopOcelotStarting(internalConfig);
            }            // now save it in memory
            var internalConfigRepo = builder.ApplicationServices.GetService();
            internalConfigRepo.AddOrReplace(internalConfig.Data);            //fileConfig.onChange(async (config) =>
            //{
            //    var newInternalConfig = await internalConfigCreator.Create(config);
            //    internalConfigRepo.AddOrReplace(newInternalConfig.Data);
            //});

            var adminPath = builder.ApplicationServices.GetService();            
            var configurations = builder.ApplicationServices.GetServices();            // Todo - this has just been added for consul so far...will there be an ordering problem in the future? Should refactor all config into this pattern?
            foreach (var configuration in configurations)
            {                await configuration(builder);
            }            if (AdministrationApiInUse(adminPath))
            {                //We have to make sure the file config is set for the ocelot.env.json and ocelot.json so that if we pull it from the 
                //admin api it works...boy this is getting a spit spags boll.
                var fileConfigSetter = builder.ApplicationServices.GetService();                //  await SetFileConfig(fileConfigSetter, fileConfig.Data);
            }           
             return GetOcelotConfigAndReturn(internalConfigRepo);
        }        
        private static bool AdministrationApiInUse(IAdministrationPath adminPath)        {            
        return adminPath != null;
        }        //private static async Task SetFileConfig(IFileConfigurationSetter fileConfigSetter, IOptionsMonitor fileConfig)
        //{
        //    var response = await fileConfigSetter.Set(fileConfig.CurrentValue);

        //    if (IsError(response))
        //    {
        //        ThrowToStopOcelotStarting(response);
        //    }
        //}

        private static bool IsError(Response response)        
        {            
        return response == null || response.IsError;
        }        
        private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigurationRepository provider)        
        {            
        var ocelotConfiguration = provider.Get();            
        if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError)
            {
                ThrowToStopOcelotStarting(ocelotConfiguration);
            }            
            return ocelotConfiguration.Data;
        }        
        private static void ThrowToStopOcelotStarting(Response config)        
        {            
        throw new Exception($"Unable to start Ocelot, errors are: {string.Join(",", config.Errors.Select(x => x.ToString()))}");
        }        
        private static IApplicationBuilder CreateOcelotPipeline(IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)        
        {            
        var pipelineBuilder = new OcelotPipelineBuilder(builder.ApplicationServices);            //重写自定义管道
            pipelineBuilder.BuildAhphOcelotPipeline(pipelineConfiguration);            var firstDelegate = pipelineBuilder.Build();            

            builder.Properties["analysis.NextMiddlewareName"] = "TransitionToOcelotMiddleware";

            builder.Use(async (context, task) =>
            {                
            var downstreamContext = new DownstreamContext(context);                await firstDelegate.Invoke(downstreamContext);
            });            return builder;
        }        
        private static void ConfigureDiagnosticListener(IApplicationBuilder builder)        
        {            
        var env = builder.ApplicationServices.GetService();            var listener = builder.ApplicationServices.GetService();            
        var diagnosticListener = builder.ApplicationServices.GetService();
            diagnosticListener.SubscribeWithAdapter(listener);
        }
    }

这其中最主要的代码就是,重写配置文件获取这块。我在下面进行了截图,并圈出来了,大家自行查看吧。

代码重写好了。由于我们服务注册时通过扩展IOcelotBuilder,所以,我们需要在ConfigureServices方法引入Ocelot服务的时候比Ocelot多写一个方法,并传入相关的配置信息,如下所示:

services.AddOcelot()//注入Ocelot服务
                    .AddAuthLimitCache(option=> {
                        option.DbConnectionStrings = "Server=.;Database=Ocelot;User ID=sa;Password=1;";
                    });

这里的目的就是为了注入我们实现了IFileConfigurationRepository接口的SqlServerFileConfigurationRepository这个类。

接下来就是在管道中使用我们重写的Ocelot服务了。如下所示,在Configure方法中按如下代码进行使用:

app.UseAhphOcelot().Wait();

好了,以上就是实现的整个过程了。经过这么一分析是不是觉得很简单呢。当然具体为什么按照上面处理就能够从数据库获取配置了呢,这个还需要你分析了源码后才能了解。我也只是给你引路,传达我实现的思路。

源码

https://github.com/yilezhu/Ocelot.ConfigAuthLimitCache

总结

今天抽空对上篇文章进行了补充说明,目的是给大家阐述下,配置文件存储到数据库中的实现过程及原理。让你能够根据自身需要来进行改写来满足你的业务需求。当然我也只是给你引路,具体为什么这样实现下就能够成功呢?答案在Ocelot的源码中。


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/230752.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号