如何自定义长连接策略

叁叁肆2018-11-16 10:50

此文已由作者赵慧莉授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

一、前言


最近在做内容分发网络(Content Delivery Network,简称 CDN)CDN的后端线上回归集监控时,常常出现连续执行多个用例时会报“org.apache.http.NoHttpResponseException”错误,而单个执行一个用例就不会报这种错误。经过分析为什么接口测试时不会出现这种问题,而线上回归的时候就会触发这种错误呢?原因是由于线上的回归集是为了验证实际的情况要检查CDN是否部署成功,所以线上一个用例的执行时间是30分钟之上,而线下接口测试只需要毫秒级别。接下来我将介绍下为什么会出现“org.apache.http.NoHttpResponseException”,以及如何解决这种问题。


二、出现“org.apache.http.NoHttpResponseException”的原因


HttpClient的实现类为CloseableHttpClient。创建CloseableHttpClient实例有两种方式: (1)使用CloseableHttpClient的工厂类HttpClients的方法来创建实例。HttpClients提供了根据各种默认配置来创建CloseableHttpClient实例的快捷方法。最简单的实例化方式是调用HttpClients.createDefault()。 (2)使用CloseableHttpClient的builder类HttpClientBuilder,先对一些属性进行配置,再调用build方法来创建实例。上面的HttpClients.createDefault()实际上调用的也就是HttpClientBuilder.create().build()。 但是在测试的时候连续执行多个方法的时候就会报错,直接执行其中的一个测试方法就不会有这种问题:


java.lang.AssertionError: org.apache.http.NoHttpResponseException: nos-yq-yanlian.netease.com:8080 failed to respond
    at org.testng.Assert.fail(Assert.java:89)
    at com.netease.pubncdn.test.testcase.OnlineDomainDeployTest.testCreateHttpDomain(OnlineDomainDeployTest.java:192)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

经过查询资料发现:Http规范没有规定一个持久连接应该保持存活多久。有些Http服务器使用非标准的Keep-Alive头消息和客户端进行交互,服务器端会保持数秒时间内保持连接。HttpClient也会利用这个头消息。如果服务器返回的响应中没有包含Keep-Alive头消息,HttpClient会认为这个连接可以永远保持。然而,很多服务器都会在不通知客户端的情况下,关闭一定时间内不活动的连接,来节省服务器资源。在某些情况下默认的策略显得太乐观,我们可能需要自定义连接存活策略。


三、自定义长连接策略的方法


通过如下代码可以自定义连接存活策略:


CloseableHttpClient client = HttpClients.custom()  
        .setKeepAliveStrategy(keepAliveStrategy)  
        .build();
ConnectionKeepAliveStrategy keepAliveStrategy = new ConnectionKeepAliveStrategy() {  
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {  
        // Honor 'keep-alive' header  
        HeaderElementIterator it = new BasicHeaderElementIterator(  
                response.headerIterator(HTTP.CONN_KEEP_ALIVE));  
        while (it.hasNext()) {  
            HeaderElement he = it.nextElement();  
            String param = he.getName();  
            String value = he.getValue();  
            if (value != null && param.equalsIgnoreCase("timeout")) {  
                try {  
                    return Long.parseLong(value) * 1000;  
                } catch(NumberFormatException ignore) {  
                }  
            }  
        }  
        HttpHost target = (HttpHost) context.getAttribute(  
                HttpClientContext.HTTP_TARGET_HOST);  
        if ("www.baidu.com".equalsIgnoreCase(target.getHostName())) {  
            // Keep alive for 5 seconds only  
            return 5 * 1000;  
        } else {  
            // otherwise keep alive for 30 seconds  
            return 30 * 1000;  
        }  
    }  
};

四、CDN中如何解决报错问题


通过(三)中的方式可以解决“org.apache.http.NoHttpResponseException”报错的问题,但是考虑到实际的应用场景不适合此种方法。因为一次请求到下次请求之间的间隔为30分钟以上,不适合将连接时间改为30分钟以上(因为很多服务器都会在不通知客户端的情况下,关闭一定时间内不活动的连接,来节省服务器资源)。 修改测试代码为不在@BeforeClass时进行创建实例,而是在每个测试用例里面进行创建实例来规避的解决这种问题。


五、总结


为了定位上述的报错问题,需要了解使用HttpClient请求一个Http请求的步骤,以及如何自定义连接存活策略。在查到解决办法后要考虑是否真正适合我们的应用场景,以及采用了这种方式是否会造成资源浪费,最终选择适合的方式来规避的解决这个问题。


免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击


相关文章:
【推荐】 2018中国云原生用户大会:网易云深度解析微服务框架