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