前言
注册中心的心跳机制有两种形式:客户端主动上报和客户端被动响应。Eureka属于是主动上报类型的,Client通过renew机制频繁的向Server发送消息,通知Server它还活着,不要将其从服务列表中剔除,但是我们renew仅仅是监控Client是否存活,并不会去检测Client依赖的服务是否存活
从图中我们发现Client123和Client456两个客户端均依赖了第三方组件,并且MySQL同时宕机了。
- Client123使用了Eureka自带的renew机制,renew最基础的就是调一下Server的
/apps/{appName}/{instanceId}?status=&lastDirtyTimestamp=
接口,正常情况下Client启动后的status为UP,所以只要Client自身服务不出问题,永远都是UP,默认的指示器是CompositeHealthIndicator
,默认的管理器为EurekaHealthCheckHandler
; - Client456通过扩展
HealthIndicator
接口和HealthCheckHandler
接口,然后来自定义需要监控的内容
默认健康监控组件
在类DiscoveryClient#getHealthCheckHandler
方法中选择需要使用的健康管理器
1 | public HealthCheckHandler getHealthCheckHandler() { |
方法调用流程图
自定义健康监控
-
自定义监控组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56@Component
public class HealthPolicyBean implements InitializingBean {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 调度线程池
*/
private ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
/**
* 数据库健康情况
*/
public static boolean dbHealth = true;
/**
* Redis健康情况
*/
public static boolean redisHealth = true;
/**
* MongoDB健康情况
*/
public static boolean mongoHealth = true;
@Override
public void afterPropertiesSet() throws Exception {
// 创建调度器
ThreadPoolExecutor heartbeatExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("redis-HeartbeatExecutor-%d").setDaemon(true).build());
TimedSupervisorTask task = new TimedSupervisorTask("redis-heartbeat", scheduled, heartbeatExecutor, 10,
TimeUnit.SECONDS, 100, new RedisTimer());
scheduled.schedule(task, 10, TimeUnit.SECONDS);
}
/**
* 监控Redis状态
*/
protected class RedisTimer implements Runnable {
@Override
public void run() {
try {
List<RedisClientInfo> clientList = redisTemplate.getClientList();
if (clientList == null || clientList.isEmpty()) {
HealthPolicyBean.redisHealth = false;
} else {
HealthPolicyBean.redisHealth = true;
}
} catch (Exception e) {
HealthPolicyBean.redisHealth = false;
}
}
}
} -
自定义HealthIndicator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* Cc健康指示器
*/
public class CcHealthIndicator implements HealthIndicator {
public Health health() {
if (HealthPolicyBean.dbHealth && HealthPolicyBean.redisHealth && HealthPolicyBean.mongoHealth) {
// 当所有组件都正常时才返回UP
return new Health.Builder(Status.UP).build();
} else {
return new Health.Builder(Status.DOWN).build();
}
}
} -
自定义HealthCheckHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* Cc健康管理器
*/
public class CcHealthCheckHandler implements HealthCheckHandler {
private CcHealthIndicator ccHealthIndicator;
public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) {
if (ccHealthIndicator.health().getStatus().equals(Status.UP)) {
return InstanceInfo.InstanceStatus.UP;
}
return InstanceInfo.InstanceStatus.DOWN;
}
}方法调用流程图
区别
我们打开Redis服务,启动Eureka Server、Client123和Client456。
-
Redis运行中
Redis正常运行时,两个服务都处于正常情况
-
Redis停止
将Redis服务停掉,等待一个renew周期后,服务状态发生变化,使用默认HealthCheckHandler的CUSER-SERVICE的status仍然为UP,而我们自定义HealthCheckHandler的EUREKA-HEALTH服务的status已经变成了DOWN,符合正常要求。
总结
在实际的生产工作中,尽量不要使用默认的HealthCheckHandler,不然就算是我们项目的MySQL、Redis、MongoDB、MQ都挂掉了,只要项目的进程还存活,那么status就很大的可能是UP,但实际上项目已经无法正常提供服务了,会给我们的项目带来很大的麻烦。