博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ocelot简易教程(七)之配置文件数据库存储插件源码解析
阅读量:4985 次
发布时间:2019-06-12

本文共 11386 字,大约阅读时间需要 37 分钟。

原文:

作者:依乐祝

原文地址:https://www.cnblogs.com/yilezhu/p/9852711.html

上篇文章给大家分享了如何集成我写的一个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(); /* 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
(); var listener = builder.ApplicationServices.GetService
(); var diagnosticListener = builder.ApplicationServices.GetService
(); 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的定制相关的·内容可以看我一个朋友写的系列博客,博客地址:https://www.cnblogs.com/jackcao/p/9928879.html 【.NET Core微服务实战-统一身份认证】网关篇,这里给你详细介绍了如何进行自定义限流以及客户端授权并重写Ocelot缓存成Redis!有兴趣的可以看下!

Ocelot简易教程目录

posted on
2018-12-21 00:37 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/10153474.html

你可能感兴趣的文章
符号修饰与函数签名、extern “C”(转载)
查看>>
JDK10 新特性
查看>>
css初级之属性选择器
查看>>
CentOS 查看进程状态
查看>>
tf.transpose()
查看>>
xgboost防止过拟合
查看>>
序列化模块
查看>>
周进度报告5
查看>>
测开之路十九:实现栈
查看>>
js提高篇
查看>>
《深入浅出wpf》第一章
查看>>
阿里架构师之Java 代码性能优化
查看>>
个人总结
查看>>
76.递归
查看>>
第一个web项目(静态,两页面)
查看>>
saltstack-day1
查看>>
SIGCHLD信号
查看>>
malloc、calloc、realloc的区别
查看>>
void 0
查看>>
javascript 备忘
查看>>