【问题标题】:Rest Template org.apache.http.NoHttpResponseException休息模板 org.apache.http.NoHttpResponseException
【发布时间】:2018-07-17 11:02:31
【问题描述】:

我有两个 Spring Boot 服务 A 和 B。还有一个外部服务 C。 那是请求路径:

Web 浏览器 服务 A 服务 B 外部服务 C

外部服务正在返回一个返回到前端的资源。对于 A、B 和 C 之间的通信,我使用的是 Rest Template。 进入 Web 应用程序时一切正常,但一旦我运行并行运行的 BDD 测试(9 个线程),我在调用外部服务 C 时在服务 B 中得到 NoHttpResponseException。

org.apache.http.NoHttpResponseException Service_C failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)

这是我的 Rest Template 配置:

    @Bean
public RestTemplate restTemplateExternal() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    HttpComponentsClientHttpRequestFactory requestFactory = getRequestFactoryWithDisabledSSLValidation();
    RestTemplate restTemplate = new RestTemplate(requestFactory);

    return restTemplate;
}

private HttpComponentsClientHttpRequestFactory getRequestFactoryWithDisabledSSLValidation() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = SSLContexts.custom()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .build();

    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);

    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();

    CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setSSLSocketFactory(csf)
            .build();

    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();

    requestFactory.setHttpClient(httpClient);

    return requestFactory;
}

我已经尝试拨打connectionManager.setValidateAfterInactivity(0);,但没有帮助。

让我补充一点,从服务 B 到外部服务 C 的所有请求都到同一个端点。只有参数(x)在变化:/resource?param={x}

老实说,我不能 100% 确定是否会在每次服务请求时创建 HttpClient(RestTemplate bean 是单例)还是每个服务只有一个实例?

也许我需要在连接管理器中设置“setDefaultMaxPerRoute”?如果是,那么我如何区分正确的数字是什么?我真的很感激在这种情况下如何正确配置 RestTemplate 的简要说明。

【问题讨论】:

    标签: java spring spring-boot resttemplate


    【解决方案1】:

    看起来这是一个类似的问题:get NoHttpResponseException for load testing

    尝试使用clientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true)); 并成功了。如果有人能更深入地解释这个问题,我仍然很感激。

    【讨论】:

    • 连接管理器保持活动状态的持久连接很可能变得陈旧。也就是说,目标服务器在 HttpClient 无法对该事件做出反应的情况下关闭其端的连接,而连接处于空闲状态,从而使连接处于半关闭状态或“陈旧”状态。
    【解决方案2】:

    问题可能是因为响应包含标头connections=close。因此一个连接被关闭,但下一个请求尝试重用现有连接(已关闭)并得到错误。

    setRetryHandler 在第一次连接重用时总是会失败,但在第二次重试时会启动一个新连接,然后就会成功。

    您可以使用以下行禁止重复使用连接: httpClient.setReuseStrategy(new NoConnectionReuseStrategy());

    【讨论】:

      【解决方案3】:

      如果你想在日志中看到重试,你可以使用这样的重试解决方案:

      private void addRetryHandler( HttpClientBuilder httpClientBuilder ) {
          logger.debug("adding retry handler to httpClient");
          httpClientBuilder.setRetryHandler(( exception, executionCount, context ) -> {
              if (executionCount > 3) {
                  logger.debug("Maximum http request tries reached for client http pool ");
                  return false;
              }
              if (exception instanceof org.apache.http.NoHttpResponseException) {
                  logger.debug("No response from server on {} call, got NoHttpResponseException", executionCount);
                  return true;
              }
              return false;
          });
      }
      

      【讨论】:

        【解决方案4】:

        你可以尝试启用更多额外的日志记录,以便从服务器搜索隐藏的异常。 服务器好像断开了连接。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-01-24
          • 2014-02-28
          • 2018-12-17
          • 2015-11-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多