赞
踩
目录
2.3 修改Controller,增加HealthCheck方法
上一篇讲到微服务要灵活伸缩,需要一种特殊的机制去实现它,这个机制就是服务注册与发现,但是不肯定不是必须的,如果你的服务实例很少,并且非常稳定,那么就没有必要使用服务注册与发现了,毕竟写代码如此麻烦。
什么叫服务注册与发现呢
服务注册:简单理解就是,有那么一个注册中心,我们每一个服务实例启动时,都去注册中心注册一下,告诉注册中心我的地址,端口等信息,同样的服务实例需要删除时,也去注册中心删除一下,注册中心就服务维护这些服务实例的信息。
服务发现:既然注册中心维护了各个服务实例的信息,那么客户端就可以通过注册中心很容易发现服务的变化了。
有了服务注册跟发现,客户端就不用再去配置各个服务实例的地址,就可以改为从注册中心统一获取,那么注册中心又怎么可以保证每个服务实例的可用状态呢,假如某一个实例挂了,我们肯定不可以让该挂掉的实例让客户端获取到,这个时候,就引入了另外一个概念,叫做,健康检查。
健康检查:每个服务都需要提供一个用于健康检查的接口,该接口不具备任何业务功能,服务注册时把这个接口的地址也告诉注册中心,注册中心会定时调用这个接口来检测服务是否正常,如果不正常,则将它移除,这样就可以保证服务的可用性。
常见的服务注册中心有Consul、ZooKeeper、etcd、Eureka。
Consul官网地址:https://www.consul.io/https://www.consul.io/
Consul提供的主要功能有服务注册与发现,健康检查,K-V存储,多数据中心等等功能。
Consul安装非常简单,在官网,点击download
由于我是在window中测试,所以我选择window版本
Consul安装:下载后,解压即可。解压后只有一个consul.exe可执行文件。
Consul运行命令如下:
cd F:\工具\consul_1.17.1_windows_amd64
.\consul.exe agent -dev
运行该命令后,出现如下提示信息:
我们在浏览器中访问地址,http://localhost:8500/ 以判断我们的Consul服务是否运行成功。
访问结果如下:
这表示我们的consul已经运行成功了。
新建文件夹consul
cd /usr/local
mkdir consul
chmod 777 consul
下载 consul
cd /usr/local/consul
wget https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip
unzip consul_1.3.0_linux_amd64.zip
cp consul /usr/local/bin/
检查consul是否安装成功
consul
consul version
运行consul
consul agent -dev
启动开发模式,这个模式会快速启动一个单节点的Consul。注意,这个模式不能数据持久化,因此,不能用于生产环境
启用命令简介:
-server
:定义agent运行在server模式,每个数据中心的Server建议在3~5个避免失败情况下数据的丢失-client
:定义agent运行在client模式-bootstrap-expect
:在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群-bind
:节点的ip地址一般是0.0.0.0
或云服务内网地址,用于被集群中的其他节点所访问-node
:指定节点在集群中的唯一名称,默认为机器的hostname-config-dir
:配置文件目录,里面所有以.json结尾的文件都会被加载consul agent -dev -client 0.0.0.0 -ui
浏览器输入:http://IP:8500/出现ConsulWeb界面就表示成功了
我们也可以运行如下命令查看consul是否运行
ps -ef | grep consul
如果需要重启consul,杀死consul进程即可
kill 3938
如果无法访问的话,我们查看一下8500的端口是否正常开放
consul安装完成后,我们需要修改我们的服务实例的代码。
我们需要使用Nuget安装一下consul。
这个类库封装了consul的api方法,方便我们直接调用,当然你也可以直接写http请求去调用consul的接口。
我们需要在appsettings中添加Consul配置信息
ConsulSetting.json的内容如下:
- {
- "ConsulSetting": {
- "ServiceName": "OrderService",
- "ServiceIP": "localhost",
- "ServiceHealthCheck": "/healthcheck",
- "ConsulAddress": "http://host.docker.internal:8500", //注意,docker容器内部无法使用localhost访问宿主机器,如果是控制台启动的话就用localhost
- "ServicePort": "8050"
- }
- }
注意,我们在使用 http://host.docker.internal:8500
之前,需要先检测一下当前版本的docker是否支持host.docker.internal,我们需要在DNS查询,输入如下命令
nslookup host.docker.internal
如果您看到类似于以下输出,表示您的 Docker 支持 host.docker.internal:
- Server: 192.168.65.1
- Address 1: 192.168.65.1Name: host.docker.internal
- Address 1: 192.168.X.X # 宿主机的 IP 地址
如果输出显示了宿主机的 IP 地址,则说明 Docker 支持 host.docker.internal 主机名。
如果查询失败或显示其他错误消息,则可能是因为您的 Docker 版本不支持 host.docker.internal。
同时我们还需要注册Consul,我们需要写一个基于IServiceCollection的扩展方法ConsulExtendsion。
ConsulExtendsion的完整代码如下:
- namespace ForumOrderApi
- {
- public static class ConsulExtendsion
- {
- /// <summary>
- /// 服务注册到consul
- /// </summary>
- /// <param name="services"></param>
- /// <returns></returns>
- public static IServiceCollection RegisterConsul(this IServiceCollection services, IConfiguration configuration, IHostApplicationLifetime lifetime)
- {
-
- var consulClient = new ConsulClient(c =>
- {
- //consul地址
- c.Address = new Uri(configuration["ConsulSetting:ConsulAddress"]);
- });
-
- var registration = new AgentServiceRegistration()
- {
- ID = Guid.NewGuid().ToString(),//服务实例唯一标识
- Name = configuration["ConsulSetting:ServiceName"],//服务名
- Address = configuration["ConsulSetting:ServiceIP"], //服务IP
- Port = int.Parse(configuration["ConsulSetting:ServicePort"]),//服务端口 因为要运行多个实例,端口不能在appsettings.json里配置,在docker容器运行时传入
- Check = new AgentServiceCheck()
- {
- DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
- Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔
- HTTP = $"http://{configuration["ConsulSetting:ServiceIP"]}:{configuration["ConsulSetting:ServicePort"]}{configuration["ConsulSetting:ServiceHealthCheck"]}",//健康检查地址
- Timeout = TimeSpan.FromSeconds(5)//超时时间
- }
- };
-
- //服务注册
- consulClient.Agent.ServiceRegister(registration).Wait();
-
- //应用程序终止时,取消注册
- lifetime.ApplicationStopping.Register(() =>
- {
- consulClient.Agent.ServiceDeregister(registration.ID).Wait();
- });
-
- return services;
- }
- }
- }
同时我们在Program中调用该扩展方法,完整代码如下:
- using Consul;
- using ForumOrderApi;
-
- var builder = WebApplication.CreateBuilder(args);
-
- // Add services to the container.
-
-
- builder.Services.AddControllers();
- // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
- builder.Services.AddEndpointsApiExplorer();
- builder.Services.AddSwaggerGen();
-
-
- var app = builder.Build();
-
- //服务注册
- builder.Services.RegisterConsul(builder.Configuration, app.Lifetime);
-
- // Configure the HTTP request pipeline.
- //if (app.Environment.IsDevelopment())
- //{
- app.UseSwagger();
- app.UseSwaggerUI();
- //}
-
- app.UseHttpsRedirection();
-
- app.UseAuthorization();
-
- app.MapControllers();
-
- app.Run();
我们还需要修改OrderController方法,我们增加一个serviceport参数,方便我们更好的观察结果,完整代码如下:
- namespace ForumOrderApi.Controllers
- {
- [ApiController]
- [Route("order")]
- public class OrderController : ControllerBase
- {
- private readonly ILogger<OrderController> _logger;
-
- private readonly IConfiguration _configuration;
-
- public OrderController(ILogger<OrderController> logger, IConfiguration configuration)
- {
- _logger = logger;
- _configuration = configuration;
- }
-
- [HttpGet(Name = "GetOrder")]
- public Task<OrderEntity> GetOrder()
- {
- return Task.FromResult(new OrderEntity()
- {
- date_time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
- ip_address = Request.HttpContext.Connection.LocalIpAddress?.ToString(),
- ip_port = Request.HttpContext.Connection.LocalPort.ToString(),
- service_name = "订单服务",
- service_port= _configuration["ConsulSetting:ServicePort"]
- });
-
- }
- }
-
-
- public class OrderEntity
- {
- /// <summary>
- /// 当前时间
- /// </summary>
- public string? date_time { get; set; }
-
- /// <summary>
- /// Ip地址
- /// </summary>
- public string? ip_address { get; set; }
-
-
- /// <summary>
- /// Ip端口
- /// </summary>
- public string? ip_port { get; set; }
-
-
- /// <summary>
- /// 服务名称
- /// </summary>
- public string? service_name { get; set; }
-
-
- /// <summary>
- /// 服务端端口
- /// </summary>
- public string? service_port { get; set; }
-
- }
- }
同时我们增加一个HealthCheck的接口,由于该接口只用于返回Ok,所以我们写一个基于app的扩展方法即可,我们可以将代码写入ConsulExtendsion中,完整代码如下:
- public static void UseHealthCheckMiddleware(this IApplicationBuilder app, string checkPath = "/healthcheck")
- {
- app.Map(checkPath, applicationBuilder => applicationBuilder.Run(async context =>
- {
- context.Response.StatusCode = (int)HttpStatusCode.OK;
-
- await context.Response.WriteAsync("OK");
- }));
- }
同样我们需要在Program中注册该方法。
app.UseHealthCheckMiddleware();
至此就完成了服务注册,取消注册,健康检查等功能的代码编写。
同样的方式我们改造一下产品服务,代码一模一样,这里我就不再重复黏贴了。
我们需要发布项目,然后在docker中运行项目,docker部署我就不再重复了。
然后我们需要在docker中运行实例。
运行OrderService
- docker run -d -p 8060:80 --name orderapi1 ordercontainer:1.0 --ConsulSetting:ServicePort="8060"
-
- docker run -d -p 8061:80 --name orderapi2 ordercontainer:1.0 --ConsulSetting:ServicePort="8061"
-
- docker run -d -p 8062:80 --name orderapi3 ordercontainer:1.0 --ConsulSetting:ServicePort="8062"
运行ProductService
- docker run -d -p 8050:80 --name productapi1 productcontainer:1.0 --ConsulSetting:ServicePort="8050"
-
- docker run -d -p 8051:80 --name productapi2 productcontainer:1.0 --ConsulSetting:ServicePort="8051"
-
- docker run -d -p 8052:80 --name productapi3 productcontainer:1.0 --ConsulSetting:ServicePort="8052"
然后这里注意一下 ConsulSetting:ServicePort="8061"
这里的意思是会替换appsetting.json 文件中的ConsulSetting配置文件中ServicePort的内容,这里可以方便后面启动多实例时指定对应端口,
然后访问 http://localhost:8500 查看Consul查看服务是否注册成功。
至此,6个服务器实例都已运行,并且成功注册到Consul。
随便停止2个服务:
docker stop orderapi1 productapi1
可以看到停止的服务已经在Consul中被移除。注意,这个是我们停止程序时主动调用Consul移除的。
- //应用程序终止时,取消注册
- lifetime.ApplicationStopping.Register(() =>
- {
- consulClient.Agent.ServiceDeregister(registration.ID).Wait();
- });
当然程序发生异常,健康检查不能正确响应的话,Consul也会移除,有一点区别。
那么注册,发现,健康检查功能都完成了,下一步就该考虑客户端如何拿到这些服务实例的地址了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。