当前位置: 首页 > 知识库问答 >
问题:

Rest模板组织。阿帕奇。http。NoHttpResponseException

蒙才
2023-03-14

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

网络浏览器

外部服务正在返回一个返回前端的资源。为了在A、B和C之间进行通信,我使用了Rest模板。进入Web应用程序时一切都很好,但是一旦我运行并行运行的BDD测试(9个线程),我就会在调用外部服务C时在服务B中获得NoHttp响应异常。

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模板配置:

    @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.setValidateAfterInactive(0);但没有帮助。

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

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

也许我需要在连接管理器中设置DefaultMaxPerRoute?如果是,那么如何区分正确的数字?如果能简单介绍一下在这种情况下如何正确配置RestTemplate,我将不胜感激。

共有3个答案

简滨海
2023-03-14

如果希望在日志中看到重试,可以使用如下重试解决方案:

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;
    });
}
彭烨烁
2023-03-14

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

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

您可以通过以下行禁用以重用连接:httpClient。SetReuseStragy(新的NoConnectionReuseStragy())

哈烨熠
2023-03-14

看起来这里有一个类似的问题:获取NoHttp响应异常进行负载测试

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

 类似资料: