How will the application restart the executing task?

preface

Recently, some thoughts have been raised about system restart. How will ongoing requests be handled during system restart? Will the message being consumed be lost? Will asynchronous tasks be interrupted? Since these problems exist, can't our application be restarted? However, our application is constantly restarted with version iteration. Why do these problems not occur? Or did the application do additional processing? With these questions, combined with the scene simulation, see how to deal with the actual situation.

2. Scenario

2.1 http request

2.1. 1 create request

@RestController
public class ShutDownController {

    @RequestMapping("shut/down")
    public String shutDown() throws InterruptedException {
        TimeUnit.SECONDS.sleep(20);
        return "hello";
    }
}
Copy code

2.1. 2 call request

http://localhost:8080/shut/down

2.1. 3 simulation restart

kill -2 application pid
 Copy code

2.1. 4 phenomenon

2.1. 5 Conclusion

During the execution of the request, an inaccessible prompt appears when the application is closed

2.1. 6 turn on and turn off gracefully

The above phenomenon is very unfriendly to users and will cause users to look confused. Are there any measures to avoid this phenomenon? Can I execute accepted requests and reject new requests before the application closes? The answer is yes. You only need to add an elegant shutdown configuration in the configuration file

server:
  shutdown: graceful # Set elegant shutdown. This function is available in spring boot2 Only in version 3. Note: you need to use Kill -2 trigger to close the application. This command will trigger shutdown hook

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s # Set the buffer time. Pay attention to the time unit (this time is used to wait for the completion of task execution)
Copy code

After adding the configuration, execute 2.1.1 again 2 and 2.1 3 process, you will see the following effects

It can be seen that even if the application is closed during request execution, the received requests will still be executed

2.2 message consumption

As mentioned in the preface, will messages be lost or put back into the message queue when the application is closed during message consumption?

2.2. 1 create producer

@RestController
public class RabbitMqController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @GetMapping("/sendBusinessMessage")
    public void sendBusinessMessage() throws InterruptedException {
        rabbitTemplate.convertAndSend(RabbitmqConfig.BUSINESS_EXCHANGE, RabbitmqConfig.BUSINESS_ROUTING_KEY, "send message");
        TimeUnit.SECONDS.sleep(10000);
    }
}
Copy code

2.2. 2 create a consumer

@Component
@RabbitListener(queues = RabbitmqConfig.BUSINESS_QUEUE_NAME)
@Slf4j
public class BusinessConsumer {

    /**
     * Operation scenario:
     * 1.Start the application through the RabbitmqApplication startup class
     * 2.Call the / sendBusinessMessage interface to send a message
     * 3.RabbitMQ broker Send message to consumer
     * 4.Consumers consume after receiving the message
     * 5.When consumers consume messages, the application closes, the channel is disconnected, the connection is disconnected, and the messages without ack will be put back into the broker
     *
     * @param content Message content
     * @param channel channel passageway
     * @param message message object
     */
    @RabbitHandler
    public void helloConsumer(String content, Channel channel, Message message) {
        log.info("business consumer receive message: {}", content);
        try {
            // Simulated business execution time
            TimeUnit.SECONDS.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Copy code

2.2. 3 call request

http://localhost:8080/sendBusinessMessage

2.2. 4 before closing the application

2.2. 5 after closing the application

2.2. 6 Conclusion

In the process of message consumption, when the application is closed, the messages without ack will be put back into the message queue to ensure that the messages will be consumed

2.3 asynchronous tasks

2.3. 1. Configuration of route pool

@Component
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("test-");
        threadPoolTaskExecutor.setCorePoolSize(3);
        threadPoolTaskExecutor.setMaxPoolSize(3);
        threadPoolTaskExecutor.setQueueCapacity(100);
        return threadPoolTaskExecutor;
    }
}
Copy code

2.3. 2 asynchronous task request

@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;

@RequestMapping("async/task")
public void asyncTask() throws InterruptedException {
  for (int i = 0; i < 10; i++) {
    threadPoolTaskExecutor.execute(() -> {
      try {
        TimeUnit.SECONDS.sleep(10);
      } catch (InterruptedException e) {
        throw new RuntimeException();
      }
      log.info("task execute complete...");
    });
  }
}
Copy code

2.3. 3 call request

http://localhost:8080/async/task

2.3. 4 simulation restart

kill -2 application pid
 Copy code

2.3. 5 phenomenon

Exception in thread "test-2" Exception in thread "test-1" Exception in thread "test-3" java.lang.RuntimeException
	at com.boot.example.ShutDownController.lambda$asyncTask$0(ShutDownController.java:37)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
java.lang.RuntimeException
	at com.boot.example.ShutDownController.lambda$asyncTask$0(ShutDownController.java:37)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
java.lang.RuntimeException
	at com.boot.example.ShutDownController.lambda$asyncTask$0(ShutDownController.java:37)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Copy code

2.3. 6. Modify thread pool configuration

Add the following configuration to the thread pool configuration:

threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);
threadPoolTaskExecutor.setAwaitTerminationSeconds(120);
Copy code

2.3. 7 phenomena after modifying the configuration

2021-12-09 17:09:40.054  INFO 22383 --- [         test-1] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:09:40.055  INFO 22383 --- [         test-3] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:09:40.055  INFO 22383 --- [         test-2] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:09:50.059  INFO 22383 --- [         test-3] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:09:50.059  INFO 22383 --- [         test-1] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:09:50.060  INFO 22383 --- [         test-2] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:10:00.062  INFO 22383 --- [         test-2] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:10:00.062  INFO 22383 --- [         test-1] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:10:00.065  INFO 22383 --- [         test-3] com.boot.example.ShutDownController      : task execute complete...
2021-12-09 17:10:10.066  INFO 22383 --- [         test-1] com.boot.example.ShutDownController      : task execute complete...
Copy code

2.3. 8 conclusion

Use the thread pool to execute asynchronous tasks. Without adding configuration, the task cannot be completed. When adding configuration, the task can still be completed.

3. Summary

In order to ensure that tasks can still be executed and completed during application restart, you need to enable graceful shutdown configuration and add wait task execution completion and wait time configuration to the thread pool

Added by CBR on Fri, 10 Dec 2021 02:46:10 +0200