.Net Core 服務閘道器、服務註冊、服務發現總結
阿新 • • 發佈:2020-09-15
一、簡介
Ocelot:Ocelot是一個用.NET Core實現並且開源的API閘道器,它功能強大,包括了:路由、請求聚合、服務發現、認證、鑑權、限流熔斷、並內建了負載均衡器與Service Fabric、Butterfly Tracing整合,官方文件:https://ocelot.readthedocs.io/en/latest/index.html
Consul:Consul本質上是一個Socket通訊中介軟體。它主要實現了兩個功能,服務註冊與發現與自身的負載均衡的叢集。官方文件:https://www.consul.io/docs
二、API閘道器搭建
1、新建一個ASP.NET Core Web專案,選用空模板建立
2、安裝Ocelot相關包
3、在Startup中配置Ocelot
public void ConfigureServices(IServiceCollection services) { services.AddLogDashboard(opt => { //授權登陸 opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "123qwE*"));//請求追蹤 opt.CustomLogModel<RequestTraceLogModel>(); }); services .AddOcelot() //服務發現 .AddConsul() //快取 .AddCacheManager(x => { x.WithDictionaryHandle(); })//服務質量控制 .AddPolly(); services.AddCors(options => { options.AddPolicy(_defaultCorsPolicyName, builder => builder .WithOrigins( this.Configuration["App:CorsOrigins"] .Split(",", StringSplitOptions.RemoveEmptyEntries) .ToArray() ) .AllowAnyMethod() .AllowAnyHeader()); }); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseLogDashboard(); app.UseOcelot().Wait(); app.UseCors(_defaultCorsPolicyName); }
新建Ocelot.json檔案,開啟了服務發現,後面講解服務發現配置,具體如下:
{ "Routes": [ { //服務名稱,開啟服務發現時需要配置 "ServiceName": "web-api", //是否開啟服務發現 "UseServiceDiscovery": true, //下游服務路由模板 "DownstreamPathTemplate": "/{url}", //下游服務http schema "DownstreamScheme": "http", //下游服務的地址,如果使用LoadBalancer的話這裡可以填多項 //"DownstreamHostAndPorts": [ // { // "Host": "192.168.1.205", // "Port": 12000 // } //], "UpstreamPathTemplate": "/{url}", //上游請求http方法,可使用陣列 "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ], /** * 負載均衡的演算法: * LeastConnection – 跟蹤哪些服務正在處理請求,並將新請求傳送到具有最少現有請求的服務。演算法狀態沒有分佈在Ocelot叢集中。 * RoundRobin – 遍歷可用服務併發送請求。演算法狀態沒有分佈在Ocelot叢集中。 * NoLoadBalance – 從配置或服務發現中獲取第一個可用服務 * CookieStickySessions - 使用cookie將所有請求貼上到特定伺服器 */ "LoadBalancerOptions": { "Type": "LeastConnection" //以下配置再設定了 CookieStickySessions 後需要開啟 //用於粘性會話的cookie的金鑰 //"Key": "ASP.NET_SessionId", //會話被阻塞的毫秒數 //"Expiry": 1800000 }, //快取 "FileCacheOptions": { "TtlSeconds": 15 //"Region": "" }, //限流 "RateLimitOptions": { //包含客戶端白名單的陣列。這意味著該陣列中的客戶端將不受速率限制的影響 "ClientWhitelist": [], //是否啟用端點速率限制 "EnableRateLimiting": true, //指定限制所適用的期間,例如1s,5m,1h,1d等。如果在該期間內發出的請求超出限制所允許的數量,則需要等待PeriodTimespan過去,然後再發出其他請求 "Period": "1s", //指定可以在一定秒數後重試 "PeriodTimespan": 1, //指定客戶端在定義的時間內可以發出的最大請求數 "Limit": 10 }, //熔斷 "QoSOptions": { //允許多少個異常請求 "ExceptionsAllowedBeforeBreaking": 3, //熔斷的時間,單位為毫秒 "DurationOfBreak": 1000, //如果下游請求的處理時間超過多少則自如將請求設定為超時 "TimeoutValue": 5000 }, "HttpHandlerOptions": { //是否開啟路由追蹤 "UseTracing": false } } ], "GlobalConfiguration": { //Consul服務發現 "ServiceDiscoveryProvider": { "Host": "192.168.1.37", "Port": 8500, "Type": "Consul" }, //外部暴露的Url "BaseUrl": "http://localhost:17450", //限流擴充套件配置 "RateLimitOptions": { //指定是否禁用X-Rate-Limit和Retry-After標頭 "DisableRateLimitHeaders": false, //當請求過載被截斷時返回的訊息 "QuotaExceededMessage": "Oh,Oops!", //當請求過載被截斷時返回的http status "HttpStatusCode": 4421, //用來識別客戶端的請求頭,預設是 ClientId "ClientIdHeader": "ClientId" } } }
appsettings.json配置如下:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "App": { "CorsOrigins": "http://192.168.1.205:4422" } }
修改Program.cs,安裝NLog,因為閘道器啟用了日誌面板
public class Program { public static void Main(string[] args) { var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger(); try { logger.Debug("init main"); CreateWebHostBuilder(args).Build().Run(); } catch (Exception ex) { //NLog: catch setup errors logger.Error(ex, "Stopped program because of exception"); throw; } finally { // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) NLog.LogManager.Shutdown(); } } private static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, builder) => { builder .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .AddJsonFile("appsettings.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile("Ocelot.json"); }) .UseStartup<Startup>() .ConfigureLogging(logging => { logging.ClearProviders(); logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); }) .UseNLog(); // NLog: setup NLog for Dependency injection }
nlog.config配置如下:
<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" throwExceptions="false" internalLogLevel="Off" internalLogFile="nlog-internal.log"> <variable name="myvar" value="myvalue"/> <targets> <target xsi:type="file" name="File" fileName="App_Data/Logs/${shortdate}.log" layout="${longdate}||${level}||${logger}||${message}||${exception:format=ToString:innerFormat=ToString:maxInnerExceptionLevel=10:separator=\r\n} || ${aspnet-traceidentifier} ||end" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="file" /> </rules> </nlog>
至此閘道器配置完成,接下來開始配置服務發現
三、服務註冊、發現
下載consul,使用以下命令啟動服務(這裡不講解叢集搭建方式):
consul agent -server -ui -bootstrap-expect=1 -data-dir=/tmp/consul -node=consul-1 -client=0.0.0.0 -bind=192.168.1.37 -datacenter=dc1
瀏覽器開啟UI介面如下:
新建WebApi專案,同時安裝Consul包到你的專案
新增服務發現擴充套件類ServiceDiscoveryExtensions.cs
public static class ServiceDiscoveryExtensions { public static void AddConsul(this IServiceCollection serviceCollection) { IConfiguration configuration; using (var serviceProvider = serviceCollection.BuildServiceProvider()) { configuration = serviceProvider.GetService<IConfiguration>(); } ConsulOptions option = configuration.GetSection("Consul").Get<ConsulOptions>(); if (option.Enabled) { serviceCollection.AddSingleton<IConsulClient>(c => new ConsulClient(cfg => { //Consul主機地址 if (!string.IsNullOrEmpty(option.Host)) { cfg.Address = new Uri(option.Host); } })); } } public static void UseConsul(this IApplicationBuilder app, IApplicationLifetime lifetime) { using (var scope = app.ApplicationServices.CreateScope()) { var configuration = scope.ServiceProvider.GetService<IConfiguration>(); ConsulOptions option = configuration.GetSection("Consul").Get<ConsulOptions>(); if (option.Enabled) { Guid serviceId = Guid.NewGuid(); string consulServiceID = $"{ option.App.Name }:{serviceId}"; var client = scope.ServiceProvider.GetService<IConsulClient>(); //健康檢查 var httpCheck = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久後註冊 Interval = TimeSpan.FromSeconds(10),//間隔固定的時間訪問一次 HTTP = $"{ option.App.Scheme }://{ option.App.Host }:{ option.App.Port }/api/Health/Check",//健康檢查地址 Timeout = TimeSpan.FromSeconds(5) }; var consulServiceRistration = new AgentServiceRegistration { ID = consulServiceID, Name = option.App.Name, Address = $"{ option.App.Scheme }://{ option.App.Host }", Port = option.App.Port, Tags = option.App.Tags, Checks = new[] { httpCheck } }; client.Agent.ServiceRegister(consulServiceRistration); lifetime.ApplicationStopping.Register(() => { client.Agent.ServiceDeregister(consulServiceRistration.ID).Wait(); }); } } } }
配置實體ConsulOptions.cs:
/// <summary> /// Consul配置專案 /// </summary> public class ConsulOptions { /// <summary> /// 是否啟用 /// </summary> public bool Enabled { get; set; } /// <summary> /// Consul主機地址 /// </summary> public string Host { get; set; } /// <summary> /// 應用資訊 /// </summary> public AppInfo App { get; set; } } public class AppInfo { /// <summary> /// 應用名稱 /// </summary> public string Name { get; set; } /// <summary> /// 協議 /// </summary> public string Scheme { get; set; } /// <summary> /// 應用主機地址 /// </summary> public string Host { get; set; } /// <summary> /// 應用監聽埠 /// </summary> public int Port { get; set; } /// <summary> /// 標籤 /// </summary> public string[] Tags { get; set; } }
分別在ConfigureServices和Configure新增如下程式碼:
services.AddConsul();
app.UseConsul(lifetime);
appsetting.json新增如下配置專案:
"Consul": { //是否啟用 "Enabled": true, //Consul主機地址 "Host": "http://192.168.1.37:8500", "App": { //應用名稱 "Name": "web-api", //協議 "Scheme": "http", //應用主機地址 "Host": "localhost", //應用監聽埠 "Port": 10002, //標籤 "Tags": [ "web-api-node-1" ] } }
至此程式碼部分就完成了,接下來分別釋出你的閘道器、API服務到伺服器,訪問你的閘道器地址: