First, Consul several ways of health check

Reference: www.consul.io/docs/discov…

Consulagents conduct health checks in the following main ways

  • Script + Interval: Executes the Script periodically and determines whether the application is alive based on the return code of the Script. 0 indicates the application is alive, 1 indicates the warning, and other values indicate the death.
  • HTTP + Interval: periodically sends HTTP requests to an application endpoint and determines whether the application is alive based on the HTTP status code. 2xx indicates that the application is alive.
  • TCP + Interval: initiates A TCP connection periodically. If the connection is enabled within the timeout period, the TCP connection survives.
  • Time to Live (TTL) : An application actively sends a heartbeat to a ConsulAgent through HTTP within the expired Time. If no heartbeat is received within the expired Time, the application dies.
  • Docker + Interval: Periodically executes the Docker exec command and determines whether the application is alive based on the return code by shell script.
  • GRPC + Interval: periodically requests configured endpoints through THE gRPC protocol to check whether the endpoints survive.

For SpringCloudConsul, two approaches are supported

  • HTTP + Interval
  • Time to Live (TTL)

Either way, this is determined in the message that first calls Consul to perform service registration.

Review of service registration

Review the first, the service registry entry is ConsulAutoServiceRegistrationListener, when Tomcat container starts, receive WebServerInitializedEvent here, performing service registration.

/** * Service registration is triggered when the container instantiates all singleton beans, FinishRefresh phase * org. Springframework. Context. Support. AbstractApplicationContext# finishRefresh * Call all SmartLifecycle bean start method * org. Springframework. Context. Support. DefaultLifecycleProcessor# onRefresh * Call WebServerStartStopLifecycle start method, It after the servlet container * org. Springframework. Boot. Web. Servlet. Context. WebServerStartStopLifecycle# start * @ param applicationEvent  */ @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { if (applicationEvent instanceof WebServerInitializedEvent) { WebServerInitializedEvent event = (WebServerInitializedEvent) applicationEvent; ApplicationContext context = event.getApplicationContext(); this.autoServiceRegistration.setPortIfNeeded(event.getWebServer().getPort()); this.autoServiceRegistration.start(); }}Copy the code
  • AutoServiceRegistration corresponding ConsulAutoServiceRegistration examples, it is the process of start method is stipulated by the parent class
ConsulAutoServiceRegistration
public void start() {
    super.start();
}
Copy the code
  • Superclass AbstractAutoServiceRegistration provided by spring – cloud – a common abstract registration processes
Public void the start () {/ / 1. Judge ConsulAutoServiceRegistration. IsEnabled if (! isEnabled()) { return; } if (! This. Running. The get ()) {/ / 2. This release InstancePreRegisteredEvent events. Context. PublishEvent (new InstancePreRegisteredEvent(this, getRegistration())); / / 3. Call ConsulAutoServiceRegistration. Register / / is actually called AbstractAutoServiceRegistration# register register (); / / 4. Release events InstanceRegisteredEvent enclosing context. PublishEvent (new InstanceRegisteredEvent < > (this, getConfiguration ())); / / 5. Set the state of running = true this.running.com pareAndSet (false, true); }}Copy the code
  • AbstractAutoServiceRegistration#register

Call ConsulServiceRegistry register method (http://localhost:8500/v1/agent/service/register) registered a ConsulAutoRegistration instance directly

protected void register() {
    this.serviceRegistry.register(getRegistration());
}
Copy the code

GetRegistration () obtains automatically injected instances of ConsulAutoRegistration. The key point is messages made up of data in the file

@Bean @ConditionalOnMissingBean public ConsulAutoRegistration consulRegistration( AutoServiceRegistrationProperties autoServiceRegistrationProperties, ConsulDiscoveryProperties properties, ApplicationContext applicationContext, ObjectProvider<List<ConsulRegistrationCustomizer>> registrationCustomizers, ObjectProvider<List<ConsulManagementRegistrationCustomizer>> managementRegistrationCustomizers, HeartbeatProperties heartbeatProperties) { return ConsulAutoRegistration.registration(autoServiceRegistrationProperties,  properties, applicationContext, registrationCustomizers.getIfAvailable(), managementRegistrationCustomizers.getIfAvailable(), heartbeatProperties); }Copy the code

Third, ConsulAutoRegistration

ConsulAutoRegistration encapsulates NewService, which is an abstraction of a service instance in Consul and the source of data packets registered with Consul.

ConsulAutoRegistration. Registration: build ConsulAutoRegistration

public static ConsulAutoRegistration registration( AutoServiceRegistrationProperties autoServiceRegistrationProperties, ConsulDiscoveryProperties properties, ApplicationContext context, List<ConsulRegistrationCustomizer> registrationCustomizers, List<ConsulManagementRegistrationCustomizer> managementRegistrationCustomizers, HeartbeatProperties HeartbeatProperties) {// NewService is an abstraction from Consul on service instance NewService = new NewService(); / / priority spring. Cloud. Consul. Discovery. The serviceName / / spring. Otherwise application. The name / / the default application String appName = getAppName(properties, context.getEnvironment()); // instanceId service.setId(getInstanceId(properties, context)); if (! properties.isPreferAgentAddress()) { // return this.preferIpAddress ? this.ipAddress : this.hostname service.setAddress(properties.getHostname()); } service.setName(normalizeForDns(appName)); service.setTags(createTags(properties)); service.setEnableTagOverride(properties.getEnableTagOverride()); service.setMeta(getMetadata(properties)); if (properties.getPort() ! = null) { service.setPort(properties.getPort()); // Set the Check attribute, Said consul health examination way setCheck (service, autoServiceRegistrationProperties, properties, context, heartbeatProperties); } ConsulAutoRegistration registration = new ConsulAutoRegistration(service, autoServiceRegistrationProperties, properties, context, heartbeatProperties, managementRegistrationCustomizers); / / trigger all ConsulRegistrationCustomizer customize method, generally is called consul registration service instance tag customize (registrationCustomizers, registration); return registration; }Copy the code

SetCheck method:

public static void setCheck(NewService service, AutoServiceRegistrationProperties autoServiceRegistrationProperties, ConsulDiscoveryProperties properties, ApplicationContext context, HeartbeatProperties heartbeatProperties) { // spring.cloud.consul.discovery.registerHealthCheck = true if (properties.isRegisterHealthCheck() && service.getCheck() == null) { Integer checkPort = service.getPort(); service.setCheck(createCheck(checkPort, heartbeatProperties, properties)); }}Copy the code

CreateCheck Method: Set newservice. Check

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()); } / / 1. If spring. Cloud. Consul. Discovery. Heartbeat. Enabled = true (the default is false), Use TTL for health check if (ttlconfig.isenabled ()) {// The default TTL is 30s check.setTtl(ttlconfig.getttl ()); return check; } // Make sure that the url is set to "200" and the url is set to "200". Using custom health check interface if (the properties. The getHealthCheckUrl ()! = null) { check.setHttp(properties.getHealthCheckUrl()); Format ("%s://%s:%s%s", properties.getScheme(), properties.getHostname(), port, properties.getHealthCheckPath())); } check.setHeader(properties.getHealthCheckHeaders()); / / check interval check. SetInterval (properties. GetHealthCheckInterval ()); / / check the timeout time check. SetTimeout (properties. GetHealthCheckTimeout ()); check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify()); return check; }Copy the code

Implementation of TTL health check

TTL is implemented by encapsulating service ids as ConsulHeartbeatTask into a thread pool after registration to continuously request ConsulHeartbeatTask to send heartbeats

  • Sign up and start a heartbeat
/ / 1. http://localhost:8500/v1/agent/service/register registered service instance with the consul this. Client. AgentServiceRegister (reg. GetService (), this.properties.getAclToken()); NewService service = reg.getService(); // 2. If the health check mode is TTL, submit a scheduled task. Continue to consul agent sends the heart / / spring cloud. Consul. Discovery. Heartbeat. Enable = true if (this. HeartbeatProperties. IsEnabled () && this.ttlScheduler ! = null && service.getCheck() ! = null && service.getCheck().getTtl() ! = null) { this.ttlScheduler.add(reg.getInstanceId()); }Copy the code
  • Injected TtlScheduler ConsulHeartbeatAutoConfiguration
@Bean
@ConditionalOnMissingBean
public TtlScheduler ttlScheduler(HeartbeatProperties heartbeatProperties,
        ConsulClient consulClient) {
    return new TtlScheduler(heartbeatProperties, consulClient);
}
Copy the code
  • Ttlscheduler. add(String instanceId) encapsulates instanceId as ScheduledFuture and executes it periodically in the thread pool
public void add(String instanceId) {
	ScheduledFuture task = this.scheduler.scheduleAtFixedRate(
			new ConsulHeartbeatTask(instanceId), this.configuration
					.computeHeartbeatInterval().toStandardDuration().getMillis());
	ScheduledFuture previousTask = this.serviceHeartbeats.put(instanceId, task);
	if (previousTask != null) {
		previousTask.cancel(true);
	}
}
Copy the code
private class ConsulHeartbeatTask implements Runnable { private String checkId; ConsulHeartbeatTask(String serviceId) { this.checkId = serviceId; if (! this.checkId.startsWith("service:")) { this.checkId = "service:" + this.checkId; }} / call * * * http://localhost:8500/v1/agent/check/pass/service:testConsulApp-8080 sends a heartbeat the consul * / @ Override public void run() { TtlScheduler.this.client.agentCheckPass(this.checkId); }}Copy the code

Five, the summary

  • SpringCloudConsul supports two health check modes: 1) HTTP +interval 2)TTL.
  • By default, SpringCloudConsul uses HTTP +interval. By default, springboot-actuator uses /actuator/ Health endpoints. Can be set by the spring. Cloud. Consul. Discovery. HealthCheckUrl set custom endpoint.
  • By setting the spring. Cloud. Consul. Discovery. Heartbeat. Enable = true health examination mode can be set to TTL, consists of an application sends a heartbeat to the consul expiration time default 30 s, Can be set by the spring. Cloud. Consul. Discovery. Heartbeat. TtlValue to change the expiration time.
  • Service Registration:PUT http://localhost:8500/v1/agent/service/register
{" ID ":" testConsulApp - 8080 ", "Name" : "testConsulApp", "Tags" : [" secure = false "], "Address" : "127.0.0.1", "Meta" : {}, "Port": 8080, "Check": { "TTL": "30s" } }Copy the code
  • Service logout:PUT http://localhost:8500/v1/agent/service/deregister/testConsulApp-8080