《实战录》导语
云端卫士的新栏目《实战录》将会定期分享一些我们的工程师伙伴们在产品研发的过程中总结的实践经验,希望对于热爱技术且关注安全领域的受众有所裨益。本期分享人为云端卫士安全运营工程师许广辉,将带来HttpClient的超时设置的分享。
HttpClient是Apache旗下开源HTTP协议客户端库,基于她可以很方便地编写HTTP类型服务的客户端。但我们在使用过程中发现,如果不合理设置一些超时时间,客户端的行为将严重受到所在OS平台和访问服务质量的影响,进而影响整个客户端的正确性和稳定性.本文从项目中实际遇到的问题出发,分享如何设置HttpCleint的超时时间,使编写的程序能按照我们期望的方式工作。
如果HTTP服务不可用,HttpClient默认什么时候返回异常?
在客户端连接服务的过程中,如果服务方没有开启服务或者从客户端所在网络根本无法访问服务,HttpClient将会抛出无法连接的异常,但如果没有设置connectTimeout,抛异常的时间将随客户端所在OS平台的不同有很大的区别,我们测试了Windows平台和Linux平台,通过抓包发现:
windows平台在尝试重传syn 2次后报告连接失败,耗时大约3秒
linux平台会尝试重传syn 6次,每次尝试的时间间隔以指数方式增长,最终耗时长达65秒
这是因为Jvm最终需要调用OS的系统调用来创建tcp socket,不同OS的tcp协议栈实现不同,导致了这种差别。对于我们的客户端来讲,我们需要它在各种平台的行为一致,因此HttpClient的默认行为不可取,可以在HttpClient创建的时候设定连接超时时间connectTimeout来覆盖默认行为,这个值最终也是通过传递到系统调用生效.设定的方法如下:
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout) /* 设置建立连接超时时间 */
.build();
HttpClient client = HttpClients.custom()
.setDefaultRequestConfig(config)
.build();
客户端发出请求后,如果服务端没有给出响应(出现bug等原因),HttpClient默认怎么办?
在我们使用的实际场景中,由于HTTP服务出现了罕见的一个bug,导致在接收请求后,没有发回响应,这种情况下客户端的默认行为是一直等待,我们发现三个客户端从凌晨0点左右发出了请求,但直到第二天早上9点半,通过日志发现客户端依然在等待。用ss(或者netstat)命令发现客户端与服务的连接是ESTABLISHED状态,这种行为已经严重影响了客户端的业务逻辑,因此必须解决.方法是设置socketTimeout,方法如下:
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout) /* 设置建立连接超时时间 */
.setSocketTimeout(timeout) /* 设置等待数据超时时间 */
.build();
HttpClient client = HttpClients.custom()
.setDefaultRequestConfig(config)
.build();
socketTimtout表示等待数据的时间,超过该时间将抛超时异常。在我们的场景中,HttpClient发出请求后,等待的时间不能超过socketTimtout,从而解决了因服务问题导致客户端hang的问题。