rest-template是spring对httpclient的逻辑封装,它底层还是基于httpclient,所以一些配置其实跟httpclient是强相关的。
基本配置
rest-template可以不带参数,使用默认配置,也可以指定ClientHttpRequestFactory参数,ClientHttpRequestFactory是一个interface,其具体实现有:
- HttpComponentsClientHttpRequestFactory,包含一个apache的HttpClient实例
- OkHttp3ClientHttpRequestFactory,包含一个OkHttpClient的实例
以HttpComponentsClientHttpRequestFactory为例,它接受一个apache的HttpClient实例,我们可以定制其配置,样例代码如下:
return HttpClients.custom()
.setMaxConnTotal(10000) // 连接池的最大连接数
.evictIdleConnections(10, TimeUnit.MINUTES) // 连接池内的连接空闲超出该时间即销毁
.setConnectionTimeToLive(15, TimeUnit.MINUTES) //连接在15分钟后过期,默认-1表示永不过期
.evictExpiredConnections() //启动过期连接的检测线程,勿漏掉!
.setMaxConnPerRoute(2000) //单域名允许的最大连接数
.setDefaultRequestConfig(buildRequestConfig()) // 连接配置见下
.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true)) //失败后重试次数;重试时是否需要重新发送请求
.build();
RequestConfig属于基本的连接配置,如下:
return RequestConfig.custom()
.setConnectTimeout(5) // TCP连接超时
.setConnectionRequestTimeout(60) //从连接池获取连接的超时
.setSocketTimeout(60) // 请求等待超时
.build();
顺带说一下一个TCP连接耗费的内存数(参考这里),使用
sysctl -A | grep net | grep mem
命令可以查看一个TCP连接的读写缓存默认大小,在我的ubuntu上大约200~300K,注意只是默认大小,随着连接数增大,读写缓存大小是会降低的。像上面的例子,若10000个连接都是忙碌的,内存应该会达到G级别(实际则不会,总有部分连接是空闲的)。
连接有效性问题
我们在spring后台开发里一般用的长连接去访问三方服务,这样可避免TCP握手的开销。长连接是可复用的,一般用连接池管理,像apache httpclient里就有一个连接池管理类:PoolingHttpClientConnectionManager。
当我们在代码里关闭一个连接时,该连接并不真的释放,而是被放回连接池里。下次有请求到来,该连接会被取出再次使用。那么,我们如何保证再次取出的连接是有效的呢?
apache httpclient提供了2个选项:
- 设置TTL(timeToLive),对应上一节的setConnectionTimeToLive
- 空闲连接清理,对应上一节的evictIdleConnections
apache httpclient会起一个单独的线程去检查连接池里的连接是否过期,相应的机制在IdleConnectionEvictor里,检测线程会分别检查每个连接的TTL和空闲时间,及时将连接池里的无效连接清理掉。
但上述机制并非100%可靠,现网情况千奇百怪,总有例外情形,所以使用连接池必须要配置retry机制,否则网络故障、限流等都可能造成无效连接,在无效连接上发送或接收数据,都会报connection reset错误。
关于连接池的具体分析,可参考我的另一篇文章:
https://blog.youkuaiyun.com/tlxamulet/article/details/146562844?spm=1011.2415.3001.5331
安全配置
httpclient需指定SSLContext,所谓SSLContext,就是对接的SSL协议,像SSLv2、SSLv3、TLSv1、TLSv1.1、TLSv1.2、TLSv1.3等,它规定了握手和数据加密传输的一套安全算法,最新最安全的是TLSv1.3,当前多数浏览器和服务器都支持了,不过TLSv1.2还在广泛使用。
生成一个最简单的SSLContext的代码样例:
X509TrustManager x509TrustManager = new X509TrustManagerImpl();
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, new X509TrustManager[] {x509TrustManager}, randomInstance);
return sslContext;
这里,X509TrustManagerImpl可以是一个空实现,不对client和server证书做任何校验。