Spring 教程

Spring 笔记

spring cloud http 客户端连接使用 apache http client 连接池配置

Spring 笔记 Spring 笔记


Spring Cloud 服务化之间的通信用 http 协议,它的 http client 组件支持 3 种形式,默认情况下,采用 JDK 的 HttpURLConnection,它比较低效,每次请求都建立一个新连接,此外,还可以使用 apache 的 httpclient 或 square 公司开源的 okhttp client。

apache http client 引入

Spring Cloud 的 http client 是 feign 层使用,ribbon 的 LoadBalancerFeignClient 拿到对应的 http client 使用。

maven 构建的项目中,在 pom.xml 引入:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

gradle 构建的项目中,在 build.gradle 引入:

compile group: 'io.github.openfeign', name: 'feign-httpclient'

feign-httpclient 这里可以不指定版本,它会根据 spring cloud dependencies 依赖导入相应版本。

配置 application.yml

feign 中使用 http client 的优先顺序可以查看源码 FeignRibbonClientAutoConfiguration 类,该类上的 @Import 注解上的 FeignLoadBalancedConfiguration 导入顺序就是优先顺序,笔者的版本示例如下:

@Import({ HttpClientFeignLoadBalancedConfiguration.class,
		OkHttpFeignLoadBalancedConfiguration.class,
		DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
    ...
}

如上源代码中可以看出,它会优先适配 apache http client,然后 okhttp,最后是默认的 jdk http client。

根据源码逻辑可以得出,为了确保 httpclient 的使用,最好显性地禁止 okhttp(万一其他依赖导入 okhttp),application.yml 增加如下配置:

feign:
  httpclient:
    enabled: true
  okhttp:
    enabled: false

配置 HttpClient 连接池

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

@Configuration
public class HttpClientPoolConfig {

    @Bean
    public HttpClient httpClient() {

        /**
         * 生成默认请求配置
         */
        RequestConfig requestConfig = RequestConfig.custom()
                //  连接超时时间
                .setConnectTimeout(5 * 1000)
                //  socket 超时时间
                .setSocketTimeout(5 * 1000)
                .build();

        /**
         * 连接池配置
         */
        // 长连接保持30秒
        final PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
                new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        //  总连接数
        poolingHttpClientConnectionManager.setMaxTotal(5000);
        //  同路由的并发数
        poolingHttpClientConnectionManager.setDefaultMaxPerRoute(100);

        //  httpclient
        HttpClient httpClient = HttpClientBuilder.create()
                //  保持长连接配置,需要在头添加 Keep-Alive
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
                .setConnectionManager(poolingHttpClientConnectionManager)
                .setDefaultRequestConfig(requestConfig)
                .build();


        /**
         * 启动定时器,定时回收过期的连接
         */
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                poolingHttpClientConnectionManager.closeExpiredConnections();
                poolingHttpClientConnectionManager.closeIdleConnections(5, TimeUnit.SECONDS);
            }
        }, 10 * 1000, 5 * 1000);

        return httpClient;
    }

}