Microservice Architecture: Nacos Local Cache PK Microservice Elegant Offline

Posted by ol4pr0 on Fri, 21 Jan 2022 11:26:00 +0100

Preface

In the previous article, " Microservice: Take a look at the source code and Nacos'health checks are so simple The release scenario for the sudden suspension of micro-services is described in: adjusting health check cycles and requesting retries for failures. Friends read the article and suggested that they talk about how to gracefully offline the microservice when it is normally turned off.

Why elegant offline? We know that in distributed applications, clients of registries such as Nacos, Eureka, and so on, cache the list of instances to meet A (availability) in CAP principles. When the application is shut down normally, the list of instances cached by these clients will take some time to expire, although the registry can be actively called to log off.

This may lead to service requests to instances that have been shut down. Although retry mechanisms can solve this problem, retries can occur for this solution, which can slow down user-side requests to some extent. Elegant offline operations are required.

Let's start by talking about several ways that processes are normally shut down.

Mode 1: Based on kill Command

Spring Cloud itself supports shutting down services and actively calls Shutdown hook to log off the current instance when the process is shut down by the kill command. How to use:

kill Java process ID

This is done by using Spring Cloud's Hutdown hook mechanism, which is essentially provided by Spring Boot and specifically deregistered by the Spring Cloud service discovery function, to deregister services such as Nacos and Eureka before the service is shut down. However, this logout only tells the registry that the client's cache may take several seconds (Nacos defaults to 5 seconds) to become aware.

This Shutdown hook mechanism is not only applicable to kill commands, but also to program exit normally and use System.exit(), terminal using Ctrl + C, etc. However, it is not applicable in scenarios such as kill-9 that force shutdown or server downtime.

Although this scheme shortens the wait time by 15 seconds, which is better than hanging it directly, it does not solve the client-side caching problem in essence and is not recommended.

Mode 2: Based on/shutdown endpoint

In SpringBoot, the / shutdown endpoint is provided, which allows for elegant downtime, but is essentially the same as the first, all based on a Hutdown hook. Once the logic based on the hutdown hook has been processed, the service will also be shut down, but there is also a problem with client-side caching, so it is not recommended.

This way, you first need to introduce corresponding dependencies into your project:

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

Then configure the open/shutdown endpoint in the project:

management:
  endpoint:
    shutdown:
      enabled: true
  endpoints:
    web:
      exposure:
        include: shutdown

Then request the corresponding endpoint when stopping, using the curl command example here:

curl -X http://Instance Service Address/actuator/shutdown

Mode 3: Based on/pause endpoint

Spring Boot also provides the / pause endpoint (provided by Spring Boot Actuator), through which you can modify instances of / health in UP state to Down state.

The basic operation is to open the pause endpoint in the configuration file:

management:
  endpoint:
    # Enable pause endpoint
    pause:
      enabled: true
    # pause endpoints depend on restart endpoints in some versions
    restart:
      enabled: true
  endpoints:
    web:
      exposure:
        include: pause,restart

Then send the curl command to terminate the service. Note that POST requests are required here.

The use of the / pause endpoint varies greatly from version to version. The author uses Spring Boot 2.4.2. The RELEASE version found no effect at all, checking Issues for the Spring Boot and Spring Cloud projects found that the problem was from 2.3.1.RELEASE exists. At this point it seems that the administration of Web Server has changed to SmartLifecycle in the latest version, which Spring Cloud seems to have given up support (to be checked) and the latest version calls/pause endpoints have not responded to.

Due to the large changes in the above versions, it is not recommended to use the / pause endpoint for offline operation of microservices, but the whole idea of using the / pause endpoint is still worth learning.

The basic idea is that when the / pause endpoint is invoked, the state of the microservice changes from UP to DOWN, and the service itself can still provide the service normally. When the microservice is marked as DOWN, it is removed from the registry and waited for a period of time (for example, 5 seconds) before it is stopped processing when the list of instances cached by the Nacos client is updated.

The core of this idea is to switch off the traffic of the micro service first, then turn it off or republish it. This solves the problem of client caching instance lists when publishing normally.

Based on the above ideas, you can actually implement the appropriate functions yourself, such as providing a Controller, calling the method in the Controller to unregister the current instance from Nacos, then waiting for five seconds before shutting down the service through scripting or other means.

Mode 4: Based on/service-registry endpoint

The scheme mentioned in Mode 3 would be better if Spring Cloud could support it directly. No, Spring Cloud provides the / service-registry endpoint. But the name tells you an endpoint that is specifically implemented for service registration.

Open/service-registry endpoint in configuration file:

management:
  endpoints:
    web:
      exposure:
        include: service-registry
      base-path: /actuator
  endpoint:
    serviceregistry:
      enabled: true

Visit http://localhost:8081/actuator Endpoints can see that the following endpoints are turned on:

{
    "_links": {
        "self": {
            "href": "http://localhost:8081/actuator",
            "templated": false
        },
        "serviceregistry": {
            "href": "http://localhost:8081/actuator/serviceregistry",
            "templated": false
        }
    }
}

Modify the state of the service through the curl command:

curl -X "POST" "http://localhost:8081/actuator/serviceregistry?status=DOWN" -H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"

Before executing the above command, check that the corresponding instance of Nacos is in the following state:

You can see that the button in the instance details is offline, which means it is currently in UP state. When the above curl command is executed, the button in the instance details is "Online", indicating that the instance is offline.

The above commands are equivalent to manually running instances up and down in the Nacos administration background.

Of course, this is based on the patterns of Spring Cloud and Nacos, which essentially defines a specification, such as the ServiceRegistry interface that all registries need to implement, and the generic Endpoint that is defined based on the ServiceRegistry abstraction:

@Endpoint(id = "serviceregistry")
public class ServiceRegistryEndpoint {

   private final ServiceRegistry serviceRegistry;

   private Registration registration;

   public ServiceRegistryEndpoint(ServiceRegistry<?> serviceRegistry) {
      this.serviceRegistry = serviceRegistry;
   }

   public void setRegistration(Registration registration) {
      this.registration = registration;
   }

   @WriteOperation
   public ResponseEntity<?> setStatus(String status) {
      Assert.notNull(status, "status may not by null");

      if (this.registration == null) {
         return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
      }

      this.serviceRegistry.setStatus(this.registration, status);
      return ResponseEntity.ok().build();
   }

   @ReadOperation
   public ResponseEntity getStatus() {
      if (this.registration == null) {
         return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
      }

      return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration));
   }

}

The Endpoint we called above is implemented using the code above. So not only Nacos, but the registry centers based on Spring Cloud integration are essentially offline services that support this approach.

Summary

Many projects are gradually carrying out microservice transformation, but once the microservice system is in progress, it will face a more complex situation. This article focuses on the elegant offline of Nacos in the Spring Cloud system to analyze a common problem and solution in the microservice industry. Are you using microservices, and have you noticed this? If you want to learn more about microservices, don't say anything, just pay attention.

Nacos Series

Program New Horizon
Public Number Program New Horizon", a platform to synchronize your soft power and hard technology, to provide huge amounts of data

Topics: Spring Cloud Microservices Nacos