最近业务运行,出现了长连接的问题,整理了一下,记录下来。
环境:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
HttpClient 长连接问题
长连接需要客户端和服务端都进行设置,才能起到作用。默认生成的http client使用了长连接的strategy。所以也可以在客户端设置长连接的断开逻辑。
ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainSocketFactory)
.register("https", sslSocketFactory).build();
manager = new PoolingHttpClientConnectionManager(registry);
manager.setMaxTotal(MAX_CONN); // 线程池最大数量
manager.setDefaultMaxPerRoute(Max_PRE_ROUTE); // 相同路由请求的最大连接数量
manager.setMaxPerRoute(new HttpRoute(httpHost), MAX_ROUTE);
/**
* 如果想自定义keep-alive逻辑,这个逻辑参考DefaultConnectionKeepAliveStrategy
* -1 好像是不超时
* 默认的也是设置这个keep-alive逻辑
* ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
* if (keepAliveStrategyCopy == null) {
* keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
* }
*/
clientBuilder.setKeepAliveStrategy((httpResponse, httpContext) -> {
BasicHeaderElementIterator it = new BasicHeaderElementIterator(httpResponse.headerIterator("Keep-Alive"));
while(true) {
String param;
String value;
do {
do {
if (!it.hasNext()) {
return -1L;
}
HeaderElement he = it.nextElement();
param = he.getName();
value = he.getValue();
} while(value == null);
} while(!param.equalsIgnoreCase("timeout"));
try {
return Long.parseLong(value) * 1000L;
} catch (NumberFormatException var8) {
;
}
}
});
Spring boot 长连接问题
spring 使用内嵌的tomcat 是不支持,修改keep-alive逻辑的。所以在tomcat初始化的时候修改。
参考 spring boot配置官方文档 看了一遍,确实没有支持keep-alive的设置
上代码:
@Configuration
public class TomcatConfig {
@Bean
public EmbeddedServletContainerFactory createEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
// tomcat的配置可以在这里加
// public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
tomcatFactory.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
//设置最大连接数
protocol.setKeepAliveTimeout(10 * 1000);
protocol.setMaxKeepAliveRequests(100);
});
return tomcatFactory;
}
}
源码
/**
* Keepalive timeout, if not set the soTimeout is used.
*/
private Integer keepAliveTimeout = null;
public int getKeepAliveTimeout() {
if (keepAliveTimeout == null) {
return getSoTimeout();
} else {
return keepAliveTimeout.intValue();
}
}
注:
- 客户端如果是用长连接发送的,端口会复用,可以用wireshark抓包看一下。
- iptables设置简单的负载均衡逻辑,主机的8080端口进行转发。
iptables -t nat -A PREROUTING -p tcp --dport 8080 -m statistic --mode random --probablity 0.22222 -j REDIRECT --TO-PORTS 5000
iptables -t nat -A PREROUTING -p tcp --dport 8080 -m statistic --mode random --probablity 0.22222 -j REDIRECT --TO-PORTS 5001
iptables -t nat -A PREROUTING -p tcp --dport 8080 -m statistic --mode random --probablity 0.22222 -j REDIRECT --TO-PORTS 5002
版权声明:本文为u011426341原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。