okhttp连接问题
起因
由于公司降本增效的要求,手头的视频转码服务需要提高压缩率,于是对接了某云厂商的媒体处理服务。具体对接方式是通过调用云厂商的http接口发送任务,然后等待媒体处理完成回调。媒体处理本身没有问题,但是在正式上线运行后发现,常常有请求超时,15s都无法完成,具体如下图:
排查过程
- 首先怀疑接口耗时,找对方服务端排查并没发现耗时很久,且从上面的请求来看不像是服务端故障。
- 然后怀疑网络问题,但是在机器上ping对方的域名,并未发现严重的超时。
- 最后怀疑客户端问题,开始查看某云厂商提供的sdk源码,重点观察http请求提交的部分。问题果然处在这里。
问题概述
他们的sdk的http请求部分代码,每次请求新建一个HttpConnection
,看起来没什么问题,每次请求新建一个http连接。
private String getResponseBody(String url, HashMap<String, String> headers, byte[] body) throws TencentCloudSDKException {
HttpConnection conn = new HttpConnection(this.profile.getHttpProfile().getConnTimeout(), this.profile.getHttpProfile().getReadTimeout(), this.profile.getHttpProfile().getWriteTimeout());
conn.addInterceptors(this.log);
//...
Response resp = conn.postRequest(url, body, hb.build());
//...
}
来看看这个HttpConnection
里面做了什么,每次都新建了一个OkHttpClient
,一看就觉得有问题,因为一个OkHttpClient
里面维护的是一个连接池,这种使用方式相当于每次新建一个连接池。但是这应该只会带来更多垃圾回收的消耗,在项目刚上线请求量低的情况下应该不会出什么问题。让我们再看看OkHttpClient
做了什么。
public class HttpConnection {
private OkHttpClient client = new OkHttpClient();
public HttpConnection(Integer connTimeout, Integer readTimeout, Integer writeTimeout) {
this.client.setConnectTimeout((long)connTimeout, TimeUnit.SECONDS);
this.client.setReadTimeout((long)readTimeout, TimeUnit.SECONDS);
this.client.setWriteTimeout((long)writeTimeout, TimeUnit.SECONDS);
}
}
每次请求时新建一个Call
类,call里面会检查client,如果有没设置的参数,会使用默认值设置,看到实际运行时会这样创建默认连接池,最大空闲连接=5,检查时间5min,即最大可以持有5个空闲连接,5分钟检查清理空闲连接。但是按每次新建一个的使用方式,相当于每次都新建一个tcp连接,但不释放,运行一段时间后,客户端将于服务端建立许多无用的tcp连接,导致新有用的连接无法建立,因而在client端造成SocketTimeout
ConnectionPool conn = new ConnectionPool(5, 5 * 60 * 1000);
修改
- 不要每次都创建一个
HttpConnection
即OkHttpClient
,所有请求共用连接池 -
连接池指定空闲连接数=0,即全部使用短连接,转码场景任务请求不频繁,没必要使用长连接
ConnectionPool conn = new ConnectionPool(0, 60);