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

简介: 作者:依乐祝原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html 上篇文章给大家分享了如何集成我写的一个Ocelot扩展插件把Ocelot的配置存储到数据库中。

作者:依乐祝
原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html

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

源码解析过程

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

代码如下:

 /// <summary>
    /// yilezhu
    /// 2018.10.22
    /// 实现从SQLSERVER数据库中提取配置信息
    /// </summary>
    public class SqlServerFileConfigurationRepository : IFileConfigurationRepository
    {
        private readonly IOcelotCache<FileConfiguration> _cache;
        private readonly IOcelotLogger _logger;
        private readonly ConfigAuthLimitCacheOptions _option;
        public SqlServerFileConfigurationRepository(ConfigAuthLimitCacheOptions option, IOcelotCache<FileConfiguration> cache, IOcelotLoggerFactory loggerFactory)
        {
            _option = option;
            _cache = cache;
            _logger = loggerFactory.CreateLogger<SqlServerFileConfigurationRepository>();
        }

        public Task<Response> Set(FileConfiguration fileConfiguration)
        {
            _cache.AddAndDelete(_option.CachePrefix + "FileConfiguration", fileConfiguration, TimeSpan.FromSeconds(1800), "");
            return Task.FromResult((Response)new OkResponse());
        }

        /// <summary>
        /// 提取配置信息
        /// </summary>
        /// <returns></returns>
        public async Task<Response<FileConfiguration>> Get()
        {
            var config = _cache.Get(_option.CachePrefix + "FileConfiguration", "");

            if (config != null)
            {
                return new OkResponse<FileConfiguration>(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<OcelotGlobalConfiguration>(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<FileHttpHandlerOptions>();
                    }
                    if (!String.IsNullOrEmpty(result.LoadBalancerOptions))
                    {
                        glb.LoadBalancerOptions = result.LoadBalancerOptions.ToObject<FileLoadBalancerOptions>();
                    }
                    if (!String.IsNullOrEmpty(result.QoSOptions))
                    {
                        glb.QoSOptions = result.QoSOptions.ToObject<FileQoSOptions>();
                    }
                    if (!String.IsNullOrEmpty(result.ServiceDiscoveryProvider))
                    {
                        glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider.ToObject<FileServiceDiscoveryProvider>();
                    }
                    file.GlobalConfiguration = glb;

                    //提取路由信息

                    string routesql = "select * from OcelotReRoutes where OcelotGlobalConfigurationId=@OcelotGlobalConfigurationId and IsStatus=1";
                    var routeresult = (await connection.QueryAsync<OcelotReRoutes>(routesql, new { OcelotGlobalConfigurationId=result.Id })).AsList();
                    if (routeresult != null && routeresult.Count > 0)
                    {
                        var reroutelist = new List<FileReRoute>();
                        foreach (var model in routeresult)
                        {
                            var m = new FileReRoute();
                            if (!String.IsNullOrEmpty(model.AuthenticationOptions))
                            {
                                m.AuthenticationOptions = model.AuthenticationOptions.ToObject<FileAuthenticationOptions>();
                            }
                            if (!String.IsNullOrEmpty(model.CacheOptions))
                            {
                                m.FileCacheOptions = model.CacheOptions.ToObject<FileCacheOptions>();
                            }
                            if (!String.IsNullOrEmpty(model.DelegatingHandlers))
                            {
                                m.DelegatingHandlers = model.DelegatingHandlers.ToObject<List<string>>();
                            }
                            if (!String.IsNullOrEmpty(model.LoadBalancerOptions))
                            {
                                m.LoadBalancerOptions = model.LoadBalancerOptions.ToObject<FileLoadBalancerOptions>();
                            }
                            if (!String.IsNullOrEmpty(model.QoSOptions))
                            {
                                m.QoSOptions = model.QoSOptions.ToObject<FileQoSOptions>();
                            }
                            if (!String.IsNullOrEmpty(model.DownstreamHostAndPorts))
                            {
                                m.DownstreamHostAndPorts = model.DownstreamHostAndPorts.ToObject<List<FileHostAndPort>>();
                            }
                            //开始赋值
                            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<List<string>>();
                            }
                            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<FileConfiguration>(null);
            }
            return new OkResponse<FileConfiguration>(file);
        }
    }

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

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

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

   /// <summary>
    /// yilezhu
    /// 2018.10.22
    /// 扩展IApplicationBuilder,新增use方法
    /// </summary>
    public static class OcelotMiddlewareExtensions
    {
        /// <summary>
        /// 扩展UseOcelot
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static async Task<IApplicationBuilder> UseAhphOcelot(this IApplicationBuilder builder)
        {
            await builder.UseAhphOcelot(new OcelotPipelineConfiguration());
            return builder;
        }

        /// <summary>
        /// 重写Ocelot,带参数
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="pipelineConfiguration"></param>
        /// <returns></returns>
        public static async Task<IApplicationBuilder> UseAhphOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration)
        {
            var configuration = await CreateConfiguration(builder);

            ConfigureDiagnosticListener(builder);

            return CreateOcelotPipeline(builder, pipelineConfiguration);
        }

        private static async Task<IInternalConfiguration> 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<IOptionsMonitor<FileConfiguration>>();
            var fileConfig = await builder.ApplicationServices.GetService<IFileConfigurationRepository>().Get();
            // now create the config
            var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>();
            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<IInternalConfigurationRepository>();
            internalConfigRepo.AddOrReplace(internalConfig.Data);

            //fileConfig.OnChange(async (config) =>
            //{
            //    var newInternalConfig = await internalConfigCreator.Create(config);
            //    internalConfigRepo.AddOrReplace(newInternalConfig.Data);
            //});

            var adminPath = builder.ApplicationServices.GetService<IAdministrationPath>();

            var configurations = builder.ApplicationServices.GetServices<OcelotMiddlewareConfigurationDelegate>();

            // 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<IFileConfigurationSetter>();

                //  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<FileConfiguration> 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();

            /*
            inject first delegate into first piece of asp.net middleware..maybe not like this
            then because we are updating the http context in ocelot it comes out correct for
            rest of asp.net..
            */

            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<IHostingEnvironment>();
            var listener = builder.ApplicationServices.GetService<OcelotDiagnosticListener>();
            var diagnosticListener = builder.ApplicationServices.GetService<DiagnosticListener>();
            diagnosticListener.SubscribeWithAdapter(listener);
        }
    }

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

1540470343082

代码重写好了。由于我们服务注册时通过扩展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的源码中。

Ocelot简易教程目录

  1. Ocelot简易教程(一)之Ocelot是什么
  2. Ocelot简易教程(二)之快速开始1
  3. Ocelot简易教程(二)之快速开始2
  4. Ocelot简易教程(三)之主要特性及路由详解
  5. Ocelot简易教程(四)之请求聚合以及服务发现
  6. Ocelot简易教程(五)之集成IdentityServer认证以及授权
  7. Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
  8. Ocelot简易教程(七)之配置文件数据库存储插件源码解析
目录
相关文章
|
存储 Oracle 关系型数据库
服务器数据恢复—光纤存储上oracle数据库数据恢复案例
一台光纤服务器存储上有16块FC硬盘,上层部署了Oracle数据库。服务器存储前面板2个硬盘指示灯显示异常,存储映射到linux操作系统上的卷挂载不上,业务中断。 通过storage manager查看存储状态,发现逻辑卷状态失败。再查看物理磁盘状态,发现其中一块盘报告“警告”,硬盘指示灯显示异常的2块盘报告“失败”。 将当前存储的完整日志状态备份下来,解析备份出来的存储日志并获得了关于逻辑卷结构的部分信息。
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
2121 5
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
533 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
存储 关系型数据库 数据库
高性能云盘:一文解析RDS数据库存储架构升级
性能、成本、弹性,是客户实际使用数据库过程中关注的三个重要方面。RDS业界率先推出的高性能云盘(原通用云盘),是PaaS层和IaaS层的深度融合的技术最佳实践,通过使用不同的存储介质,为客户提供同时满足低成本、低延迟、高持久性的体验。
|
SQL 存储 分布式数据库
分布式存储数据恢复—hbase和hive数据库数据恢复案例
分布式存储数据恢复环境: 16台某品牌R730xd服务器节点,每台服务器节点上有数台虚拟机。 虚拟机上部署Hbase和Hive数据库。 分布式存储故障: 数据库底层文件被误删除,数据库不能使用。要求恢复hbase和hive数据库。
754 12
|
存储 SQL NoSQL
【赵渝强老师】达梦数据库的逻辑存储结构
本文介绍了达梦数据库的存储结构,包括逻辑和物理存储两部分。逻辑存储结构由数据库(Database)、表空间(Tablespaces)、段(Segments)、簇(Cluster)和页(Page)组成。数据库是最大逻辑单元,包含所有表、索引等;表空间由数据文件组成,用于存储对象;段由簇构成,簇包含连续的数据页;页是最小存储单元。文中还提供了查询表空间、段和页大小的SQL语句,并附有视频讲解和示意图。
793 7
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
789 4
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
存储 SQL 安全
【赵渝强老师】达梦数据库的物理存储结构
本文介绍了达梦数据库的存储结构及各类物理文件的作用。达梦数据库通过逻辑和物理存储结构管理数据,包含配置文件(如dm.ini、sqllog.ini)、控制文件(dm.ctl)、数据文件(*.dbf)、重做日志文件(*.log)、归档日志文件、备份文件(*.bak)等。配置文件用于功能设置,控制文件记录数据库初始信息,数据文件存储实际数据,重做日志用于故障恢复,归档日志增强数据安全性,备份文件保障数据完整性,跟踪与事件日志辅助问题分析。这些文件共同确保数据库高效、稳定运行。
935 0
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

热门文章

最新文章

推荐镜像

更多
  • DNS