2.2.4.RELEASE 版本
- spring-boot 版本设置为
2.2.4.RELEASE
.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
- 新增
ajpConnector
配置。
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AjpAutoConfig {
private int ajpPort = 38001;
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() {
return tomcat -> {
if (tomcat instanceof TomcatServletWebServerFactory) {
Connector ajpConnector = new Connector("AJP/1.3");
ajpConnector.setPort(ajpPort);
tomcat.addAdditionalTomcatConnectors(ajpConnector);
}
};
}
}
启动服务,启动日志如下所示:
启动日志
- 通过
CPing
测试ajpConnector
配置是否已经生效。
Maven 依赖:
<!-- https://mvnrepository.com/artifact/com.github.jrialland/ajpclient -->
<dependency>
<groupId>com.github.jrialland</groupId>
<artifactId>ajpclient</artifactId>
<version>1.11</version>
</dependency>
测试代码:
package com.github.jrialland.ajpclient;
import com.github.jrialland.ajpclient.pool.Channels;
import io.netty.channel.Channel;
import java.util.concurrent.TimeUnit;
public class CPingDemo {
public static void main(String[] args) throws Exception {
//get a tcp connection
final Channel channel = Channels.connect("localhost", 38001);
//will try a cping/cpong exchange on the opened tcp connection
boolean success = new CPing(2, TimeUnit.SECONDS).execute(channel);
System.out.println(success);
}
}
运行结果如下图所示:
CPing 测试结果
优雅关闭 AJP Connector
- 新增一个
DelayShutdown
,来模拟关闭耗时。
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class DelayShutdown implements DisposableBean {
@Override
public void destroy() throws Exception {
log.info("start destroy....");
for (int i = 1; i < 20; ++ i) {
log.info("this is {} count.", i);
Thread.sleep(1000);
}
log.info("end destroy....");
}
}
从关闭 Spring 容器日志可以看出,在关闭过程中,CPing
请求还是会返回对应 CPong
响应。
说明关闭 Spring 容器过程中,
ajpConnector
还可以接收请求。
关闭 Spring 容器日志
- 新增
/shutdown
接口,来优雅关闭ajpConnector
。
@Slf4j
@RestController
public class DemoController {
@Autowired
private ApplicationContext context;
@Autowired
private TomcatServletWebServerFactory tomcatServletWebServerFactory;
@GetMapping("shutdown")
public void shutdown() throws Exception {
Connector ajpConnector = tomcatServletWebServerFactory.getAdditionalTomcatConnectors().get(0);
close(ajpConnector);
Thread.sleep(1000 * 10);
SpringApplication.exit(context);
}
private void close(Connector connector) {
connector.pause();
connector.getProtocolHandler().closeServerSocketGraceful();
}
}
-
pause()
方法:- (1)设置
Endpoint
的paused
状态为true
。 - (2)
Acceptor
在Endpoint
的paused=true
时不再建立新的socket
链接(即不再建立新的socket
连接) - (3)
AjpProcessor
在Endpoint
的paused=true
时,service()
不再处理当前socket
的新请求,会在上一个请求处理完毕后关闭当前socket
链接(即历史socket
不再接受新的请求)。
- (1)设置
-
closeServerSocketGraceful()
方法:- 因为其
Endpoint
的bindState
等于BOUND_ON_INIT
,所以do nothing
。
- 因为其
关闭 Spring 容器日志
从关闭 Spring 容器日志可以看出,在关闭过程中,CPing
请求不会再返回对应 CPong
响应。从而实现在关闭 Spring 容器过程中不能接受新请求。
2.3.10.RELEASE 版本
-
AjpAutoConfig
调整。
@Configuration
public class AjpAutoConfig {
private int ajpPort = 38001;
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> servletContainer() {
return tomcat -> {
if (tomcat instanceof TomcatServletWebServerFactory) {
Connector ajpConnector = new Connector("AJP/1.3");
ajpConnector.setPort(ajpPort);
((AbstractAjpProtocol) ajpConnector.getProtocolHandler()).setSecretRequired(false); // 2.3.10.RELEASE 新增
tomcat.addAdditionalTomcatConnectors(ajpConnector);
}
};
}
}
- 优雅关机通过
application.properties
中配置即可实现:
## 开启优雅停机, 如果不配置是默认IMMEDIATE, 立即停机
server.shutdown=graceful
## 优雅停机宽限期时间
spring.lifecycle.timeout-per-shutdown-phase=20s