我们知道Eureka是通过Client向Server发送renew通知来续命,属于是"去中心化"的设计,而Consul是"中心化"设计,Consul的心跳由Server端发起
Client在注册到Consul Server的时候( ConsulServiceRegistry#register ),会将客户端的注册信息全部发送给注册中心(接口: /v1/agent/service/register ),其中主要信息包括服务id、name、ip、port、health-check-url等,所以Consul Server才会知道向Client的哪个接口发送心跳。
ConsulServiceRegistry#register
/v1/agent/service/register
1234567891011121314151617181920212223242526
{ 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文件中配置的:
123456
server.port=7702spring.application.name=consul-demospring.cloud.consul.host=127.0.0.1spring.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
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,大于此值的响应将被截断,会被认为健康检查未通过。
/health
spring.cloud.consul.discovery.prefer-ip-address参数决定上报给注册中心的健康接口是IP还是hostname
健康检查接口创建源码
ConsulAutoRegistration#createCheck
1234567891011121314151617181920212223242526272829303132
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"); // 若自定义了spring.cloud.consul.discovery.health-check-url if (properties.getHealthCheckUrl() != null) { check.setHttp(properties.getHealthCheckUrl()); } else { // 自定义了spring.cloud.consul.discovery.health-check-path或默认 check.setHttp(String.format("%s://%s:%s%s", properties.getScheme(), properties.getHostname(), port, properties.getHealthCheckPath())); } // spring.cloud.consul.discovery.health-check-headers check.setHeader(properties.getHealthCheckHeaders()); // 设置健康检查频率spring.cloud.consul.discovery.health-check-interval,字符串,要加上单位"5s" check.setInterval(properties.getHealthCheckInterval()); // 设置健康检查超时时间spring.cloud.consul.discovery.health-check-timeout,字符串,要加上单位"5s" check.setTimeout(properties.getHealthCheckTimeout()); check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify()); return check;}
图中Tags一栏有一个 secure=false ,这个是由客户端返回给Server,这个标识是检测健康检查接口是否为https协议
secure=false
1234567891011121314151617
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()); } // 检查请求schema是否为https tags.add("secure=" + Boolean.toString(properties.getScheme().equalsIgnoreCase("https"))); return tags;}