05_NET中使用Ocelot网关(负载均衡、限流、认证)
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