http client客户端和Spring boot服务端长连接梳理

最近业务运行,出现了长连接的问题,整理了一下,记录下来。

环境:

<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-starter-testartifactId>
   <scope>testscope>
dependency>
<dependency>
   <groupId>org.springframework.bootgroupId>
   <artifactId>spring-boot-starter-webartifactId>
dependency>

<dependency>
    <groupId>org.apache.httpcomponentsgroupId>
    <artifactId>httpclientartifactId>
    <version>4.5.6version>
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();
    }
}

注:

  1. 客户端如果是用长连接发送的,端口会复用,可以用wireshark抓包看一下。
  2. 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

你可能感兴趣的:(java)