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);
}
}
这其中最主要的代码就是,重写配置文件获取这块。我在下面进行了截图,并圈出来了,大家自行查看吧。
代码重写好了。由于我们服务注册时通过扩展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简易教程目录
- Ocelot简易教程(一)之Ocelot是什么
- Ocelot简易教程(二)之快速开始1
- Ocelot简易教程(二)之快速开始2
- Ocelot简易教程(三)之主要特性及路由详解
- Ocelot简易教程(四)之请求聚合以及服务发现
- Ocelot简易教程(五)之集成IdentityServer认证以及授权
- Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
- Ocelot简易教程(七)之配置文件数据库存储插件源码解析
Ocelot简易教程(七)之配置文件数据库存储插件源码解析的更多相关文章
- Ocelot简易教程(六)之重写配置文件存储方式并优化响应数据
本来这篇文章在昨天晚上就能发布的,悲剧的是写了两三千字的文章居然没保存,结果我懵逼了.今天重新来写这篇文章.今天我们就一起来探讨下如何重写Ocelot配置文件的存储方式以及获取方式. 作者:依乐祝 原 ...
- Ocelot简易教程(五)之集成IdentityServer认证以及授权
Ocelot简易教程目录 Ocelot简易教程(一)之Ocelot是什么 Ocelot简易教程(二)之快速开始1 Ocelot简易教程(二)之快速开始2 Ocelot简易教程(三)之主要特性及路由详解 ...
- Ocelot简易教程(四)之请求聚合以及服务发现
上篇文章给大家讲解了Ocelot的一些特性并对路由进行了详细的介绍,今天呢就大家一起来学习下Ocelot的请求聚合以及服务发现功能.希望能对大家有所帮助. 作者:依乐祝 原文地址:https://ww ...
- Ocelot简易教程(三)之主要特性及路由详解
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9664977.html 上篇<Ocelot简易教程(二)之快速开始2>教大家如何快速跑起来一个 ...
- Ocelot简易教程(二)之快速开始2
为什么这篇的标题叫"Ocelot简易教程(二)之快速开始2"呢,因为很多朋友跟我说上一篇" Ocelot简易教程(二)之快速开始1"内容太少了,只是简单介绍Oc ...
- Ocelot简易教程(二)之快速开始1
Ocelot简易教程目录 Ocelot简易教程(一)之Ocelot是什么 Ocelot简易教程(二)之快速开始1 Ocelot简易教程(二)之快速开始2 Ocelot简易教程(三)之主要特性及路由详解 ...
- Ocelot简易教程(一)之Ocelot是什么
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9557375.html Ocelot简易教程目录 Ocelot简易教程(一)之Ocelot是什么 Ocel ...
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
- Netty 源码解析(七): NioEventLoop 工作流程
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...
随机推荐
- @无痕客 https://www.cnblogs.com/wuhenke/archive/2012/12/24/2830530.html 通篇引用
无痕客 https://www.cnblogs.com/wuhenke/archive/2012/12/24/2830530.html 关于Async与Await的FAQ 关于Async与Await的 ...
- [Java]异常在项目中的使用
自己经历过的两个项目都有自定义异常,网上找了项目中自定义异常的例子: https://blog.csdn.net/aiyaya_/article/details/78989226. 这个例子基本上来说 ...
- Player2.0
由于上次做的播放器实在是有点丑,加之这期间学习了许多关于页面排版的东西,这次做第二次作业就重新来了一遍“换汤不换药”的操作.其实刚开始看到的作业要求的时候,有点傻眼猫咪,但是做到现在这个程度,本人也是 ...
- Vue 获取登录用户名
本来是打算登录的时候把用户名传过去,试了几次都没成功,然后改成用cookie保存用户名,然后在读取就行了, 登录时候设置cookie setCookie(c_name,c_pwd,exdays) { ...
- c++中段自评
不知不觉,学期已过一大半.也是时候对自己的编程水平重新进行一次评估了. 1.通过最近的中段测试和acm新手赛的洗礼,以及之前的课前预习.课中学习.和课后作业的锻炼,我逐渐体会到编程语言的魅力同时也理解 ...
- SSH通过密钥登陆
A服务器上操作 ssh-keygen -t rsa/dsa 后面所带参数rsa/dsa为加密方式,默认为dsa [root@localhost ~]# ssh-keygen Generating pu ...
- WBS分析
我们的产品是2048是一个基于安卓平台开发的小游戏,WBS如下: 进一步优化版本: 小组最终版本:
- vector作为二维数组
vector本来就是可以用来代替一维数组的,vector提供了operator[]函数,可以像数组一样的操作,而且还有边界检查,动态改变大小. 这里只介绍用它来代替二维的数组,二维以上的可以依此类推. ...
- Shell条件测试和流程控制-4
- 使用jenkins自部署Coding项目
下载安装jenkins 下载地址:https://jenkins.io/download/ 安装后通过主机的8080端口进行程序设置,插件安装默认的就好 Jenkins项目目录:C:\Program ...