Spring Cloud 服务化之间的通信用 http 协议,组件支持 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;
}
}