当我们关闭或者重启你的web应用的时候,需要考虑很多问题,比如你的请求有没有处理完,你的定时任务是不是在执行,你的其他线程里运行的代码是不是突然被停止了而导致各种各样的问题。当然我们最常见的是第一种,其他的情况基于你的程序有没有这种处理,我们今天主要来讨论一下第一种情况。假设你的请求处理时间比较长,如果立即停止你的服务,那么这个请求就无法返回了,客户端会得到一个server error,在springboot的程序里我们怎么处理这种情况呢。其实有很多办法,我们现从springboot代码层面尝试解决它。
直接贴代码,大家参考即可,
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private static final int TIMEOUT = 30;
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
log.warn("Tomcat thread pool did not shut down gracefully within "
+ TIMEOUT + " seconds. Proceeding with forceful shutdown");
threadPoolExecutor.shutdownNow();
if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
log.error("Tomcat thread pool did not terminate");
}
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
@Bean
public ConfigurableServletWebServerFactory webServerFactory(final GracefulShutdown gracefulShutdown) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(gracefulShutdown);
return factory;
}
解释一下,我们拿到请求线程池,在触发关闭的时候让其延迟等待30s才关闭线程池,也就是给任务最多30s的等待时间。测试如下:
@RestController
public class LongProcessController {
@RequestMapping("/long-process")
public String pause() throws InterruptedException {
Thread.sleep(10000);
return "Process finished";
}
}
这个请求会处理10s,我们curl一下我们的接口,然后立即杀死springboot的java进程,我们的curl请求最终会返回我们期望的Process finished之后程序才会关闭。
当然这是从springboot代码实现这样的功能,我们完全也可以从运维架构层面实现,比如我们在kubernetes(目前应该大部分公司都会使用k8s)里进行AB发布,把新版本都启动起来,先进行流量切换,之后等待30s再把旧版本杀死,可以实现同样让老服务等待30s关闭的效果。
欢迎关注我的blog: ShareTech
网友评论