Ocelot是一個用.NET Core實現並且開源的API網關,它功能強大,包括了:路由、請求聚合、服務發現、認證、鑑權、限流熔斷、並內置了負載均衡器與Service Fabric、Butterfly Tracing集成。而且這些功能都只需要簡單的配置即可完成。
官網:GitHub - ThreeMammals/Ocelot: .NET API Gateway
Welcome to Ocelot 23.2 — Ocelot 23.2 documentation
安裝包:Ocelot,命令:Install-Package Ocelot
簡單使用:
1.首先兩個WebApi服務,Service1和Service2用於測試
Service1:
[ApiController] [Route("[controller]/[action]")] public class UserController:ControllerBase { [HttpGet] public List<UserInfo> GetUsers([FromQuery] UserBo bo) { List<UserInfo> result = new() { new(){Id = 1,NickName = "張三"}, new(){Id = 2,NickName = "李四"} }; return result; } [HttpGet] public string GetStr() { return "hello world,gateway,server1"; } }
Service2:
[ApiController] [Route("[controller]/[action]")] public class UserController:ControllerBase { [HttpGet] public List<UserInfo> GetUsers([FromHeader] long userId) { List<UserInfo> result = new() { new(){Id = 3,NickName = "王五"}, new(){Id = 4,NickName = "趙六"} }; return result; } [HttpGet] public string GetStr() { return "hello world,gateway,server2"; } }
2.添加一個空的Web項目Ocelot,添加Ocelot安裝包。
2.1.添加ocelot.json的配置文件。
Program.cs修改
var builder = WebApplication.CreateBuilder(args); // 添加配置文件,optional:文件是否可選,reloadOnChange:如果文件發生更改,是否應重載配置。 builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); // 添加Ocelot 服務 builder.Services.AddOcelot(builder.Configuration);var app = builder.Build(); // 會阻斷當前項目自己的 /controller/action 路由 app.UseOcelot().Wait(); app.Run();
2.2.ocelot.json配置 ( 不推薦這麼使用 )
{ //這裏注意,以前是ReRoutes現在是Routes "Routes": [ { //Upstream表示上游請求,即客戶端請求到API Gateway的請求 "UpstreamPathTemplate": "/s1/{url}", //請求路徑模板 "UpstreamHttpMethod": [ "Get", "Post" ], //請求方法數組 //"UseServiceDiscovery": true, //啓用服務發現 //Downstream表示下游請求,即API Gateway轉發的目標服務地址 "DownstreamPathTemplate": "/{url}", //下游請求地址模板 "DownstreamScheme": "https", //請求協議,目前應該是支持http和https //A***************指定單個轉發地址 "DownstreamHostAndPorts": [ //請求服務地址,可以有多個 { "Host": "localhost", "Port": 5146 } ] }, { "UpstreamPathTemplate": "/s2/{url}", "UpstreamHttpMethod": [ "Get", "Post" ], "DownstreamPathTemplate": "/{url}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5211 } ] } ], "GlobalConfiguration": { "BaseUrl": "http://localhost:5063" //網關地址 } }
啓動服務:啓動兩個服務和網關
測試:http://localhost:5063/s1/user/getusers 和 http://localhost:5063/s2/user/getusers
--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------
模板介紹:
{ "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/", "UpstreamHttpMethod": [ "Get", "Post" ], "AddHeadersToRequest": {}, "AddClaimsToRequest": {}, "RouteClaimsRequirement": {}, "AddQueriesToRequest": {}, "RequestIdKey": "", "FileCacheOptions": { "TtlSeconds": 0, "Region": "" }, "ReRouteIsCaseSensitive": false, "ServiceName": "", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 51876 } ], "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 0, "DurationOfBreak": 0, "TimeoutValue": 0 }, "LoadBalancer": "", "RateLimitOptions": { "ClientWhitelist": [], "EnableRateLimiting": false, "Period": "", "PeriodTimespan": 0, "Limit": 0 }, "AuthenticationOptions": { "AuthenticationProviderKey": "", "AllowedScopes": [] }, "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, "UseTracing": true }, "UseServiceDiscovery": false }
Downstream是下游服務配置
UpStream是上游服務配置
Aggregates 服務聚合配置
ServiceName, LoadBalancer, UseServiceDiscovery 配置服務發現
AuthenticationOptions 配置服務認證
RouteClaimsRequirement 配置Claims鑑權
RateLimitOptions爲限流配置
FileCacheOptions 緩存配置
QosOptions 服務質量與熔斷
DownstreamHeaderTransform頭信息轉發
DownstreamPathTemplate:下游模板
DownstreamScheme:下游服務http schema
DownstreamHostAndPorts:下游服務的地址,如果使用LoadBalancer的話這裏可以填多項
UpstreamPathTemplate: 上游也就是用戶輸入的請求Url模板
UpstreamHttpMethod: 上游請求http方法,可使用數組
--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------
負載均衡
當下遊服務有多個結點的時候,我們可以在DownstreamHostAndPorts中進行配置。
{ "Routes": [ { "UpstreamPathTemplate": "/s/{url}", "UpstreamHttpMethod": [ "Get","Post" ], "LoadBalancerOptions": { "Type": "RoundRobin" }, "DownstreamPathTemplate": "/{url}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5146 }, { "Host": "localhost", "Port": 5211 } ] } ], "GlobalConfiguration": { "BaseUrl": "http://localhost:5063" } }
LoadBalancerOptions:
LeastConnection – 將請求發往最空閒的那個服務器
RoundRobin – 輪流發送
NoLoadBalance – 總是發往第一個請求或者是服務發現
測試:http://localhost:5063/s/user/getusers
--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------
限流
對請求進行限流可以防止下游服務器因爲訪問過載而崩潰。非常優雅的實現,我們只需要在路由下加一
些簡單的配置即可以完成。
{ "Routes": [ { "UpstreamPathTemplate": "/s/{url}", "UpstreamHttpMethod": [ "Get", "Post" ], "LoadBalancerOptions": { "Type": "RoundRobin" }, // 限流配置 "RateLimitOptions": { "ClientWhitelist": [], //白名單,不會被限流,爲空表示訪問的都被限流 "EnableRateLimiting": true, //是否被開啓 "Period": "1s", //1秒鐘超過了Limit定義的會拋異常 "PeriodTimespan": 1, //超過一秒後纔可以重新請求 "Limit": 1 }, "DownstreamPathTemplate": "/{url}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5146 }, { "Host": "localhost", "Port": 5211 } ] } ], "GlobalConfiguration": { "BaseUrl": "http://localhost:5063", "RateLimitOptions": { "DisableRateLimitHeaders": false, "QuotaExceededMessage": "你被限流了..........", "HttpStatusCode": 999, //發生異常返回轉態碼 "ClientIdHeader": "ClientId" } } }
DisableRateLimitHeaders Http頭 X-Rate-Limit 和 Retry-After 是否禁用
QuotaExceedMessage 當請求過載被截斷時返回的消息
HttpStatusCode 當請求過載被截斷時返回的http status , 默認爲429(too many requests)
ClientIdHeader 用來識別客戶端的請求頭,默認是 ClientId
--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------
認證
添加一個WebApi項目(AuthenticationCente),用於獲取Token,不知道怎麼用JWT 的可以去查資料或 02_Web Api使用Jwt - 野碼 - 博客園 (cnblogs.com)
網關項目(Ocelot)中的Program.cs調整
var builder = WebApplication.CreateBuilder(args); // 添加配置文件 builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); #region 認證 //身份認證--如何鑑權 builder.Services.AddAuthentication(options => { /options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { //取出私鑰 var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretKey"]); options.TokenValidationParameters = new TokenValidationParameters() { //驗證發佈者 ValidateIssuer = true, ValidIssuer = builder.Configuration["JWT:Issuer"], //驗證接收者 ValidateAudience = true, ValidAudience = builder.Configuration["JWT:Audience"], //ValidateIssuerSigningKey= true,//是否驗證SigningKey //驗證是否過期 ValidateLifetime = true, //驗證私鑰 IssuerSigningKey = new SymmetricSecurityKey(secretByte) }; }); #endregion // 添加Ocelot 服務 builder.Services.AddOcelot(builder.Configuration); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // 會阻斷當前項目自己的 /controller/action 路由 app.UseOcelot().Wait(); app.Run();
修改ocelot.json配置
{ "Routes": [ { "UpstreamPathTemplate": "/s/{url}", "UpstreamHttpMethod": [ "Get", "Post" ], "LoadBalancerOptions": { "Type": "RoundRobin" }, // 限流配置 "RateLimitOptions": { "ClientWhitelist": [], //白名單,不會被限流,爲空表示訪問的都被限流 "EnableRateLimiting": true, //是否被開啓 "Period": "1s", //1秒鐘超過了Limit定義的會拋異常 "PeriodTimespan": 1, //超過一秒後纔可以重新請求 "Limit": 1 }, "AuthenticationOptions": { //認證 "AuthenticationProviderKey": "Bearer", "AllowedScopes": [] }, "DownstreamPathTemplate": "/{url}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5146 }, { "Host": "localhost", "Port": 5211 } ] } ], "GlobalConfiguration": { "BaseUrl": "http://localhost:5063", "RateLimitOptions": { "DisableRateLimitHeaders": false, "QuotaExceededMessage": "你被限流了..........", "HttpStatusCode": 999, //發生異常返回轉態碼 "ClientIdHeader": "ClientId" } } }
然後service1和Service2方法上添加[Authorize]
測試:http://localhost:5063/s/user/getusers 請求頭帶上Token
--------------------------------------------------------漂亮的分割線-----------------------------------------------------------------------------
子服務獲取參數
外部服務調用子服務,走網關。
參數對象
public class UserInfo { [FromHeader] public int UserId{ get; set; } public string NickName { get; set; } }
子服務要接收userId
[HttpGet] public List<UserInfo> GetUsers([FromHeader] long userInfo) { List<UserInfo> result = new() { new(){UserId = 1,NickName = "張三"}, new(){UserId = 2,NickName = "李四"} }; return result; }
上游請求中添加報頭,請在ocelot.json中的路由 GlobalConfiguration 配置中中添加以下內容:
{ "Routes": [ { "UpstreamPathTemplate": "/s/{url}", "UpstreamHttpMethod": [ "Get", "Post" ], "LoadBalancerOptions": { "Type": "RoundRobin" }, // 限流配置 "RateLimitOptions": { "ClientWhitelist": [], //白名單,不會被限流,爲空表示訪問的都被限流 "EnableRateLimiting": true, //是否被開啓 "Period": "1s", //1秒鐘超過了Limit定義的會拋異常 "PeriodTimespan": 1, //超過一秒後纔可以重新請求 "Limit": 1 }, "AuthenticationOptions": { //認證 "AuthenticationProviderKey": "Bearer", "AllowedScopes": [] }, "DownstreamPathTemplate": "/{url}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5146 }, { "Host": "localhost", "Port": 5211 } ] } ], //全局 "GlobalConfiguration": { "BaseUrl": "http://localhost:5063", "RateLimitOptions": { "DisableRateLimitHeaders": false, "QuotaExceededMessage": "你被限流了..........", "HttpStatusCode": 999, //發生異常返回轉態碼 "ClientIdHeader": "ClientId" }, "UpstreamHeaderTransform": { "UserId": "{UserId}" } } }
測試:http://localhost:5063/s/user/getusers
Params:NickName--->1234
Headers:UserId--->2233
Auth:你的Token