HttpClient报错 org.apache.http.NoHttpResponseException : 10.1.1.0:13001 failed to respond 解决
  gE4dSMi8Swcx 2023年11月07日 111 0

一、问题描述

使用 HttpClient 并发调用http 接口,并发量稍微大一点就会报错

org.apache.http.NoHttpResponseException : 10.1.1.0:13001 failed to respond 

二、排查过程

  1. 最开始怀疑是服务端连接过多,拒绝请求了,监控发现服务端并没有多少连接
  2. 找运维搭建了一个新环(只有我们请求服务端),发现只要并发大一点,必定会出现此错误
  3. 网上一通找,主要有这几个解决方法

a、添加自定义重试策略,在重试策略中判断异常如果是NoHttpResponseException,就重试

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setRetryHandler(new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (exception instanceof NoHttpResponseException) {
                    //NoHttpResponseException 异常重试
                    return true;
                }
                
                if (executionCount >= 3) {
                    return false;
                }
                
                return false;
            }

b、减小validateAfterInactivity配置时间(这个配置是在从连接池获取连接时校验连接可用的间隔时间,默认2s)

PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(
            socketFactoryRegistry);
poolingHttpClientConnectionManager.setValidateAfterInactivity(1);

     c、直接关闭连接重用,即 

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionReuseStrategy(new DefaultConnectionReuseStrategy(){
            @Override
            public boolean keepAlive(HttpResponse response, HttpContext context) {
              //不重用连接
                return false;
            }
        });
  1. 但可惜,这三种都不能最终非常优雅的解决问题
  1. 重试虽然能解决这种问题,但是网络存在问题也可能会出现这种问题,可能请求已经发到服务器上了,只是最后响应没有写回,重试可能导致重复请求,存在风险,pass 掉
  2. 减小这个配置,虽然可以减低报错概率,但是并发非常大或者说请求很频繁的话,还是存在较低概率发生报错,pass 掉
  3. 直接不复用连接,那 httpclient 连接池的作用就不大了,影响性能,pass 掉(如果没有更好的办法就使用这个)
  1. 最后通过debug客户端和服务端的代码,发现服务端居然是短链接,每次返回响应就主动断掉连接(服务端使用的是 Netty Http服务)
  2. 有同事说反正服务端是短链接,那我们就使用第三种方案得了。但是我们不是只调用一个服务,其他好几个服务也是通过这个连接池调用的,可以说是一个工具类,不能直接这样改掉
  3. 最终采取了一个折中方案,采用第二、第三种方案结合的方式,解决此问题
  1. 自定义重用连接的策略
  2. 如果服务端的响应 header 带了 Connection=close,则不复用连接
  3. 如果服务端连接已不可用,则不复用连接(即调用第二种方式去校验连接是否可用)
  4. 其他情况复用连接
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setConnectionReuseStrategy(CustomClientConnectionReuseStrategy.INSTANCE);

public class CustomClientConnectionReuseStrategy extends DefaultConnectionReuseStrategy {
    public static final CustomClientConnectionReuseStrategy INSTANCE = new CustomClientConnectionReuseStrategy();

    @Override
    public boolean keepAlive(final HttpResponse response, final HttpContext context) {
        final HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST);
        if (request != null) {
            final Header[] connHeaders = request.getHeaders(HttpHeaders.CONNECTION);
            if (connHeaders.length != 0) {
                final TokenIterator ti = new BasicTokenIterator(new BasicHeaderIterator(connHeaders, null));
                while (ti.hasNext()) {
                    final String token = ti.nextToken();
                    if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
                        //请求头自带 connection:close,不复用连接
                        return false;
                    }
                }
            }
        }

        final Object obj = context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
        if (obj instanceof HttpConnection) {
            //连接是否已经关闭或者失效,不复用连接
            return !((HttpConnection) obj).isStale();
        }

        return super.keepAlive(response, context);
    }

}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  SuVXIKXQi51s   2023年12月23日   99   0   0 pythonHTTPHTTPPython
gE4dSMi8Swcx