OkHttpClient:IO问题排查
问题描述
我们有个需求需要获取上传至oss图片的宽高信息,在原有文件链接后面加上后缀?x-oss-process=image/info,然后使用OkHttpClient组装get请求获取数据。在测试环境测试的时候,发现会出现偶现的IOException报错
java.io.IOException: unexpected end of stream on Connection{, proxy=DIRECT hostAddress= cipherSuite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 protocol=http/1.1}
问题排查
- 通过搜索资料发现这个问题的解释:The error occurs when OkHttp try to reuse a connection that is in FIN_WAIT2 state in server,because the server keep_alive timeout is lesser than the client timeout.
简单翻译下,可以知道是OkHttp重复使用了上一次处于FIN_WAIT2状态的连接,导致报错 - 那么在TCP三次握手中什么情况下会出现FIN_WAIT2这个状态:
主动一方发起FIN报文,只要对端发送ACK确认后主动方就会处于FIN_WAIT2状态,然后等待对端发送FIN报文,如果一直没有发送FIN报文(就会一直处于CLOSE_WAIT状态,还有数据要发送,等等再关闭),那么主动一方就可能永远处于FIN_WAIT2状态
问题解决
- 第一种解决方案:
在http1.1中,client和server都是默认对方支持长链接的, 如果client使用http1.1协议,但又不希望使用长链接,则需要在header中指明connection的值为close;如果server方也不想支持长链接,则在response中也需要明确说明connection的值为close。不论request还是response的header中包含了值为close的connection,都表明当前正在使用的tcp链接在请求处理完毕后会被断掉。以后client再进行新的请求时就必须创建新的tcp链接了。 - 第二种解决方案:
设置retryOnConnectionFailure=true,即连接失败时允许重连
OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory(), x509TrustManager())
.retryOnConnectionFailure(true)
.connectionPool(pool())//连接池
.connectTimeout(3L, TimeUnit.SECONDS)
.readTimeout(3L, TimeUnit.SECONDS)
.build();