Eureka心跳检测

前言

注册中心的心跳机制有两种形式:客户端主动上报和客户端被动响应。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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public HealthCheckHandler getHealthCheckHandler() {
  HealthCheckHandler healthCheckHandler = this.healthCheckHandlerRef.get();
  if (healthCheckHandler == null) {
    if (null != healthCheckHandlerProvider) {
      healthCheckHandler = healthCheckHandlerProvider.get();
    } else if (null != healthCheckCallbackProvider) {
      healthCheckHandler = new HealthCheckCallbackToHandlerBridge(healthCheckCallbackProvider.get());
    }

    if (null == healthCheckHandler) {
      healthCheckHandler = new HealthCheckCallbackToHandlerBridge(null);
    }
    this.healthCheckHandlerRef.compareAndSet(null, healthCheckHandler);
  }

  return this.healthCheckHandlerRef.get();
}

方法调用流程图

自定义健康监控

  1. 自定义监控组件

    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 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 clientList = redisTemplate.getClientList();
                    if (clientList == null || clientList.isEmpty()) {
                        HealthPolicyBean.redisHealth = false;
                    } else {
                        HealthPolicyBean.redisHealth = true;
                    }
                } catch (Exception e) {
                    HealthPolicyBean.redisHealth = false;
                }
            }
        }
    
    }
    
  2. 自定义HealthIndicator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    /**
     * Cc健康指示器
     */
    @Component
    public class CcHealthIndicator implements HealthIndicator {
    
        @Override
        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();
            }
        }
    }
    
  3. 自定义HealthCheckHandler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    /**
     * Cc健康管理器
     */
    @Component
    public class CcHealthCheckHandler implements HealthCheckHandler {
    
        @Autowired
        private CcHealthIndicator ccHealthIndicator;
    
        @Override
        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,但实际上项目已经无法正常提供服务了,会给我们的项目带来很大的麻烦。

你可能感兴趣的:(Eureka心跳检测)