Ocelot是一个用.NET Core实现并且开源的API网关,功能包括:路由,请求聚合,服务验证,鉴权,限流熔断,并内置了负载均衡器与Service Fabric,Buttefly Tracing集成。这些功能都只需要简单配置就可以完成。
简单的ocelot是一堆asp.net core middleware组成的一个管道,当它拿到请求之后会用一个request builder来构造一个HttpRequestMessage发到下游的真实服务器,等下游的服务返回response之后再由一个middleware将它返回的HttpResponseMessage映射到HttpResponse上.
网关集群
只有一个网关是很危险的,也就是我们通常所将的单点,只要它挂了,所有的服务全挂。这显然无法达到高可用,所以我们也可以部署多台网关。当然这个时候再多台网关前,你还需要一台负载均衡器。
Consul 服务发现
在Ocelot已经支持简单的负载均衡功能,也就是当下游服务存在多个结点的时候,Ocelot能够承担起负载均衡的作用。但是它不提供健康检查,服务的注册也只能通过手动子配置文件里面添加完成。这个时候我们就可以用Consul来做服务发现,它能与Ocelot完美结合
下面实战
ocelot配置信息, 这里ocelot.json是系统生成的,code中实现了多配置文件合并功能
1、ocelot.consul.json
{ "Routes": [ { "DownstreamPathTemplate": "/api/{url}", "DownstreamScheme": "https", "DownstreamHttpMethod": "Get", "UpstreamHttpMethod": [ "Options", "Get", "Post", "Put", "Delete" ], "UpstreamPathTemplate": "/Ocelot/{url}", //"UpstreamHost": "localhost",//404 Not found "UseServiceDiscovery": true, "ServiceName": "serviceA", /* LeastConnection RoundRobin NoLoadBalance */ "LoadBalancerOptions": { "Type": "CustomRandomLoadBalancer" }, "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10000, //ms "TimeoutValue": 10000 //ms,default 90s }, "RateLimitOptions": { "ClientWhiteList": [ "NoLimitedAPI" ], "EnableRateLimiting": true, "Period": "10s", //1s, 5m, 1h, 1d "PeriodTimespan": 10, //retry after n second/seconds "Limit": 3 }, "authenticationoptions": { "authenticationproviderkey": "authenticationkey", "allowedscopes": [] }, //"routeclaimsrequirement": { // "userrole": "admin" //}, //"DownstreamHttpVersion": "", //"AddHeadersToRequest": {}, //"AddClaimsToRequest": {}, //"AddQueriesToRequest": {}, //"RequestIdKey": "", //"FileCacheOptions": { // "TtlSeconds": 10, // "Region": "gatewaycacheregion" //}, //"HttpHandlerOptions": { // "AllowAutoRedirect": true, // "UseCookieContainer": false, // "UseTracing": true, // "MaxConnectionsPerServer": 100 //}, //"DangerousAcceptAnyServerCertificateValidator": false, "Priority": 10 //priority } ] }ocelot.consul
1 { 2 "GlobalConfiguration": { 3 "BaseUrl": "https://localhost:7000", //gateway public address 4 "RequestIdKey": "OcRequestId", 5 "RouteIsCaseSensitive": false, 6 "ServiceDiscoveryProvider": { 7 "Host": "127.0.0.1", 8 "Port": 8500, 9 "Type": "Consul", 10 "ConfigurationKey": "ApiGateway.Ocelot" 11 }, 12 "RateLimitOptions": { 13 "ClientIdHeader": "IgnoreRateLimit", //request header key 14 "QuotaExceededMessage": "The requests has reached the quota.", //The customized prompt 15 "RateLimitCounterPrefix": "ocelotratelimit", 16 "DisableRateLimitHeaders": false, 17 "HttpStatusCode": 666 18 } 19 } 20 }ocelot.global mutiple configuration merge
2、Add ocelot
1 builder.Services.AddOcelot() 2 .AddConsul() 3 .AddPolly(); 4 5 app.UseOcelot().Wait();ocelot
3、添加自定义load balancer
1 builder.Services.AddOcelot() 2 .AddCustomLoadBalancer((serviceProvider, route, serviceDiscoveryProvider) => new CustomRandomLoadBalancer(serviceDiscoveryProvider.Get)) 3 .AddConsul() 4 .AddPolly();custom load balancer
1 public class CustomRandomLoadBalancer : ILoadBalancer 2 { 3 private readonly Func<Task<List<Service>>> _services; 4 private readonly Object _lock = new object(); 5 private int _index; 6 7 public CustomRandomLoadBalancer(Func<Task<List<Service>>> services) 8 { 9 _services = services; 10 } 11 12 public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext) 13 { 14 var services = await _services(); 15 if (services == null) 16 return new ErrorResponse<ServiceHostAndPort>(new ErrorInvokingLoadBalancerCreator(new Exception("Load balance algorithm error."))); 17 lock (_lock) 18 { 19 if (services.Count == 1) 20 return new OkResponse<ServiceHostAndPort>(services[0].HostAndPort); 21 _index = new Random().Next(services.Count); 22 return new OkResponse<ServiceHostAndPort>(services[_index].HostAndPort); 23 } 24 } 25 26 public void Release(ServiceHostAndPort hostAndPort) 27 { 28 } 29 }load balancer
4、添加中间件,设置特定header,避免限流
1 app.Use((httpContext, requestDelegate) => 2 { 3 if (!httpContext.Request.Headers.TryGetValue("IgnoreRateLimit", out Microsoft.Extensions.Primitives.StringValues value)) 4 httpContext.Request.Headers.Add("IgnoreRateLimit", "NoLimitedAPI"); 5 return requestDelegate.Invoke(httpContext); 6 });middleware
添加中间件另种方式
app.UseMiddleware<CustomMiddleware>(3);middleware
1 public class CustomMiddleware 2 { 3 private readonly RequestDelegate _requestDelegate; 4 private Int32 _parameter { get; set; } 5 public CustomMiddleware(RequestDelegate requestDelegate, Int32 parameter) 6 { 7 this._requestDelegate = requestDelegate; 8 this._parameter = parameter; 9 } 10 11 public async Task InvokeAsync(HttpContext httpContext) 12 { 13 var argument = _parameter; 14 await _requestDelegate(httpContext); 15 } 16 }custom middleware
5、集成identity server4
identity server4 config 配置信息
1 public class Config 2 { 3 4 public static IEnumerable<ApiScope> ApiScopes 5 => new List<ApiScope> 6 { 7 new ApiScope("apiscope1", "scope1"), 8 new ApiScope("apiscope2","scope2") 9 }; 10 11 public static IEnumerable<ApiResource> ApiResources 12 => new List<ApiResource> 13 { 14 new ApiResource("service1","this is service1") 15 { 16 Scopes= { "apiscope1" } 17 }, 18 new ApiResource("service2","this is service2") 19 { 20 Scopes ={ "apiscope2" } 21 } 22 }; 23 24 public static IEnumerable<Client> Clients 25 => new List<Client> 26 { 27 new Client 28 { 29 ClientId = "clientId", 30 ClientSecrets = new [] { new Secret("auth123456".Sha256()) }, 31 AllowedGrantTypes = { GrantType.ClientCredentials }, 32 AllowedScopes = new [] 33 { 34 "apiscope1", 35 "apiscope2", 36 IdentityServerConstants.StandardScopes.OpenId, 37 IdentityServerConstants.StandardScopes.Address, 38 IdentityServerConstants.StandardScopes.Email, 39 IdentityServerConstants.StandardScopes.Phone, 40 IdentityServerConstants.StandardScopes.Profile 41 }, 42 Claims = new List<ClientClaim> 43 { 44 new ClientClaim(IdentityModel.JwtClaimTypes.Role,"admin"), 45 new ClientClaim(IdentityModel.JwtClaimTypes.NickName,"tom"), 46 new ClientClaim("Emai","[email protected]") 47 } 48 } 49 }; 50 51 public static IEnumerable<IdentityResource> IdentityResources 52 => new List<IdentityResource> 53 { 54 new IdentityResources.OpenId(), 55 new IdentityResources.Profile(), 56 new IdentityResources.Address(), 57 new IdentityResources.Email(), 58 new IdentityResources.Phone() 59 }; 60 }ids4 configuration
1 builder.Services.AddIdentityServer() 2 .AddDeveloperSigningCredential() 3 .AddInMemoryIdentityResources(Config.IdentityResources) 4 .AddInMemoryApiResources(Config.ApiResources) 5 .AddInMemoryApiScopes(Config.ApiScopes) 6 .AddInMemoryClients(Config.Clients); 7 8 app.UseIdentityServer();Ids4 configure service
启动Ids4 dotnet run
6、ocelot中配置支持Ids4 authentication
1 var authenticationProviderKey = "authenticationkey"; 2 builder.Services 3 .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 4 .AddIdentityServerAuthentication(authenticationProviderKey, options => 5 { 6 options.Authority = "https://localhost:9000"; 7 options.RequireHttpsMetadata = false; 8 options.ApiName = "service1"; 9 options.SupportedTokens = SupportedTokens.Both; 10 options.JwtBearerEvents = new JwtBearerEvents 11 { 12 OnChallenge =ctx => 13 { 14 return Task.CompletedTask; 15 }, 16 OnMessageReceived = ctx => 17 { 18 var token = ctx.Token; 19 return Task.CompletedTask; 20 }, 21 OnTokenValidated = ctx => 22 { 23 var securityToken = ctx.SecurityToken as JwtSecurityToken; 24 var claimIdentities = ctx.Principal?.Identities; 25 if (claimIdentities?.Any(i => i.Claims.Any(c=>c.Value.Equals("admin", StringComparison.OrdinalIgnoreCase))) ?? false) 26 ctx.Success(); 27 else 28 ctx.Fail("token validate failed."); 29 return Task.CompletedTask; 30 }, 31 OnAuthenticationFailed = context => 32 { 33 context.Fail(context.Exception); 34 return Task.CompletedTask; 35 }, 36 OnForbidden = context => 37 { 38 context.Fail("403 forbidden"); 39 return Task.CompletedTask; 40 } 41 }; 42 });Ocelot Ids4
app.UseAuthentication();use authentication
7、为了避免恶意攻击避开ocelot直接访问service,在netcore api项目中同样需要添加验证
1 builder.Services.AddAuthentication(options => 2 { 3 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 4 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 5 }).AddIdentityServerAuthentication(options => 6 { 7 options.Authority = "https://localhost:9000"; 8 options.ApiName = "service1"; 9 options.RequireHttpsMetadata = false; 10 options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Both; 11 options.Events = new JwtBearerEvents 12 { 13 OnTokenValidated = context => 14 { 15 var token = context.SecurityToken as JwtSecurityToken; 16 var identity = context.Principal?.Identity as ClaimsIdentity; 17 if (!string.IsNullOrEmpty(token?.Issuer)) 18 context.Success(); 19 else 20 context.Fail($"Token invalid"); 21 return Task.CompletedTask; 22 }, 23 OnAuthenticationFailed = context => 24 { 25 context.Fail(context.Exception); 26 return Task.CompletedTask; 27 }, 28 OnForbidden = context => 29 { 30 context.Fail("403 forbidden"); 31 return Task.CompletedTask; 32 }, 33 OnChallenge = context => 34 { 35 var error = context.Error; 36 return Task.CompletedTask; 37 }, 38 OnMessageReceived = context => 39 { 40 var token = context.Token; 41 return Task.CompletedTask; 42 } 43 }; 44 });NetCore API authentication
配置详解
负载均衡
/* LeastConnection RoundRobin NoLoadBalance */ "LoadBalancerOptions": { "Type": "CustomRandomLoadBalancer" }
熔断,超时
"QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10000, //ms "TimeoutValue": 10000 //ms,default 90s }
限流
"RateLimitOptions": { "ClientWhiteList": [ "NoLimitedAPI" ], "EnableRateLimiting": true, "Period": "10s", //1s, 5m, 1h, 1d "PeriodTimespan": 10, //retry after n second/seconds "Limit": 3 }
认证
"authenticationoptions": { "authenticationproviderkey": "authenticationkey", "allowedscopes": [] }
Consul服务发现
"ServiceDiscoveryProvider": { "Host": "127.0.0.1", "Port": 8500, "Type": "Consul", "ConfigurationKey": "ApiGateway.Ocelot" }
Ocelot缓存
"FileCacheOptions": { "TtlSeconds": 10, "Region": "gatewaycacheregion" }cache
最终效果,通过访问ocelot 定义url 负载到具体的服务实例
token
ocelot api
OK, 时间紧迫,有时间详搞。
标签:ocelot,return,Ocelot,NetCore,public,context,new,options From: https://www.cnblogs.com/qindy/p/17101176.html