前言:上一篇中实现了consul的服务注册与发现,现在可以把网关系统进行一次升级改造,实现简单的服务发现与治理以及身份认证
一,Consul服务发现的引用与配置
1,使用NuGet安装Ocelot.Provider.Consul:
2,使用Startup.cs进行注册:
public void ConfigureServices(IServiceCollection services) { //添加ocelot服务 services.AddOcelot() //服务发现 .AddConsul(); }
3,修改ocelot.json配置文件:
{ "Routes": [ { /*下游地址*/ "DownstreamPathTemplate": "/product/{xx}", /*请求方式*/ "DownstreamScheme": "http", /*服务名称*/ "ServiceName": "productsevrice", /*下游服务的主机和端口*/ //"DownstreamHostAndPorts": [ // { // "Host": "localhost", // "Port": 4001 // }, // { // "Host": "localhost", // "Port": 4002 // } //], /*上游路径地址*/ "UpstreamPathTemplate": "/api/product/{xx}", "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], /*请求方式*/ /*负载均衡:RoundRobin - 循环访问, NoLoadBalancer - 从配置或服务发现中获取第一个可用服务, "LeastConnection" 请求最少的服务发送新请求, CookieStickySessions - 使用 cookie 将所有请求粘贴到特定服务器 "*/ "LoadBalancerOptions": { "Type": "LeastConnection" /*请求最少的服务发送新请求*/ } } ], "GlobalConfiguration": { /*ocelot网关将要运行的地址*/ "BaseUrl": "http://localhost:4000", "ServiceDiscoveryProvider": { "Scheme": "http", "Host": "localhost", "Port": 8500, "Type": "Consul" } } }
使用consul服务发现相关配置,在全局GlobalConfiguration增加ServiceDiscoveryProvider, 移除DownstreamHostAndPorts节点。
二,Polly服务治理的引用与配置
服务治理是为了提高服务质量以及可用性,缓存,限流,熔断,负载均衡等等都算,实际使用中按需实现即可
1,超时/熔断:
超时即网关请求服务时的最长响应时间,熔断某个服务的请求异常次数达到一定量时不对其继续请求
(1)添加Ocelot.Provider.Polly
(2)修改Startup配置
public void ConfigureServices(IServiceCollection services) { //添加ocelot服务 services.AddOcelot() //服务发现 .AddConsul() //添加Polly(防止熔断) .AddPolly(); }
(3)修改Ocelot.json
/*熔断/超时 超时就是网关请求服务时可容忍的最长响应时间。 熔断的意思就是当请求某个服务的异常次数达到一定量时, 那么网关在一定时间内就不再对这个服务发起请求了,直接熔断*/ "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, //发生错误的次数 "DurationOfBreak": 10000, //熔断时间 "TimeoutValue": 5000 //请求超过 5 秒,它将自动超时。 }
2,缓存/限流
(1) 添加 Ocelot.Cache.CacheManager
(2)修改配置:
public void ConfigureServices(IServiceCollection services) { //添加ocelot服务 services.AddOcelot() //服务发现 .AddConsul() //添加Polly(防止熔断) .AddPolly() //添加缓存,限流 .AddCacheManager(x => { x.WithDictionaryHandle(); }); }
Ocelot.json配置修改:
/*缓存*/ "FileCacheOptions": { "TtlSeconds": 5, //过期时间 "Region": "productcache" }, /*限流*/ "RateLimitOptions": { "ClientWhitelist": [ "SuperClient" ], //白名单中的客户端可以不受限流的影响 "EnableRateLimiting": true, //是否限流 "Period": "5s", //限流的单位时间 "PeriodTimespan": 2, //请求上限多少秒后可以重试 "Limit": 1 //定义的时间内可以发出的最大请求数 }
最后修改完成之后的Ocelot.json:
{ "Routes": [ { /*下游地址*/ "DownstreamPathTemplate": "/product/{xx}", /*请求方式*/ "DownstreamScheme": "http", /*服务名称*/ "ServiceName": "productsevrice", /*下游服务的主机和端口*/ //"DownstreamHostAndPorts": [ // { // "Host": "localhost", // "Port": 4001 // }, // { // "Host": "localhost", // "Port": 4002 // } //], /*上游路径地址*/ "UpstreamPathTemplate": "/api/product/{xx}", "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ], /*请求方式*/ /*负载均衡:RoundRobin - 循环访问, NoLoadBalancer - 从配置或服务发现中获取第一个可用服务, "LeastConnection" 请求最少的服务发送新请求, CookieStickySessions - 使用 cookie 将所有请求粘贴到特定服务器 "*/ "LoadBalancerOptions": { "Type": "LeastConnection" /*请求最少的服务发送新请求*/ }, /*缓存*/ "FileCacheOptions": { "TtlSeconds": 5, //过期时间 "Region": "productcache" }, /*限流*/ "RateLimitOptions": { "ClientWhitelist": [ "SuperClient" ], //白名单中的客户端可以不受限流的影响 "EnableRateLimiting": true, //是否限流 "Period": "5s", //限流的单位时间 "PeriodTimespan": 2, //请求上限多少秒后可以重试 "Limit": 1 //定义的时间内可以发出的最大请求数 }, /*熔断/超时 超时就是网关请求服务时可容忍的最长响应时间。 熔断的意思就是当请求某个服务的异常次数达到一定量时, 那么网关在一定时间内就不再对这个服务发起请求了,直接熔断*/ "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3, //发生错误的次数 "DurationOfBreak": 10000, //熔断时间 "TimeoutValue": 5000 //请求超过 5 秒,它将自动超时。 } }, { "DownstreamPathTemplate": "/order/{xx}", /*请求方式*/ "DownstreamScheme": "http", /*下游服务的主机和端口*/ "ServiceName": "orderservice", "LoadBalancerOptions": { "Type": "LeastConnection" }, "UpstreamPathTemplate": "/api/order/{xx}", "UpstreamHttpMethod": [ "Get", "Post" ] } ], "GlobalConfiguration": { /*ocelot网关将要运行的地址*/ "BaseUrl": "http://localhost:4000", "ServiceDiscoveryProvider": { "Scheme": "http", "Host": "localhost", "Port": 8500, "Type": "Consul" }, "RateLimitOptions": { "DisableRateLimitHeaders": false, //是否禁用X-Rate-Limit和Retry-After标头 "QuotaExceededMessage": "{\"res_code\":\"999\",\"res_msg\":\"请求中\"}", //请求达到上限时返回 "HttpStatusCode": 999, //HTTP状态代码 "ClientIdHeader": "Test" } } }
三,JWT的引用配置与认证
1 ,第一步老规矩Nugget 获取JWT
2,添加了一个帮助类:
public class JWtHelper {
/*生成token*/ public static string JwtEncrypt(string phone, string uid, string openid, string encrypt_key) { var token = ""; try { var claims = new[] { new Claim("openid", openid), new Claim("phone", phone), new Claim("uid", uid),}; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(encrypt_key)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtToken = new JwtSecurityToken("MER", "API", claims, expires: DateTime.Now.AddYears(1), signingCredentials: credentials); token = new JwtSecurityTokenHandler().WriteToken(jwtToken); } catch (Exception) { throw; } return token; } /*解析token*/ public static string JwtDecrypt(string token, string encrypt_key) { var josn = ""; try { var param = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(encrypt_key)), ValidateIssuer = true, ValidIssuers = new string[] { "ASD123", "MER"}, ValidateAudience = true, ValidAudience = "API", ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5) }; SecurityToken security; var principal = new JwtSecurityTokenHandler().ValidateToken(token, param, out security); if (principal != null) { var exp = principal.Claims.FirstOrDefault(c => c.Type.Equals("exp"))?.Value; var uid = principal.Claims.FirstOrDefault(c => c.Type.Equals("uid"))?.Value; var phone = principal.Claims.FirstOrDefault(c => c.Type.Equals("phone"))?.Value; var openid = principal.Claims.FirstOrDefault(c => c.Type.Equals("openid"))?.Value; josn = JsonConvert.SerializeObject(new { openid, phone, uid, exp }); } } catch (Exception ex) { return null; } return josn; } }
ps:这个帮助类只是为了看看效果,测试类随便编写能不能再生产环境使用自行考虑。
3,添加Jwt中间件
public class JwtSafeMiddleware { private readonly RequestDelegate _next; public IConfiguration _configuration; public JwtSafeMiddleware(RequestDelegate next, IConfiguration configuration) { _next = next; _configuration = configuration; } public async Task Invoke(HttpContext context) { //请求不走jwt校验的一种方式:识别url //if(!context.Request.Path.Value.StartsWith("/auth")) context.Response.ContentType = "application/json"; if (context.Request.Method == "GET" || context.Request.Method == "POST") { string token = context.Request.Headers["token"].FirstOrDefault(); if (string.IsNullOrEmpty(token)) { context.Response.StatusCode = 401; //401未授权 var json = JsonConvert.SerializeObject(new { res_code = 401, res_msg = "token为空!" }); await context.Response.WriteAsync(json); return; } //校验auth的正确性 var result = JWtHelper.JwtDecrypt(token, _configuration["SecretKey"]); if (result == "expired") { context.Response.StatusCode = 666; var json = JsonConvert.SerializeObject(new { res_code = 666, res_msg = "参数已经过期!" }); await context.Response.WriteAsync(json); return; } else { //校验通过 } } await _next.Invoke(context); } }
最后在Startup中的Configure方法注册:app.UseMiddleware<JwtSafeMiddleware>();
下一篇 微服务中的CAP
--------to be continue --------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构