前言
我们知道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; }