【问题标题】:Spring RestTemplate - Need to release connection?Spring RestTemplate - 需要释放连接?
【发布时间】:2024-01-06 22:06:02
【问题描述】:

这是我的 Rest 模板配置,

    @Bean
    @Qualifier("myRestService")
    public RestTemplate createRestTemplate(@Value("${connection.timeout}") String maxConn) {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
         connectionManager.setMaxTotal(maxTotalConn);
         connectionManager.setDefaultMaxPerRoute(maxPerChannel);

        RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
        CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
                .setDefaultRequestConfig(config).build();
        ClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);

        RestTemplate restTemplate = new RestTemplate(factory);

        restTemplate.setErrorHandler(new RestResponseErrorHandler());
         restTemplate.setMessageConverters(createMessageConverters());

        return restTemplate;
    }

我正在使用 PoolingHttpClientConnectionManager 来管理连接。

它被下面的代码访问,

ResponseEntity<String> response = restClient.exchange( url, HttpMethod.GET, entity , String.class );

我需要在上述调用后释放连接还是由 RestTemplate 处理。如果我们需要处理释放连接。

请有人解释/展示如何释放连接。

【问题讨论】:

    标签: spring httpclient resttemplate


    【解决方案1】:

    您应该将 ClientHttpRequestFactory 声明为 bean。通过将其声明为 bean,它由 Spring bean 工厂管理,当应用程序关闭或 bean 超出范围时,它将调用工厂的 destroy 方法。 ClientHttpRequestFactory 的destroy 方法会关闭底层ClientConnectionManager 的连接池。您可以查看 Spring API 文档。

    @Bean
    public ClientHttpRequestFactory createRequestFactory(@Value("${connection.timeout}") String maxConn) {
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
         connectionManager.setMaxTotal(maxTotalConn);
         connectionManager.setDefaultMaxPerRoute(maxPerChannel);
    
        RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
        CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connectionManager)
                .setDefaultRequestConfig(config).build();
        return new HttpComponentsClientHttpRequestFactory(httpClient);
    }
    

    然后你可以使用这个 bean 来创建你的 RestTemplate:

    @Bean
    @Qualifier("myRestService")
    public RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
    
        restTemplate.setErrorHandler(new RestResponseErrorHandler());
        restTemplate.setMessageConverters(createMessageConverters());
    
        return restTemplate;
    }
    

    【讨论】:

    • 请您用代码示例更清楚地解释一下。
    【解决方案2】:

    您提出的问题: 我需要在上述调用后释放连接还是由 RestTemplate 处理。如果我们需要处理释放连接。

    不,如果您使用 resttemplate,则不需要关闭响应上的连接。

    从 apache httpclient,你需要消费完整的响应(EntityUtils.consume(HttpEntity) 并关闭响应。

    这个可以在ClientConnectionRelease.java验证

    但是 RestTemplate 会为您执行此操作,以验证是否相同,请查看 RestTemplate.java

    寻找方法

    protected <T> T doExecute(URI url,...) {
     try {
            ClientHttpRequest request = this.createRequest(url, method);
            ...
            response = request.execute();
            ...
            if(responseExtractor != null) {
                var7 = responseExtractor.extractData(response);
                return var7;
            }
           ...
           ...
        } finally {
            if(response != null) {
                response.close();
            }
    
        }
    }
    

    响应提取器通过使用响应来为您完成工作 responseExtractor.extractData(response);

    在完全提取数据后,它也会关闭 response.close()

    【讨论】:

    • 他的重点是不要关闭每个调用的连接并使用 apache PoolingHttpClientConnectionManager 保持套接字打开?
    • Deepak 是正确的。他需要关闭连接才能将其释放回连接池。
    • 我们使用 ClientHttpRequestInterceptor 拦截了 RestTemplate 并且响应没有被关闭以防出现异常。
    • 嗨,当响应为空时会发生什么?
    • @AlafAzam 显然,有问题...*.com/questions/63451808/…
    【解决方案3】:

    我想答案就在这里:org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#doExecuteRequest

        @Override
        protected RemoteInvocationResult doExecuteRequest(
                HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
                throws IOException, ClassNotFoundException {
    
            HttpPost postMethod = createHttpPost(config);
            setRequestBody(config, postMethod, baos);
            try {
                HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod);
                validateResponse(config, response);
                InputStream responseBody = getResponseBody(config, response);
                return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
            }
            finally {
                postMethod.releaseConnection();
            }
        }
    

    【讨论】: