前言 
我们知道Eureka是通过Client向Server发送renew通知来续命,属于是"去中心化"的设计,而Consul是"中心化"设计,Consul的心跳由Server端发起
  Consul心跳 
Client在注册到Consul Server的时候(ConsulServiceRegistry#register),会将客户端的注册信息全部发送给注册中心(接口:/v1/agent/service/register),其中主要信息包括服务id、name、ip、port、health-check-url等,所以Consul Server才会知道向Client的哪个接口发送心跳。
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 {     id='consul-demo-7702',     name='consul-demo',     tags=[         secure=false     ],     address='192.168.0.107',     meta=null,     port=7702,     enableTagOverride=null,     check=Check{         script='null',         interval='10s',         ttl='null',         http='http://192.168.0.107:7702/health',         method='null',         header={         },         tcp='null',         timeout='null',         deregisterCriticalServiceAfter='null',         tlsSkipVerify=null,         status='null'     },     checks=null } 
 
这里check.http是值是我们在项目的properties文件中配置的:
1 2 3 4 5 6 server.port =7702 spring.application.name =consul-demo spring.cloud.consul.host =127.0.0.1 spring.cloud.consul.port =8500 spring.cloud.consul.discovery.health-check-path =/health 
 
Spring Cloud Consul的心跳接口默认为actuator包中的/actuator/health,所以如果我们既没设置自定义的心跳接口,也没依赖actuator包,那么Consul Server就会在我们注册的Service上显示service checks fail
我们查看Consul Server控制台,发现会控制台健康检查语句agent: Check is now critical: check=service:consul-demo-client-18090,正常的健康检查语句是agent: Check status updated: check=service:consul-demo-7702 status=passing
默认情况下Consul会每隔10秒,通过一个HTTP接口/health来检测节点的健康情况。 如果健康检测失败,那服务实例就会被标记成critical,可以通过在检查定义中指定超时字段来配置自定义HTTP检查超时值,检查的输出限制在大约4KB,大于此值的响应将被截断,会被认为健康检查未通过。
spring.cloud.consul.discovery.prefer-ip-address参数决定上报给注册中心的健康接口是IP还是hostname
 
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 public  static  NewService.Check createCheck (Integer port,  			HeartbeatProperties ttlConfig, ConsulDiscoveryProperties properties)   {  NewService.Check check = new  NewService.Check();   if  (StringUtils.hasText(properties.getHealthCheckCriticalTimeout())) {     check.setDeregisterCriticalServiceAfter(       properties.getHealthCheckCriticalTimeout());   }   if  (ttlConfig.isEnabled()) {     check.setTtl(ttlConfig.getTtl());     return  check;   }   Assert.notNull(port, "createCheck port must not be null" );   Assert.isTrue(port > 0 , "createCheck port must be greater than 0" );      if  (properties.getHealthCheckUrl() != null ) {     check.setHttp(properties.getHealthCheckUrl());   } else  {          check.setHttp(String.format("%s://%s:%s%s" , properties.getScheme(),                                 properties.getHostname(), port, properties.getHealthCheckPath()));   }      check.setHeader(properties.getHealthCheckHeaders());      check.setInterval(properties.getHealthCheckInterval());      check.setTimeout(properties.getHealthCheckTimeout());   check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify());   return  check; } 
 
图中Tags一栏有一个secure=false,这个是由客户端返回给Server,这个标识是检测健康检查接口是否为https协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  static  List<String> createTags (ConsulDiscoveryProperties properties)   {  List<String> tags = new  LinkedList<>(properties.getTags());   if  (!StringUtils.isEmpty(properties.getInstanceZone())) {     tags.add(properties.getDefaultZoneMetadataName() + "="               + properties.getInstanceZone());   }   if  (!StringUtils.isEmpty(properties.getInstanceGroup())) {     tags.add("group="  + properties.getInstanceGroup());   }      tags.add("secure="             + Boolean.toString(properties.getScheme().equalsIgnoreCase("https" )));   return  tags; }