Eureka client health detection of Spring Cloud (5)

In the previous blog, we know that the simplest Eureka microservice architecture also consists of 3 projects. When the number of projects increases, how does Eureka maintain services? How to ensure that one of the service instances is unavailable and exclude it?

Since the entire demonstration process is still the content of Eureka, we first get our sample code from the source code link at the bottom of the Spring Cloud service management framework Eureka simple example (3) article.

Client heartbeat push and detection

Eureka is divided into a server side and a client side. The client side will send a message to the server side every once in a while to explain to the server that it is still normal, let the server side continue to maintain its own services, and do not remove yourself from the service list. . At the same time, set a time for the server to wait for itself. When the service instance of your own does not continue to send heartbeats to the server, that is, the timing starts from the last heartbeat sent. After waiting for a period of time, if the message is still not received, the server will Remove this service instance from the service list, and no longer let traffic flow to this service instance.

eureka.instance.lease-renewal-interval-in-seconds

Indicates that the client needs to send a heartbeat to the server to indicate that it is alive. If the heartbeat stops for longer than the wait time set by the server, the server will remove the instance from its service list, thereby excluding traffic from the instance. Default 30s

eureka.instance.lease-expiration-duration-in-seconds

The amount of time the server side waits since it received the last heartbeat before it can remove this instance from its view and not allow traffic to this instance. Setting this value too long may mean that traffic can be routed to the instance even if the instance does not exist. Setting this value too small may mean that the instance may be excluded from traffic due to temporary network failures. This value should be set at least higher than the value specified in leaseRenewalIntervalInSeconds. Default 90s

We can intuitively understand these configurations by modifying these configurations and viewing the running results. First, run the main() method of the ServerApp class under the com.init.springCloud package of the euraka-server project to start the Eureka server. Then, modify the eureka-provider project pom.xml file and change the leaseRenewalIntervalInSeconds time to 5s. In order to see the heartbeat message sent by the project, we open the Eureka log log and view the output results in the console. pom.xml is configured as follows:

spring:
  application:
    name: eureka-provider

eureka:
  instance:
    leaseRenewalIntervalInSeconds: 5
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

logging:
  level:
    com.netflix: DEBUG

Then run the main() method of the ProviderApp class of the eureka-provider project. After the project starts successfully, we can see the printed log information on the console. Here I intercept a log within 5s. For the convenience of observation, the first time is removed:

[{}->http://localhost:8761] total kept alive: 1, total issued: 0, total allocated: 1 out of 200
Getting free connection [{}->http://localhost:8761][null]
Released connection is reusable.
Releasing connection [{}->http://localhost:8761][null]
Pooling connection [{}->http://localhost:8761][null]; keep alive indefinitely
Notifying no-one, there are no waiting threads
Jersey HTTP PUT http://localhost:8761/eureka//apps/EUREKA-PROVIDER/DESKTOP-E3UNJK3:eureka-provider; statusCode=200
DiscoveryClient_EUREKA-PROVIDER/DESKTOP-E3UNJK3:eureka-provider - Heartbeat status: 200

The browser accesses http://localhost:8761, and the project registered to the server can also be seen in the Eureka console.


Continue to add leaseExpirationDurationInSeconds in the pom.xml file of the eureka-provider project, side by side with leaseRenewalIntervalInSeconds, and the time is set to 10s. The previous introduction explained that this time is larger than leaseRenewalIntervalInSeconds. The modified pom.xml file is:

spring:
  application:
    name: eureka-provider

eureka:
  instance:
    leaseRenewalIntervalInSeconds: 5
    leaseExpirationDurationInSeconds: 10
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

logging:
  level:
    com.netflix: DEBUG

Since the server side will run a protection mechanism, although we clear our own instance after 10s is set on the client side, the server side will clear the service list after 60s by default, and remove the service instance that has expired. We can add a new configuration to the pom.xml of the eureka-server project, turn off the server's self-protection mechanism, and set the automatic cleaning time to be shorter.

enable-self-preservation Self-protection mechanism, enabled by default

eviction-interval-timer-in-ms The timer for the server to clear the service list, the default is 60s, note that the time is in milliseconds

server:
  port: 8761
  
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 10000

Then restart the eureka-provider and eureka-server projects, visit http://localhost:8761, make sure that the eureka-provider project has been registered on the eureka-server, then stop the eureka-provider project, wait about 10s, and revisit http ://localhost:8761, you will see that eureka-server no longer maintains eureka-provider services.

Client service fetch interval

After the server has updated the service list, in order to obtain the latest service list, the client needs to actively fetch the service list from the server.

eureka.client.registry-fetch-interval-seconds 

Indicates how often, in seconds, registry information is obtained from the Discovery server.

Add a method to the ConsumerController class of the eureka-consumer project to display the information of the currently cached service list. At the same time, in order to show the process of eureka-consumer fetching services, we also put it in the pom.xml of eureka-consumer Open the log and view the output information in the console (for easy viewing, you can comment out the log in the pom.xml file of the eureka-provider project). The pom.xml configuration of eureka-consumer is as follows:

server:
  port: 8081

spring:
  application:
    name: eureka-consumer

eureka:
  client:
    registry-fetch-interval-seconds: 5
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
      
logging:
  level:
    com.netflix: DEBUG

The new method countService() is added to the ConsumerController class. The complete code is as follows:

package com.init.springCloud;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

@Controller
@Configuration
public class ConsumerController {
	
	@Bean
	@LoadBalanced
	public RestTemplate getRestTemplate(){
		return new RestTemplate();
	}
	
	@GetMapping(value = "/router")
	@ResponseBody
	public String router(){
		RestTemplate temp = getRestTemplate();
		return temp.getForObject("http://eureka-provider/search/1", String.class);
	}
	
	@Autowired
	private DiscoveryClient  discoveryClient;
	
	@GetMapping(value = "/count")
	@ResponseBody
	public String countService(){
		List<String> services = discoveryClient.getServices();
		for (String string : services) {
			List<ServiceInstance> instances = discoveryClient.getInstances(string);
			System.out.println("Service name: "+string+", Number of services: "+instances.size());
		}
		return "success";
	}
	
}

After that, we start all three projects. Under the console of eureka-consumer, we can see the service capture log. Here, we also remove other information such as time:

Get connection: {}->http://localhost:8761, timeout = 5000
[{}->http://localhost:8761] total kept alive: 1, total issued: 0, total allocated: 1 out of 200
Getting free connection [{}->http://localhost:8761][null]
Released connection is reusable.
Releasing connection [{}->http://localhost:8761][null]
Pooling connection [{}->http://localhost:8761][null]; keep alive indefinitely
Notifying no-one, there are no waiting threads
Jersey HTTP GET http://localhost:8761/eureka//apps/delta?; statusCode=200
Got delta update with apps hashcode UP_2_
Added instance DESKTOP-E3UNJK3:eureka-provider to the existing apps in region null
Added instance DESKTOP-E3UNJK3:eureka-consumer:8081 to the existing apps in region null
The total number of instances fetched by the delta processor : 2
The total number of all instances in the client now is 2
Completed cache refresh task for discovery. All Apps hash code is Local region apps hashcode: UP_2_, is fetching remote regions? false

Visit http://localhost:8081/count, and the console also outputs our statistical service information:

Service name: eureka-consumer, number of services: 1
Service name: eureka-provider, number of services: 1

Detect client state using Actuator health endpoint

By default, Eureka uses the client's heartbeat to determine whether a service is in the "UP" state. As long as the client registers the service successfully, the Eureka server will announce that the service is "UP". Therefore, if the entire service is down It's down. Fortunately, the Eureka server can know that the service is down, but if a service provider can't connect to the database, the service instance is unavailable, but our server doesn't think so, because he has no way of knowing this Service is problematic. So, we introduce Actuator here and use its \Health endpoint for health detection.

In our example, eureka-provider is used as a service provider, here it is used as a test service, the package of Actuator is introduced, and the following dependencies are added to the pom.xml of eureka-provider:

	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-actuator</artifactId>
	        <version>1.5.4.RELEASE</version>
	    </dependency>

We imitate the database connection inside the program, and provide a method in the controller to modify whether the database connection fails, and then notify the health endpoint to modify the status of the current service. Add the following method to the ProviderController class:

	public static Boolean isCanLinkDb = true;
	
	@RequestMapping(value = "/linkDb/{can}", method = RequestMethod.GET)
	public void LinkDb(@PathVariable Boolean can){
		isCanLinkDb = can;
	}

Then create a new MyHealthIndicator class, implement the HealthIndicator interface, rewrite the Health method, pass in whether the database can be connected to this state, and finally change the health state of the current service:

package com.init.springCloud;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;
/**
 * Custom health indicator (Spring Boot content)
 * @author spirit   
 *@date May 3, 2018 at 2:19:58 PM
 * @email [email protected]
 */
@Component
public class MyHealthIndicator implements HealthIndicator {

	@Override
	public Health health() {
		if(ProviderController.isCanLinkDb){
			return new Health.Builder(Status.UP).build();
		}else{
			return new Health.Builder(Status.DOWN).build();
		}
	}

}

The status of the service is modified here, which is also for eureka-provider itself. We can know whether the service is normal through the health endpoint, so how can we tell eureka-server the service status returned by the health endpoint? That is to say to the Eureka server, the database of my service cannot be connected, and the service is temporarily unavailable. You can change the status of my service to the "DOWN" state in the service list, and do not continue to let requests flood into my service. . Here we use the HealthCheckHandler interface provided by Netflix, create a new MyHealthCheckHandler class, implement the HealthCheckHandler interface, and override the getStatus() method:

package com.init.springCloud;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Status;
import org.springframework.stereotype.Component;

import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.appinfo.InstanceInfo.InstanceStatus;

/**
 * Health check processor
 * @author spirit   
 *@date May 3, 2018 at 2:39:45 pm
 * @email [email protected]
 */
@Component
public class MyHealthCheckHandler implements HealthCheckHandler {

	@Autowired
	private MyHealthIndicator myHealthIndicator;
	
	@Override
	public InstanceStatus getStatus(InstanceStatus instanceStatus) {
		Status status = myHealthIndicator.health().getStatus();
		if(status.equals(Status.UP)){
			return InstanceStatus.UP;
		}else{
			return InstanceStatus.DOWN;
		}
	}

}

In fact, here, all the test code has been completed. However, in order to enable eureka-server to change and synchronize the information of the service list more quickly after receiving the service change information (the service list will be copied and updated between multiple servers that discover services), we shorten the update time. .

eureka.client.instance-info-replication-interval-seconds Indicates how often, in seconds, replication instance changes should be replicated to servers that discover the service. The default is 30s.

Add the above configuration to the pom.xml file of eureka-server and change the time to 10s:

server:
  port: 8761
  
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    instanceInfoReplicationIntervalSeconds: 10
    defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#  server:
#    enable-self-preservation: false
#    eviction-interval-timer-in-ms: 10000

In order to facilitate the test and not affect the observation, comment out all the previous configurations in the above two projects, and then start all three projects.

Visit: http://localhost:8080/health, you can see that the current status of the eureka-provider project is "UP"


Visit: http://localhost:8761, you can see that the status of the eureka-provider project is "UP":


Re-visit: http://localhost:8081/router, call the eureka-provider method through the eureka-consumer project (if you don’t know the process, you can view the previous blog content, or download the source code at the end of the article), you can also Normal return result:


Then visit: http://localhost:8080/linkDb/false, set the service of the eureka-provider project to be unavailable, and change the status to "DOWN". Visit the health endpoint of eureka-provider again: http://localhost:8080/health, you can see that the service status has changed:


Then visit: http://localhost:8761 again, check the service information maintained by the server, and the status of eureka-provider has also been changed to the unavailable state of "DOWN":


At this point, eureka-consumer can no longer call the services provided by eureka-provider:


Source code click here

Spring Cloud series:

Spring Cloud introduction and environment construction (1)

Simple use of Spring Boot (2)

Simple example of Spring Cloud service management framework Eureka (3)

Spring Cloud service management framework Eureka project cluster (4)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325567724&siteId=291194637