要点
springboot项目
spring-boot-starter-actuator插件
定制化tomcat关闭回调
nginx 负载均衡(至少两台机器)
代码实现
1、引入pom
<dependencies>
<!--集成springmvc框架并实现自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
2、定制化tomcat回调
CustomShutdown
/**
* @author lixiaoyi
* @description 定制shutdown 关闭逻辑
* 1、 先暂停所有请求
* 2、 等待现有线程处理完
* 3、 关闭线程
* @date 2021/7/22
**/
@Slf4j
public class CustomShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final int TIME_OUT = 30;
private volatile Connector connector;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
/* 暂停所有请求 */
this.connector.pause();
/* 获取tomcat的线程池 */
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
/* 关闭线程 (等待线程处理完之后)*/
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(TIME_OUT, TimeUnit.SECONDS)) {
log.warn("当前应用等待超过最大时长{}秒,将强制关闭", TIME_OUT);
/* Try shutDown Now*/
threadPoolExecutor.shutdownNow();
if (!threadPoolExecutor.awaitTermination(TIME_OUT, TimeUnit.SECONDS)) {
log.error("强制关闭失败", TIME_OUT);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
ShutdownConfig
@Configuration
public class ShutdownConfig {
@Bean
public CustomShutdown customShutdown() {
return new CustomShutdown();
}
@Bean
public ConfigurableServletWebServerFactory webServerFactory(final CustomShutdown customShutdown) {
TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
tomcatServletWebServerFactory.addConnectorCustomizers(customShutdown);
return tomcatServletWebServerFactory;
}
}
测试例子
@RestController
public class TestController {
@RequestMapping("/test")
public String test() throws InterruptedException {
System.out.println("test");
Thread.sleep(30000);
String s = System.currentTimeMillis()+"";
System.out.println("返回结果:"+s);
return s;
}
@RequestMapping("/test1")
public String test1() {
System.out.println("test1");
return "test1";
}
}
3、nginx配置
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#gzip on;
upstream backend {
server localhost:8888;
server localhost:8889;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://backend;
}
#error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
操作流程
业务请求: http://localhost/test 30s 返回结果
注释 nginx 配置 server localhost:8888;
刷新nginx配置 nginx -s reload
重新启动关闭应用
启动完成 重复 2 3 4 5 步骤,第二步注释8889的机器