Java – poolinghttpclientconnectionmanager does not release the connection
I use spring to achieve the following:
On the server, I receive data through the rest interface in XML format I want to convert the data into JSON and post it to another server My code (I deleted some sensitive class names / URLs to avoid the anger of the employer) looks like this:
Master / configuration class:
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);
}
}
Classes representing restful interfaces:
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!";
}
}
Finally, freight forwarder:
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();
}
}
However, connection managers rarely seem to release reused connections. In addition, the system will close_ The connection in the wait state (which can be seen using netstat) All connections in the pool are leased, but not released, and once closed_ When the number of connections in the wait state reaches ulimit, I will receive 'too many open files' exceptions
Due to the multithreading nature of the code, I suspect that sockets cannot be closed / connections released because some other threads cannot block them
I really appreciate any help or tips. You can help me solve the problem
Solution
Apache httpentity has a trick - releasing locked connections - the response must be completely consumed and closed For more information, see the entityutils and httpentity documentation:
EntityUtils.consume(response);
Starting from version 4.3, when the #close () method is called on the closeablehttpresponse, Apache httpclient will release the connection back to the pool
However, spring version 4.0 only supports spring web functions. See httpcomponentsclienthttpresponse Method #close() in Java:
@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) {
}
}
The key to success is "/ / paranoia" – explicit Close() call It actually frees the connection back to the pool
