本篇將介紹如何擴展Ocelot中間件實現自定義網關,並使用2種不一樣數據庫來演示Ocelot配置信息存儲和動態更新功能,內容也是從實際設計出發來編寫咱們本身的中間件,本文內容涵蓋設計思想內容和代碼內容,我但願園友們最好跟着我這個文章的思路先理解好後再看源代碼,這樣有利於融會貫通,本篇的文檔及源碼將會在GitHub上開源,每篇的源代碼我將用分支的方式管理,本篇使用的分支爲
course1
。html附文檔及源碼下載地址:[https://github.com/jinyancao/CtrAuthPlatform/tree/course1]mysql
上一篇中咱們介紹了Ocelot中要知足咱們需求,咱們須要把配置信息轉到數據庫存儲,今天咱們就從數據庫設計開始,數據庫設計我採用的是PowerDesigner
,首先打開軟件,新建一個概念模型。根據Ocelot的配置文件,咱們能夠發現,配置信息由全局配置信息和路由信息組成,這時候咱們能夠設計表結構以下,爲了知足後續多個路由的切換,增長了網關和路由多對多關係,之後咱們能夠隨時根據不一樣規則切換,詳細的表字段能夠自行根據Ocelot配置文檔和設計文檔對照查看,這裏我移除了限流的字段,由於咱們後續須要自定義限流,用不上原來的方法。
git
生成物理模型
數據庫設計好後,咱們須要把概念模型轉成物理模型,使用Ctrl+Shift+P
快捷鍵,咱們默認使用MSSQL2008R2實現配置存儲,全部在彈出的對話框中選擇,而後點擊確認後會自動生成MSSQL2008R2的物理模型,能夠看到數據類型和表之間的關連關係都生成好了,奈斯,一切都是那麼完美,若是主鍵爲自增類型,手動標記下便可。
github
如今咱們須要生成咱們建立數據庫的SQL腳本了,別忘了保存下剛纔生成的物理模型,由於之後還須要用到。sql
生成數據庫腳本數據庫
如圖所示,可使用快捷鍵Ctrl+G
生成數據庫腳本,點擊確認生成並保存,而後把生成的腳本在咱們新建的數據庫裏執行,這樣咱們的數據庫就設計完成了。
json
咱們使用VS2017
新建一個.NETCORE2.1
項目,而後新建一個類庫來實現咱們Ocelot定製版
中間件,建好後項目結構以下,如今開始咱們第一個AhphOcelot
定製中間件編寫。
c#
首先咱們回顧下【.NET Core項目實戰-統一認證平臺】第二章網關篇-重構Ocelot來知足需求的源碼解析,關於配置信息的讀取以下,咱們只須要重寫下CreateConfiguration
方法實現從數據庫裏取就能夠了,既然有思路了,api
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { //建立配置信息 var configuration = await CreateConfiguration(builder); ConfigureDiagnosticListener(builder); return CreateOcelotPipeline(builder, pipelineConfiguration); }
那就開始改造吧,咱們新建一個Ctr.AhphOcelot
類庫,來實現這個中間件,首先新建自定義中間件擴展,這個擴展是在原有的Ocelot
的基礎上進行改造,因此須要先在Nuget
中安裝Ocelot
,這系列課程咱們以最新的Ocelot 12.0.1
版本進行擴展。
瀏覽器
首先咱們要了解,Ocelot
的配置信息是怎麼加載進來的呢?
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>>(); // now create the config var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>(); var internalConfig = await internalConfigCreator.Create(fileConfig.CurrentValue); //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); } return GetOcelotConfigAndReturn(internalConfigRepo); }
查看源碼後發現是是從OcelotBuilder
加載的配置文件,也就是最先的AddOcelot()
方法時注入的。
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) { Configuration = configurationRoot; Services = services; //服務註冊,可使用IOptions<FileConfiguration>調用 Services.Configure<FileConfiguration>(configurationRoot); .... }
如今咱們要實現從數據庫提取配置信息,能夠查看下Ocelot
是否給咱們提供了相關擴展接口,經過Ctrl+F
查找FileConfiguration
實體在哪些地方能夠返回,IFileConfigurationRepository
接口一眼就能認出,配置文件倉儲類,咱們能夠重寫這個接口實現便可完成配置文件從數據庫提取,果真Ocelot是爲定製而生,其實若是沒有這個接口問題也不大,咱們本身去定義和實現這個接口也同樣能夠完成。
using System.Threading.Tasks; using Ocelot.Configuration.File; using Ocelot.Responses; namespace Ocelot.Configuration.Repository { public interface IFileConfigurationRepository { Task<Response<FileConfiguration>> Get(); Task<Response> Set(FileConfiguration fileConfiguration); } }
咱們看看這個接口是否有默認實現,DiskFileConfigurationRepository
方法實現了這個接口,經過名稱就知道是直接從配置文件提取配置信息,再看下這個接口應用到哪裏,繼續Ctrl+F
找到,FileConfigurationPoller
和FileAndInternalConfigurationSetter
兩個地方用到了這個接口,其中FileConfigurationPoller
實現了IHostedService
後臺任務,咱們不難看出,這個是一個定時更新任務,實際咱們配置信息變動,確定由管理員本身修改測試無誤後發起,這裏咱們用不上,可是實現思路能夠了解下。FileAndInternalConfigurationSetter
是配置文件更新方法,這裏咱們若是使用數據庫存儲,更新確定由咱們本身管理界面更新,因此也用不上,這時有人會問,那若是配置文件發生變動了,咱們怎麼去更新。這時候咱們須要瞭解配置信息在哪裏使用,是否使用了緩存。其實上面也給出了答案,就是IInternalConfiguration
.
// now create the config var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>(); var internalConfig = await internalConfigCreator.Create(fileConfig.CurrentValue);
如今問題都梳理清楚了,如今咱們實現的思路就是,首先經過數據庫實現
IFileConfigurationRepository
接口內容(更新不須要實現,前面說過了),而後再咱們數據庫裏修改了配置,更新IInternalConfiguration
配置信息,便可完成咱們的自定義任何地方的存儲。
開發的思路就是頂層開始一步一步往下實現,最後完成咱們的擴展。如今回到咱們本身的代碼,修改配置信息代碼以下,是否是精簡不少了,可是有2個問題未解決,一是須要實現IFileConfigurationRepository
,二是還沒實現動態更新。
private static async Task<IInternalConfiguration> CreateConfiguration(IApplicationBuilder builder) { //提取文件配置信息 var fileConfig = await builder.ApplicationServices.GetService<IFileConfigurationRepository>().Get(); var internalConfigCreator = builder.ApplicationServices.GetService<IInternalConfigurationCreator>(); var internalConfig = await internalConfigCreator.Create(fileConfig.Data); //若是配置文件錯誤直接拋出異常 if (internalConfig.IsError) { ThrowToStopOcelotStarting(internalConfig); } //配置信息緩存,這塊須要注意實現方式,由於後期咱們須要改造下知足分佈式架構,這篇不作講解 var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>(); internalConfigRepo.AddOrReplace(internalConfig.Data); return GetOcelotConfigAndReturn(internalConfigRepo); }
一、實現IFileConfigurationRepository接口
本系列全部課程都是基於輕量級的
ORM
框架dapper
實現
首先須要NuGet
包裏添加Dapper
,而後咱們須要把設計的表生成實體,至於如何生成這裏就不介紹了,實現方式不少,相關的帖子不少。使用Dapper時,咱們須要知道知道鏈接方式,這時須要在中間件的基礎上擴充一個配置文件接收配置數據,這樣咱們才能使用配置的信息內容。
namespace Ctr.AhphOcelot.Configuration { /// <summary> /// 金焰的世界 /// 2018-11-11 /// 自定義配置信息 /// </summary> public class AhphOcelotConfiguration { /// <summary> /// 數據庫鏈接字符串 /// </summary> public string DbConnectionStrings { get; set; } } }
如今能夠實現接口了,詳細代碼以下,代碼很簡單,就是從數據庫查詢出錄入的內容,使用dapper實現。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.Model; using Dapper; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Ocelot.Responses; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Text; using System.Threading.Tasks; namespace Ctr.AhphOcelot.DataBase.SqlServer { /// <summary> /// 金焰的世界 /// 2018-11-11 /// 使用SqlServer來實現配置文件倉儲接口 /// </summary> public class SqlServerFileConfigurationRepository : IFileConfigurationRepository { private readonly AhphOcelotConfiguration _option; public SqlServerFileConfigurationRepository(AhphOcelotConfiguration option) { _option = option; } /// <summary> /// 從數據庫中獲取配置信息 /// </summary> /// <returns></returns> public async Task<Response<FileConfiguration>> Get() { #region 提取配置信息 var file = new FileConfiguration(); //提取默認啓用的路由配置信息 string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1"; //提取全局配置信息 using (var connection = new SqlConnection(_option.DbConnectionStrings)) { var result = await connection.QueryFirstOrDefaultAsync<AhphGlobalConfiguration>(glbsql); if (result != null) { var glb = new FileGlobalConfiguration(); //賦值全局信息 glb.BaseUrl = result.BaseUrl; glb.DownstreamScheme = result.DownstreamScheme; glb.RequestIdKey = result.RequestIdKey; glb.HttpHandlerOptions = result.HttpHandlerOptions?.ToObject<FileHttpHandlerOptions>(); glb.LoadBalancerOptions = result.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>(); glb.QoSOptions = result.QoSOptions?.ToObject<FileQoSOptions>(); glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider?.ToObject<FileServiceDiscoveryProvider>(); file.GlobalConfiguration = glb; //提取全部路由信息 string routesql = "select T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1"; var routeresult = (await connection.QueryAsync<AhphReRoute>(routesql, new { result.AhphId }))?.AsList(); if (routeresult != null && routeresult.Count > 0) { var reroutelist = new List<FileReRoute>(); foreach (var model in routeresult) { var m = new FileReRoute(); m.AuthenticationOptions = model.AuthenticationOptions?.ToObject<FileAuthenticationOptions>(); m.FileCacheOptions = model.CacheOptions?.ToObject<FileCacheOptions>(); m.DelegatingHandlers = model.DelegatingHandlers?.ToObject<List<string>>(); m.LoadBalancerOptions = model.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>(); m.QoSOptions = model.QoSOptions?.ToObject<FileQoSOptions>(); m.DownstreamHostAndPorts = model.DownstreamHostAndPorts?.ToObject<List<FileHostAndPort>>(); //開始賦值 m.DownstreamPathTemplate = model.DownstreamPathTemplate; m.DownstreamScheme = model.DownstreamScheme; m.Key = model.RequestIdKey; m.Priority = model.Priority ?? 0; m.RequestIdKey = model.RequestIdKey; m.ServiceName = model.ServiceName; m.UpstreamHost = model.UpstreamHost; 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); } //因爲數據庫存儲可不實現Set接口直接返回 public async Task<Response> Set(FileConfiguration fileConfiguration) { return new OkResponse(); } } }
如今又延伸出兩個問題.第一個是
AhphOcelotConfiguration
這個信息從哪讀取的?第二是SqlServerFileConfigurationRepository
在哪注入。
其實讀過我前面中間件源碼解析的同窗可能已經知道了,就是在AddOcelot
裏注入的,如今咱們就可使用相同的方式實現本身的擴展。添加本身的ServiceCollectionExtensions
擴展。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.DataBase.SqlServer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Ocelot.Configuration.Repository; using Ocelot.DependencyInjection; using System; namespace Ctr.AhphOcelot.Middleware { /// <summary> /// 金焰的世界 /// 2018-11-11 /// 擴展Ocelot實現的自定義的注入 /// </summary> public static class ServiceCollectionExtensions { /// <summary> /// 添加默認的注入方式,全部須要傳入的參數都是用默認值 /// </summary> /// <param name="builder"></param> /// <returns></returns> public static IOcelotBuilder AddAhphOcelot(this IOcelotBuilder builder, Action<AhphOcelotConfiguration> option) { builder.Services.Configure(option); //配置信息 builder.Services.AddSingleton( resolver => resolver.GetRequiredService<IOptions<AhphOcelotConfiguration>>().Value); //配置文件倉儲注入 builder.Services.AddSingleton<IFileConfigurationRepository, SqlServerFileConfigurationRepository>(); return builder; } } }
有木有很簡單呢?到這裏從數據庫中提取配置信息都完成啦,如今咱們開始來測試下,看是否知足了咱們的需求。
新建一個Ctr.AuthPlatform.Gateway
網關項目,添加咱們的中間件項目引用,修改Startup.cs
代碼以下
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ocelot.DependencyInjection; using Ctr.AhphOcelot.Middleware; namespace Ctr.AuthPlatform.Gateway { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddOcelot().AddAhphOcelot(option=> { option.DbConnectionStrings = "Server=.;Database=Ctr_AuthPlatform;User ID=sa;Password=bl123456;"; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseAhphOcelot().Wait(); } } }
就實現了自定義的網關,是否是很優雅呢?可是是否達到了咱們預期的網關效果了,咱們來直接從數據庫裏插入測試數據,並新建一個測試項目。測試數據腳本以下
--插入全局測試信息 insert into AhphGlobalConfiguration(GatewayName,RequestIdKey,IsDefault,InfoStatus) values('測試網關','test_gateway',1,1); --插入路由分類測試信息 insert into AhphReRoutesItem(ItemName,InfoStatus) values('測試分類',1); --插入路由測試信息 insert into AhphReRoute values(1,'/ctr/values','[ "GET" ]','','http','/api/Values','[{"Host": "localhost","Port": 9000 }]', '','','','','','','',0,1); --插入網關關聯表 insert into dbo.AhphConfigReRoutes values(1,1);
測試項目結構以下,就是默認的一個api項目,修改下啓動端口爲9000。
爲了方便調試.NETCORE項目,我建議使用dotnet run
方式,分別啓動網關(7777端口)和測試服務(9999端口)。優先啓動網關項目,想想還有點小激動呢,開始運行項目,納尼,盡然報錯,並且是熟悉的未將對象引用到實例化錯誤,根據異常內容能夠看到是在驗證的時候報錯,咱們能夠查看下Ocelot對應的源代碼,發現問題所在了。
咱們在一些未定義的配置項目使用了爲空的賦值。而Ocleot裏面對於很多配置項目未作非空驗證。好比RateLimitOptionsCreator
對於FileGlobalConfiguration
未作非空驗證,相似這樣的地方還有很多,我但願下次Ocelot更新時最好增長這類非空驗證,這樣便於自定義擴展,而Ocelot內部實現了默認實例化,因此咱們以前從數據庫取值賦值時寫法須要改進,修改後的代碼以下。
using Ctr.AhphOcelot.Configuration; using Ctr.AhphOcelot.Model; using Dapper; using Ocelot.Configuration.File; using Ocelot.Configuration.Repository; using Ocelot.Responses; using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Text; using System.Threading.Tasks; namespace Ctr.AhphOcelot.DataBase.SqlServer { /// <summary> /// 金焰的世界 /// 2018-11-11 /// 使用SqlServer來實現配置文件倉儲接口 /// </summary> public class SqlServerFileConfigurationRepository : IFileConfigurationRepository { private readonly AhphOcelotConfiguration _option; public SqlServerFileConfigurationRepository(AhphOcelotConfiguration option) { _option = option; } /// <summary> /// 從數據庫中獲取配置信息 /// </summary> /// <returns></returns> public async Task<Response<FileConfiguration>> Get() { #region 提取配置信息 var file = new FileConfiguration(); //提取默認啓用的路由配置信息 string glbsql = "select * from AhphGlobalConfiguration where IsDefault=1 and InfoStatus=1"; //提取全局配置信息 using (var connection = new SqlConnection(_option.DbConnectionStrings)) { var result = await connection.QueryFirstOrDefaultAsync<AhphGlobalConfiguration>(glbsql); if (result != null) { var glb = new FileGlobalConfiguration(); //賦值全局信息 glb.BaseUrl = result.BaseUrl; glb.DownstreamScheme = result.DownstreamScheme; glb.RequestIdKey = result.RequestIdKey; //glb.HttpHandlerOptions = result.HttpHandlerOptions?.ToObject<FileHttpHandlerOptions>(); //glb.LoadBalancerOptions = result.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>(); //glb.QoSOptions = result.QoSOptions?.ToObject<FileQoSOptions>(); //glb.ServiceDiscoveryProvider = result.ServiceDiscoveryProvider?.ToObject<FileServiceDiscoveryProvider>(); 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 T2.* from AhphConfigReRoutes T1 inner join AhphReRoute T2 on T1.ReRouteId=T2.ReRouteId where AhphId=@AhphId and InfoStatus=1"; var routeresult = (await connection.QueryAsync<AhphReRoute>(routesql, new { result.AhphId }))?.AsList(); if (routeresult != null && routeresult.Count > 0) { var reroutelist = new List<FileReRoute>(); foreach (var model in routeresult) { var m = new FileReRoute(); //m.AuthenticationOptions = model.AuthenticationOptions?.ToObject<FileAuthenticationOptions>(); //m.FileCacheOptions = model.CacheOptions?.ToObject<FileCacheOptions>(); //m.DelegatingHandlers = model.DelegatingHandlers?.ToObject<List<string>>(); //m.LoadBalancerOptions = model.LoadBalancerOptions?.ToObject<FileLoadBalancerOptions>(); //m.QoSOptions = model.QoSOptions?.ToObject<FileQoSOptions>(); //m.DownstreamHostAndPorts = model.DownstreamHostAndPorts?.ToObject<List<FileHostAndPort>>(); 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.RequestIdKey; m.Priority = model.Priority ?? 0; m.RequestIdKey = model.RequestIdKey; m.ServiceName = model.ServiceName; m.UpstreamHost = model.UpstreamHost; 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); } //因爲數據庫存儲可不實現Set接口直接返回 public async Task<Response> Set(FileConfiguration fileConfiguration) { return new OkResponse(); } } }
而後從新運行,網關啓動成功。
接着咱們啓動咱們測試的服務,而後瀏覽器先訪問http://localhost:9000/api/values
地址,測試地址正常訪問。
而後使用測試網關路由地址訪問http://localhost:7777/ctr/values
,顯示內容和本地訪問同樣,證實網關路由生效,是否是有點小激動呢?咱們完成了從配置信息中取網關路由信息擴展。
最後咱們回顧下這篇內容,我是從設計到實現一步一步講解和實現的,並且實現過程是根據需求慢慢剖析再局部實現的,我發現如今不少人在平時學習基本都是結果未導向,不多去關心中間的實現過程,長此以往基本就會喪失解決問題的思路,寫的這麼詳細,也是但願給你們一個解決問題的思路,目前咱們實現了從數據庫中提取配置信息並在網關中生效,可是還未實現動態更新和擴展其餘數據庫存儲,你們也能夠先本身嘗試如何實現。
下一篇咱們將會實現網關路由的動態更新,會提供幾種更新思路,根據實際狀況擇優選擇。而後在使用Mysql數據庫來存儲配置信息,並擴展此網關實現很優雅的配置,爲何使用mysql擴展實現呢?由於.netcore已經跨平臺啦,後期咱們準備在Centos下實現容器化部署,這時咱們就準備以mysql爲例進行講解,本網關全部內容源碼都會實現sqlserver和mysql兩種方式,其餘存儲方式可自行擴展便可。
最後項目全部的文檔在源碼的文檔目錄,文檔按照課程源碼文件夾區分,本文的文檔標識course1
。
個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=l0q6lfr3asgg