okhttp连接问题

okhttp连接问题与空闲连接管理

Posted by Link on September 20, 2022

okhttp连接问题

起因

由于公司降本增效的要求,手头的视频转码服务需要提高压缩率,于是对接了某云厂商的媒体处理服务。具体对接方式是通过调用云厂商的http接口发送任务,然后等待媒体处理完成回调。媒体处理本身没有问题,但是在正式上线运行后发现,常常有请求超时,15s都无法完成,具体如下图:

超时

排查过程

  1. 首先怀疑接口耗时,找对方服务端排查并没发现耗时很久,且从上面的请求来看不像是服务端故障。
  2. 然后怀疑网络问题,但是在机器上ping对方的域名,并未发现严重的超时。
  3. 最后怀疑客户端问题,开始查看某云厂商提供的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);

修改

  1. 不要每次都创建一个HttpConnectionOkHttpClient,所有请求共用连接池
  2. 连接池指定空闲连接数=0,即全部使用短连接,转码场景任务请求不频繁,没必要使用长连接

         ConnectionPool conn = new ConnectionPool(0, 60);