持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
03、客户端健康检测与常用配置
一、健康检测:
1.1 使用 Spring Boot Actuator
- 在实际开发中,还有可能出现一种情况
- 客户端的服务需要连接数据库,可是数据库已经崩溃,但是客户端仍然向服务器端发送心跳请求,这种情况下服务器端误认为客户端的服务是正常的
- 客户端应该及时告诉服务器端自己的健康状态
- 加入Actuator,提供了很多端点,其中一个叫做/heath端点
修改 srl-provider-server 依赖:
<!-- 健康检查依赖此模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
访问http://localhost:8888/actuator/health 即可返回当前服务提供者的健康状态
结果如下:{"status":"UP"}
1. 2 实现应用健康自检:
如果一个客户端本身没有问题,但是该模块所依赖的服务无法使用,那么对于服务器以及其他客户端来说,该客户端是不可用的 最常见 就是访问数据库的模块。我们需要做两件事:
- 让客户端自己进行检查,是否能连接数据库 ,
- 将连接数据库的结果与客户端的状态进行关联 并且将状态告诉服务器。
健康指示器:
/**java
* 此处是健康指示器,只是服务提供者知道 自己的健康状态
* 通过如下方式:http://localhost:8888/actuator/health 返回当前服务提供者的健康状态
* EK服务器 还是通过心跳机制检测 该服务提供者的健康状态
* 如果需要 EK服务器通过自定义的健康指示器来 检测健康状态, 需要 -- 健康检查处理器、eureka.client.healthcheck.enabled=true
*/
@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
if(HealthController.canVisitDb){
//成功连接数据库 返回UP
return new Health.Builder(Status.UP).build();
}else{
//连接数据库失败 返回Down
return new Health.Builder(Status.DOWN).build();
}
}
}
/**
* 此方法只用于模拟改变 当前节点的健康状态
* @param canVisitDb
* @return
*/
@RequestMapping(value = "db/{canVisitDb}", method = RequestMethod.GET)
public String setConnectState(@PathVariable("canVisitDb") Boolean canVisitDb) {
this.canVisitDb = canVisitDb;
return "当前数据库是否正常:" + this.canVisitDb;
}
健康检查处理器 :
在自定义的健康检查处理器中,注入了前面编写的健康指示器,根据健康指示器的结
果来返回不同 的状态 。Eureka 中会启动一个定时器,定时刷新本地实例的信息,并且执行
“处理器 ”中的 getStatus 方法,再将服务实例的状态“更新”到服务器中。执行以上逻辑
的定时器 默认30秒执行一次,如果想加快 到效果,可以修改eureka.client.instance-info-replication-interval-seconds 配置。
/**
* 健康检查处理器 -- 将健康指示器里的健康状态返回给eureka server
*/
@Component
public class MyHealthCheckHandler implements HealthCheckHandler {
@Autowired
private MyHealthIndicator myHealthIndicator;
@Override
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
//通过健康指示器来获取健康状态
Status status = myHealthIndicator.health().getStatus();
//返回状态给EK服务器
if (status.equals(Status.UP)) {
System.out.println("数据库正常连接");
return InstanceStatus.UP;
} else {
System.out.println("数据库无法连接");
return InstanceStatus.DOWN;
}
}
}
1.3 服务查询
在前节中,通过浏览器访问 Eureka 界面可查看服务状态的改变。本例通过修改服务 调用者的代码来查看应用健康自检的效果。修改服务调用者(srl-invoker-server 模块〉, 控制器修改后如代码
/**
* 测试查询服务列表
*
* @return
*/
@RequestMapping(value = "/select", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String routerSelect() {
//查找服务列表
List<ServiceInstance> ins = this.getServiceInstances();
//输出服务信息及状态
for (ServiceInstance service : ins) {
EurekaServiceInstance esi = (EurekaServiceInstance) service;
InstanceInfo info = esi.getInstanceInfo();
System.out.println(info.getAppName() + "---" + info.getInstanceId() + "---" + info.getStatus());
//获取配置的元数据信息
System.out.println(service.getMetadata().get("author-name"));
}
return "";
}
/**
* 在客户端中,如果需要查询集群中的服务 可以使用 Spring Cloud 的discoveryClient类,或者 Eureka 的eurekaClient 类,
* Spring Cloud 对Eureka 进行了封装。本例中调用了discoveryClient 的方法来查询服务实例。
* 在控制器的router方法中,仅将查询到的服务实例进行输出 。
*/
private List<ServiceInstance> getServiceInstances() {
List<String> ids = discoveryClient.getServices();
List<ServiceInstance> result = new ArrayList<>();
for (String id : ids) {
List<ServiceInstance> ins = discoveryClient.getInstances(id);
result.addAll(ins);
}
return result;
}
访问:http://localhost:9000/select 结果如下:
EUREKA-SERVER---DESKTOP-29QKE42:eureka-server:8761---UP
EUREKA-SERVER---DESKTOP-29QKE42:eureka-server:8762---UP
INVOKER-SERVER---DESKTOP-29QKE42:invoker-server:9000---UP
PROVIDER-SERVER---DESKTOP-29QKE42:provider-server:9999---UP
PROVIDER-SERVER---DESKTOP-29QKE42:provider-server:8888---UP
二、Eureka 的常用配置
主要有 自我保护模式、心跳检测机制、注册表抓取策略、元数据配置
客户端配置:
#客户端的实例会向服务器发送周期性的心跳,默认是30秒,可修改客户端此配置
eureka.instance.lease-renewal-interval-in-seconds=30
#超过30s没有收到心跳,则启用停流,将这个实例从列表中清理掉,这个值必须大于:eureka.instance.lease-renewal-interval-in-seconds
eureka.instance.lease-expiration-duration-in-seconds=90
#客户端向eureka-server抓取注册表(可用的服务列表),将服务器端的注册表保存到本地缓存中,默认是30s,需要结合集群规模设置(或者服务性质)
eureka.client.registry-fetch-interval-seconds=30
服务器配置:
#剔除失效节点 -- 注册表的清理间隔
eureka.server.eviction-interval-timer-in-ms=6000
#是否启用自我保护,不启用 自我保护:如果心跳的失败率超过一定比例,服务会将这些实例保护起来,并不会马上将其从注册表中剔除。结合后面容错机制
eureka.server.enable-self-preservation=false
Eureka的元数据有两种:标准元数据和自定义元数据。 标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。 自定义元数据:可以使用eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,但是一般不改变客户端行为,除非客户端知道该元数据的含义。
分别在每个节点定义key相同value不同的元数据:
#配置元数据信息,元数据都会保存在服务器的注册表中,并且使用简单的方式与客户端进行共享。DiscoveryClient调用
eureka.instance.metadata-map.author-name=songrongliang-provider
通过此方式获取:
@Autowired
private DiscoveryClient discoveryClient;
/**
* 测试查询服务列表
* @return
*/
@RequestMapping(value = "/select", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public String routerSelect() {
//查找服务列表
List<ServiceInstance> ins = this.getServiceInstances();
//输出服务信息及状态
for (ServiceInstance service : ins) {
EurekaServiceInstance esi = (EurekaServiceInstance) service;
InstanceInfo info = esi.getInstanceInfo();
System.out.println(info.getAppName() + "---" + info.getInstanceId() + "---" + info.getStatus());
//获取配置的元数据信息
System.out.println(service.getMetadata().get("author-name"));
}
return "";
}
/**
* 在客户端中,如果需要查询集群中的服务 可以使用 Spring Cloud 的discoveryClient类,或者 Eureka 的eurekaClient 类,
* Spring Cloud 对Eureka 进行了封装。本例中调用了discoveryClient 的方法来查询服务实例。
* 在控制器的router方法中,仅将查询到的服务实例进行输出 。
*/
private List<ServiceInstance> getServiceInstances() {
List<String> ids = discoveryClient.getServices();
List<ServiceInstance> result = new ArrayList<>();
for (String id : ids) {
List<ServiceInstance> ins = discoveryClient.getInstances(id);
result.addAll(ins);
}
return result;
}
打印输出如下:
EUREKA-SERVER---DESKTOP-29QKE42:eureka-server:8761---UP songrongliang-registry EUREKA-SERVER---DESKTOP-29QKE42:eureka-server:8762---UP songrongliang-registry INVOKER-SERVER---DESKTOP-29QKE42:invoker-server:9000---UP songrongliang-invoker PROVIDER-SERVER---DESKTOP-29QKE42:provider-server:8888---UP songrongliang-provider PROVIDER-SERVER---DESKTOP-29QKE42:provider-server:9999---UP songrongliang-provider