触发时机:利用JVM的shutdownhook进行自定义控制
由于springboot本身已经注册了(自动注册ShutdownHook),且通过发布ContextClosedEvent事件,因此监听ApplicationListener事件即可
@Component
public class GracefulShutdownHandler implements ApplicationListener<ContextClosedEvent> {
public static final AtomicBoolean contextClosed = new AtomicBoolean(false);
private ApplicationContext applicationContext;
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (contextClosed.compareAndSet(false, true)) {
this.applicationContext = event.getApplicationContext();
//TODO 优雅停机
}
}
}
修改的内容
一、停止接收HTTP新的请求(摘除流量,停止新请求打到机器上)
1、nginx等方式摘除流量
2、运维侧docker的优雅停机123
3、Springboot本身的优雅停机(注意:Springboot2.3版本,同时需要配合Tomcat 9.0.33(含)以上版本)
# 开启优雅停机,默认值:immediate 为立即关闭
server.shutdown=graceful
# 设置缓冲期,最大等待时间,默认:30秒
spring.lifecycle.timeout-per-shutdown-phase=30s
原理
是在AbstractApplicationContext.this.doClose();内开始触发的。
- spring.lifecycle.timeout-per-shutdown-phase=30s
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration#defaultLifecycleProcessor
org.springframework.context.support.AbstractApplicationContext#doClose
org.springframework.context.support.DefaultLifecycleProcessor#onClose
- 触发时机(通过SmartLifecycle的stop触发)
1、所有的Lifecycle进行处理:Phased作为key,相同Phased的Lifecycle集合作为value组成Map。
org.springframework.context.support.DefaultLifecycleProcessor#stopBeans
2、根据key倒叙排列,依次执行对应的Lifecycle集合的stop。每个key的处理之间,最多wait时间(spring.lifecycle.timeout-per-shutdown-phase),待处理完成后,才会执行下一个key的业务处理
2.1、Phased为2147483647的WebServerGracefulShutdownLifecycle进行shutdown处理(停止新请求的处理,并等待现有请求完成),最多wait时间(spring.lifecycle.timeout-per-shutdown-phase)
org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle#stop
2.2、Phased为2147483646的WebServerGracefulShutdownLifecycle则会强制关闭容器
org.springframework.boot.web.reactive.context.WebServerStartStopLifecycle#stop
- 使用配置server.shutdown=graceful
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getTomcatWebServer中创建webServer时使用
二、xxl-job的优雅停机(停止接收任务)
Map<String, XxlJobExecutor> xxlJobExecutorMap = applicationContext.getBeansOfType(XxlJobExecutor.class);
Optional.ofNullable(xxlJobExecutorMap).ifPresent(map -> {
map.values().forEach(xxlJobExecutor -> {
xxlJobExecutor.destroy();
});
三、普通Scheduled定时任务的优雅停机(停止接收任务)
Map<String, ThreadPoolTaskScheduler> taskSchedulerMap = applicationContext.getBeansOfType(ThreadPoolTaskScheduler.class);
Optional.ofNullable(taskSchedulerMap).ifPresent(map -> {
map.values().forEach(taskScheduler -> {
ScheduledThreadPoolExecutor poolExecutor = taskScheduler.getScheduledThreadPoolExecutor();
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
poolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
poolExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskScheduler.shutdown();
});
四、springboot与rocketMQ的优雅停机
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq-spring-boot-starter.version}</version>
</dependency>
@Component
@RocketMQMessageListener(topic = "", consumerGroup = "", messageModel = MessageModel.CLUSTERING)
public class ConsumerRocketMQListener implements RocketMQListener<NotifyMqMessage> {
@Override
public void onMessage(NotifyMqMessage notifyMqMessage) {
}
}
Map<String, DefaultRocketMQListenerContainer> mqConsumerMap = applicationContext.getBeansOfType(DefaultRocketMQListenerContainer.class);
Optional.ofNullable(mqConsumerMap).ifPresent(map -> {
map.values().forEach(DefaultRocketMQListenerContainer::stop);
});
五、其他
通过标识控制不同的功能的暂停
public static final AtomicBoolean contextClosed = new AtomicBoolean(false);
各个业务通过contextClosed.get()判断是否进入停机流程