gpt4 book ai didi

java - PoolingHttpClientConnectionManager 不释放连接

转载 作者:IT老高 更新时间:2023-10-28 13:47:19 26 4
gpt4 key购买 nike

我正在使用 Spring 来实现以下目标:

在服务器上,我通过 REST 接口(interface)以 XML 格式接收数据。我想将数据转换为 JSON 并将其发布到另一个服务器。我的代码(我删除了一些敏感的类名/URL 以避免雇主的愤怒)如下所示:

主/配置类:

package stateservice;

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class App {
Logger log = LoggerFactory.getLogger(App.class);

public static void main(String[] args) {
System.out.println("Start!");
SpringApplication.run(StateServiceApplication.class, args);
System.out.println("End!");
}

@Bean
public RestTemplate restTemplate() {
log.trace("restTemplate()");
HttpHost proxy = new HttpHost("proxy_url", 8080);
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);

RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
httpClientBuilder.setConnectionManager(cm);
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClientBuilder.build());
return new RestTemplate(requestFactory);
}
}

代表RESTful接口(interface)的类:

package stateservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import foo.bar.XmlData

@RestController
public class StateController {

private static Logger log = LoggerFactory.getLogger(DataController.class);

@Autowired
ForwarderService forwarder;


@RequestMapping(value = "/data", method = RequestMethod.POST)
public String postState(@RequestBody XmlData data) {
forwarder.forward(data);
return "Done!";
}
}

最后是转发器:

package stateservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import foo.bar.Converter;
import foo.bar.XmlData;

@Service
public class ForwarderService {
private static Logger log = LoggerFactory.getLogger(ForwarderService.class);

String uri = "forward_uri";

@Autowired
RestTemplate restTemplate;

@Async
public String forward(XmlData data) {
log.trace("forward(...) - start");
String json = Converter.convert(data);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

ResponseEntity<String> response = restTemplate.postForEntity(uri,
new HttpEntity<String>(json, headers), String.class);
// responseEntity.getBody();
// log.trace(responseEntity.toString());
log.trace("forward(...) - end");
return response.getBody();
}
}

但是,连接管理器似乎很少释放连接以供重用,此外,系统会充斥着处于 CLOSE_WAIT 状态的连接(可以使用 netstat 看到)。池中的所有连接都被租用,但未释放,并且一旦处于 CLOSE_WAIT 状态的连接数达到 ulimit,我就会得到“打开的文件太多”-异常

由于代码的多线程特性,我怀疑无法关闭套接字/释放连接,因为其他一些线程正在阻塞它们。

如果您能给我解决问题的任何帮助或提示,我将不胜感激。

最佳答案

Apache HttpEntity 有一个技巧 - 释放锁定的连接 - 响应必须完全消耗关闭。见 EntityUtilsHttpEntity有关详细信息的文档:

EntityUtils.consume(response);

从 4.3 版开始,当在 CloseableHttpResponse 上调用 #close() 方法时,Apache HttpClient 会释放与池的连接。

然而,Spring Web 仅从版本 4.0.0-RELEASE 开始支持此功能,请参阅 HttpComponentsClientHttpResponse.java 中的方法 #close() :

@Override
public void close() {
// Release underlying connection back to the connection manager
try {
try {
// Attempt to keep connection alive by consuming its remaining content
EntityUtils.consume(this.httpResponse.getEntity());
} finally {
// Paranoia
this.httpResponse.close();
}
}
catch (IOException ignore) {
}
}

成功的关键是“//偏执狂”标记的那一行——显式的.close()调用。它实际上将连接释放回池。

关于java - PoolingHttpClientConnectionManager 不释放连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32909653/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com