sto usando primavera per raggiungere i seguenti obiettivi:PoolingHttpClientConnectionManager non rilascia le connessioni
Su un server, ricevo i dati attraverso un'interfaccia REST in un formato XML. Voglio trasformare i dati in JSON e inviarli a un altro server. Il mio codice (ho tolto alcune sensibili classnames/URL per evitare l'ira del mio datore di lavoro) si presenta così:
principale classe/Configurazione:
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);
}
}
La classe che rappresenta l'interfaccia RESTful:
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!";
}
}
Infine, lo spedizioniere:
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();
}
}
Tuttavia, il Connection Manager di rado sembra che rilasci le connessioni per il riutilizzo, e in aggiunta, il sistema si riempie di connessioni nello stato CLOSE_WAIT (che può essere visto usando netstat). Tutte le connessioni nel pool vengono concesse in lease, ma non rilasciate e non appena il numero di connessioni nello stato CLOSE_WAIT raggiunge il limite, vengono visualizzate le eccezioni "Troppe aperte"
A causa della natura multithread del codice , Sospetto che le prese non possano essere chiuse/le connessioni vengano rilasciate, perché qualche altro thread li sta bloccando in qualche modo.
Apprezzerei molto qualsiasi aiuto o suggerimento che tu possa darmi per risolvere il problema.
Il tuo codice sembra ok. Sei sicuro che il server stia consegnando le risposte? hai provato a impostare le proprietà 'setConnectTimeout' e' setReadTimeout' in 'requestFactory'? Funziona quando chiamate il 'ForwardedService' in modo sincrono (senza' @ Async')? – Ruben
Sì, il server risponde (Stato '200 OK'). Ho impostato i timeout e ho chiamato il metodo di inoltro in modo sincrono - nulla ha aiutato. – pczora
L'unico suggerimento che ho è di configurare una strategia KeepAlive. Dai un'occhiata al capitolo 2.6 in [http client docs] (http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html) – Ruben