此文已由作者赵慧莉授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
最近在做内容分发网络(Content Delivery Network,简称 CDN)CDN的后端线上回归集监控时,常常出现连续执行多个用例时会报“org.apache.http.NoHttpResponseException”错误,而单个执行一个用例就不会报这种错误。经过分析为什么接口测试时不会出现这种问题,而线上回归的时候就会触发这种错误呢?原因是由于线上的回归集是为了验证实际的情况要检查CDN是否部署成功,所以线上一个用例的执行时间是30分钟之上,而线下接口测试只需要毫秒级别。接下来我将介绍下为什么会出现“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; } } };
通过(三)中的方式可以解决“org.apache.http.NoHttpResponseException”报错的问题,但是考虑到实际的应用场景不适合此种方法。因为一次请求到下次请求之间的间隔为30分钟以上,不适合将连接时间改为30分钟以上(因为很多服务器都会在不通知客户端的情况下,关闭一定时间内不活动的连接,来节省服务器资源)。 修改测试代码为不在@BeforeClass时进行创建实例,而是在每个测试用例里面进行创建实例来规避的解决这种问题。
为了定位上述的报错问题,需要了解使用HttpClient请求一个Http请求的步骤,以及如何自定义连接存活策略。在查到解决办法后要考虑是否真正适合我们的应用场景,以及采用了这种方式是否会造成资源浪费,最终选择适合的方式来规避的解决这个问题。
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 2018中国云原生用户大会:网易云深度解析微服务框架