本文共 7460 字,大约阅读时间需要 24 分钟。
Ocelot,客户端的调用变得简单的,但是对于网关层来说,每新增一个接口,或者服务实例,我都需要把网关在重新更新发布,这样无疑增加了网关层的负担,如果说我能自动识别服务实例,并且不需要把每个接口都写一遍,那我的网关岂不是只需要更新一次就好,不管你服务实例怎么增加,接口怎么增加,我都能自动识别。 Consul就可以自动做到服务的注册与发现,那怎么注册发现呢,其实Consul是一个单独的进程,启动Consul之后,在服务里向Consul去注册,意思就是告诉Consul,我是用户服务实例1,我的地址是 localhost:5050,我启动了,你把我加入到你的名单中,然后Consul就把用户服务实例1加入名单中了。接着用户实例2,用户实例3都加入到Consul的名单中了。
这时候,网关就不直接去调用服务实例了,网关还不知道有哪些服务实例呢,它需要先去问Consul,我想要用户服务实例,你把你所有的用户服务实例地址都给我,然后Consul把 localhost:5051,localhost:5052,localhost:5053,等地址都给了网关,然后网关在按照一定的策略,比如轮询,随机等算法去获取对应的服务实例地址,然后再去请求。

Consul做的事情很简单,就是管理各个服务实例的地址,当网关或者谁要地址了,就通过名字把当前名字下服务实例地址全部返回。 Consul除了可以做服务的发现,还可以做服务的移除,关键字是健康检查,或者说心跳检查,当服务实例注册到Consul时,会同时提供一个健康检查地址,Consul就可以按照用户设定的时间,比如说每5秒向健康检查地址发送一个请求,若你能正常响应了,我就认为你还活着,若没响应,或者没按规定时间内响应,Consul会认为服务已经挂掉了,就把这个服务实例从当前的服务名称下进行移除,下次网关再要地址了,Consul不会把已经挂掉的地址在返回了。
Consul的官网地址地址: https://www.consul.io
下载好之后就是一个Consul.exe文件,这是Windows环境下的,其他环境请自行查找资料。
然后把当前Consul.exe上一级文件夹地址加入到环境变量中,这样不用每次都进入到这个文件夹去启动了。
启动Consul,打开Terminal,输入 consul.exe agent -dev
若能出现Consul agent running,说明Consul 启动成功了。
这时可以打开浏览器,端口号默认8500,若能出现网页,说明Consul 正常了。
Consul已经准备好了,那接下来就应该把服务注册到Consul中了,由于多个服务都需要注册,所以单独把注册到Consul的代码封装为一个类库,其他项目去引用即可。新建类库,ConsulBuilder,然后添加两个文件。
ConsulRegistrationExtensions.cs
using System;using Microsoft.AspNetCore.Builder;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using Microsoft.Extensions.Options;using Consul;using ConsulBuilder;namespace ConsulBuilder{ public static class ConsulRegistrationExtensions { public static IApplicationBuilder AddConsul(this IApplicationBuilder app, ConsulServiceOptions consulServiceOptions) { var consulClient = new ConsulClient(x => { // consul服务地址,默认安装consul后端口为8500 x.Address = new Uri(consulServiceOptions.ConsulAddress); }); var registration = new AgentServiceRegistration() { ID = Guid.NewGuid().ToString(), Name = consulServiceOptions.ServiceName,// 服务名 Address = consulServiceOptions.ServiceIP, // 服务绑定IP(也就是你这个项目运行的ip地址) Port = consulServiceOptions.ServicePort, // 服务绑定端口(也就是你这个项目运行的端口) Check = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册 Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔 HTTP = consulServiceOptions.ServiceHealthCheck,//健康检查地址 Timeout = TimeSpan.FromSeconds(5) } }; // 服务注册 consulClient.Agent.ServiceRegister(registration).Wait(); return app; } }} ConsulServiceOptions.cs
namespace ConsulBuilder{ // Consul配置模型类 public class ConsulServiceOptions { /// /// 服务名称 /// public string ServiceName { get; set; } /// /// 服务IP /// public string ServiceIP { get; set; } /// /// 服务端口 /// public int ServicePort { get; set; } /// /// 服务健康检查地址 /// public string ServiceHealthCheck { get; set; } /// /// Consul 地址 /// public string ConsulAddress { get; set; } }} 接下来,我们以用户服务举例,首先引用ConsulBuilder类库,接着修改Startup.cs文件的Configure方法。
//获取关于Consul的配置节点 var consulSection = Configuration.GetSection("Consul"); var consulOption = new ConsulServiceOptions { ServiceName = consulSection["ServiceName"], ServiceIP = consulSection["ServiceIP"], ServicePort = Convert.ToInt32(consulSection["ServicePort"]), ServiceHealthCheck = consulSection["ServiceHealthCheck"], ConsulAddress = consulSection["ConsulAddress"] }; //注册consul服务 app.AddConsul(consulOption); 修改appsetting.json
//Consul的配置 "Consul": { "ServiceName": "UserService", "ServiceIP": "localhost", "ServicePort": 5050, "ServiceHealthCheck": "https://localhost:5050/api/HealthCheck", "ConsulAddress": "http://127.0.0.1:8500" }, OK ,这样我们的服务在启动时就会自动注册到Consul了.
至于HealthCheck,代码如下,其实是直接返回了Ok(),即可。
using Microsoft.AspNetCore.Mvc;using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace EasyShop.UserService.Controllers{ [Route("api/[controller]")] [ApiController] public class HealthCheckController:ControllerBase { /// /// 健康检查地址 /// /// public IActionResult HealthCheck() { return Ok(); } }} 其他服务的配置过程一样,不在赘述,
网关
若服务实例可以正常被Consul发现,接下来就需要修改网关下的Ocelot.json文件,由Consul提供服务实例地址,而不是直接在json文件中配置。
添加nuget包。
Ocelot.json 修改后如下。
{ // 转发路由,数组中的每个元素都是某个服务的一组路由转发规则 "Routes": [ { "ServiceName": "UserService", //对应consul配置的ServiceName // Uri方案,http、https "DownstreamScheme": "https", // 下游(服务提供方)服务路由模板 "DownstreamPathTemplate": "/api/user/{path}", // 上游(客户端,服务消费方)请求路由模板 "UpstreamPathTemplate": "/user/{path}", "UpstreamHttpMethod": [ "Get", "Post" ], "LoadBalancerOptions": { "Type": "RoundRobin" //轮询 }, "UseServiceDiscovery": true, "RateLimitOptions": { "ClientWhitelist": [ "admin" ], // 白名单 "EnableRateLimiting": true, // 是否启用限流 "Period": "1s", // 统计时间段:1s, 5m, 1h, 1d "PeriodTimespan": 15, // 多少秒之后客户端可以重试 "Limit": 2 // 在统计时间段内允许的最大请求数量 }, "QoSOptions": { "ExceptionsAllowedBeforeBreaking": 2, // 允许多少个异常请求 "DurationOfBreak": 15000, // 熔断的时间,单位为毫秒 "TimeoutValue": 5000 // 如果下游请求的处理时间超过多少则视如该请求超时 }, "FileCacheOptions": { // cache response data - ttl: 10s 10秒内相同url请求直接返回缓存数据 "TtlSeconds": 10, "Region": "" } //,身份认证 //"AuthenticationOptions": { // "AuthenticationProviderKey": "ApiGatewayKey", // "AllowedScopes": [ "UserService" ] //} } ], "GlobalConfiguration": { "BaseUrl": "https://localhost:44335", "ReRouteIsCaseSensitive": false, "ServiceDiscoveryProvider": { "Host": "localhost", "Port": 8500, "Type": "Consul" //由Consul提供服务发现 }, "RateLimitOptions": { "DisableRateLimitHeaders": false, // Http头 X-Rate-Limit 和 Retry-After 是否禁用 "QuotaExceededMessage": "Too many requests, are you OK?", // 当请求过载被截断时返回的消息 "HttpStatusCode": 999, // 当请求过载被截断时返回的http status "ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId } }} Routes是一个数组,可以配置多个服务实例。在配置里我们可以看到有这么一个配置。"UseServiceDiscovery": true ,这就表示采用服务发现的地址。而在GlobalConfiguration里,配置了Consul的地址。
Startup.cs 修改如下
public void ConfigureServices(IServiceCollection services) { //允许跨域请求 services.AddCors(options => { //this defines a CORS policy called "default" options.AddPolicy("default", policy => { policy.WithOrigins("http://localhost:8088") .AllowAnyHeader() .AllowAnyMethod(); }); }); services.AddOcelot(Configuration).AddConsul().AddPolly(); } 和之前的区别在于,之前只引入了 Ocelot,而这次还引入了Consul和Polly。 Configure配置不变。
项目地址:https://gitee.com/limeng66/easy-shop
转载地址:http://xppcz.baihongyu.com/