设为首页 收藏本站
查看: 1084|回复: 0

[经验分享] 【BUG】org.apache.http.NoHttpResponseException

[复制链接]

尚未签到

发表于 2017-12-24 12:40:01 | 显示全部楼层 |阅读模式
  http://stackoverflow.com/questions/10570672/get-nohttpresponseexception-for-load-testing/10680629#10680629
  http://stackoverflow.com/questions/10558791/apache-httpclient-interim-error-nohttpresponseexception
  具体原由上面两个链接里都有, 希望帮助到有一定能力的朋友尽快找到解决方案.
  下面说说笔者出现这个问题的场景.

前提
  在用cas做sso的时候需要在认证中心填写表单后,再次对ticket校验吗,这是由客户端(应用端)发出的请求, 采用本文问题缘起的 HttpClients.
  这里用的是 httpclient-4.4.jar.

一次优化
  原来没什么想法, 只是用最简单的 CloseableHttpClient, 即 HttpClients.createDefault() 构造的. 很显然,在这种最简单版本背后是对每一个校验程序都生成一个 http client, 这应该是可以优化的.
  于是采用了连接池的思路, 本着 apache 无所不能的本位思想, 鼓捣了一番,有了下面这段代码. 这里连接池的缺省配置: defaultMaxPerRoute : 2, maxTotal : 20.
  

private final CloseableHttpClient httpClient = HttpClients.createMinimal(new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()  .register(
"http", PlainConnectionSocketFactory.getSocketFactory())  .register(
"https", SSLConnectionSocketFactory.getSocketFactory())  .build(),
new SystemDefaultDnsResolver() {  @Override
public InetAddress[] resolve(final String host) throws UnknownHostException {  logger.info(
"using httpClient env is {}", env);if (host.equalsIgnoreCase("test.cas.com")) {// test.cas.com 是笔者配的内网测服hosts,不必在意.return new InetAddress[]{InetAddress.getByName("192.168.0.1")};  }
else {return super.resolve(host);  }
  }
  }));
  

  这里 用的是 httpclient-4.4.jar.
  好像这段代码没什么, 可是在部署到线上环境时, 偶尔会发生题目中描述的异常也即 NoHttpResponseException, 具体的原因下面慢慢解释:
  首先 PoolingHttpClientConnectionManager 的注释很直观, 即

  * {@code ClientConnectionPoolManager} maintains a pool of
  * {@link HttpClientConnection}s and is able to service connection requests
  * from multiple execution threads. Connections are pooled on a per route
  * basis. A request for a route which already the manager has persistent
  * connections for available in the pool will be services by leasing
  * a connection from the pool rather than creating a brand new connection.

  就是说这个连接池对每一个固定请求host地址都使用了持久化连接, 在第一次访问之后的相同路径请求就会使用已经持久化的连接, 这也正是持久化连接的定义.
  我们可以再看一下官方的解释:


  HTTP/1.1 states that HTTP connections can be re-used for multiple requests per default. HTTP/1.0 compliant endpoints can also use a mechanism to explicitly communicate their preference to keep connection alive and use it for multiple requests. HTTP agents can also keep>
  可以得出一个废话结论: 连接池内的连接初始化后提供的都是持久化连接.
  然而, 持久化连接在这里会有什么问题呢?

bug 来了
  stackoverflow 上针对这一现象有解释如下:


  Most likely persistent connections that are kept alive by the connection manager become stale. That is, the target server shuts down the connection on its end without HttpClient being able to react to that event, while the connection is being>  via oleg

  即那些持久化连接用了一次或几次后就会变得 stale (老化?), 这是由于这些连接在保持握手后, 直接在通信的另一方给关闭了, 此时请求方还没来的即响应这次取消, 那这个连接有恢复闲置状态, 因此这次请求就转为 stale 或者 half-closed 状态.
  大体上可以推测出是一方A使用持久化连接, 一次通信的结束时B方觉得响应输出完毕, 那么就该直接关了socket, 这也不管这个响应到没到A方, A方然后就发现这连接直接被关了...居然也不知道咋对待. 那么, throw NoHttpResponseException.
  上面引用的文字里也有提到: 接收响应的写操作由于 SocketException 失效后会有重试机制. 哈哈, 解决方案有了.(特定的恶劣情况还是不可控的)

解决方案
  

private final CloseableHttpClient httpClient2 = HttpClientBuilder.create().setConnectionManager(new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()  .register(
"http", PlainConnectionSocketFactory.getSocketFactory())  .register(
"https", SSLConnectionSocketFactory.getSocketFactory())  .build(),
new SystemDefaultDnsResolver() {  @Override
public InetAddress[] resolve(final String host) throws UnknownHostException {  logger.info(
"using httpClient2 env is {}", env);if (host.equalsIgnoreCase("test.cas.com")) {// test.cas.com 是笔者配的内网测服hosts,不必在意.  return new InetAddress[]{InetAddress.getByName("192.168.0.106")};
  } else {
  return super.resolve(host);
  }
  }
  })).setRetryHandler(new HttpRequestRetryHandler() {
  @Override
  public boolean retryRequest(IOException exception, int executionCount,
  HttpContext context) {
  if (executionCount > 3) {
  logger.warn("Maximum tries reached for client http pool ");
  return false;
  }
  if (exception instanceof NoHttpResponseException) {
  logger.warn("No response from server on " + executionCount + " call");
  return true;
  }
  return false;
  }
  }).build();
  

  上文中的 HttpRequestRetryHandler 也很直观, 使用上保证线程安全即可. 具体如下:
  

/**  * A handler for determining if an HttpRequest should be retried after a
  * recoverable exception during execution.
  * <p>
  * Implementations of this interface must be thread-safe. Access to shared
  * data must be synchronized as methods of this interface may be executed
  * from multiple threads.
  *
  *
@since 4.0*/  
public interface HttpRequestRetryHandler {
  

  /**
  * Determines if a method should be retried after an IOException
  * occurs during execution.
  *
  * @param exception the exception that occurred
  * @param executionCount the number of times this method has been
  * unsuccessfully executed
  * @param context the context for the request execution
  *
  * @return {@code true} if the method should be retried, {@code false}
  * otherwise
  */
  boolean retryRequest(IOException exception, int executionCount, HttpContext context);
  

  
}
  

  thx reading & hope enjoy ~

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-427502-1-1.html 上篇帖子: apache+php+mysql运行环境 下篇帖子: Apache POI解析excel文件
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表