10

salmayan: Bir sunucudaPoolingHttpClientConnectionManager aşağıdaki ulaşmak için Bahar kullanıyorum bağlantıları

, ben bir XML-Format REST arayüzü üzerinden veri almak. Verileri JSON'a dönüştürmek ve başka bir Sunucuya göndermek istiyorum. Kodum (benim işveren gazabından kaçınmak için bazı hassas classnames/URL'ler kaldırıldı) şöyle görünür:

Ana/Konfigürasyon sınıfı:

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 arayüzü temsil sınıfı:

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!"; 
    } 
} 

Son olarak, yönlendirici:

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(); 
    } 
} 

Ancak Bağlantı Yöneticisi nadiren yeniden kullanım için bağlantıları serbest bırakıyor gibi görünüyor ve ek olarak, sistem CLOSE_WAIT durumunda (netstat kullanılarak görülebilir) bağlantıları ile su basıyor. Havuzdaki tüm bağlantılar kiralanır, ancak serbest bırakılmaz ve CLOSE_WAIT durumundaki bağlantı sayısı ulimit'e ulaşır ulaşmaz 'Çok fazla açık dosya var' istisnası var

Kodun çok iş parçacıklı yapısı nedeniyle Ben, bu soketlerin kapatılamayacağından şüpheleniyorum/bağlantılar serbest bırakılır, çünkü başka bir iş parçacığı bir şekilde onları engeller.

Sorunu çözmek için bana verebileceğiniz herhangi bir yardım veya ipucunu gerçekten takdir ediyorum.

+0

Sizin kodunuz hoş görünüyor. Sunucunun yanıt verdiğini biliyor musunuz? 'requestFactory' içinde 'setConnectTimeout' ve' setReadTimeout' özelliklerini ayarlamayı denediniz mi? "ForwardedService" işlevini eşzamanlı olarak çağırdığınızda ('@ Async' olmadan) çalışır mı? – Ruben

+0

Evet, sunucu yanıt veriyor (Durum '200 OK'). Zaman aşımlarını ayarladım ve iletme yöntemini eşzamanlı olarak çağırdım - hiçbir şey yardımcı olmadı. – pczora

+0

Sahip olduğum tek öneri KeepAlive stratejisini yapılandırmak. [Http client docs] 'da bölüm 2.6'ya bakın (http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html) – Ruben

cevap

2

Apache HttpEntity ile bir hile var - kilitli bağlantıyı serbest bırakmak için - cevap TAMAMEN tüketilen ve kapalı olmak zorundadır. Ayrıntılar için EntityUtils ve HttpEntity docs bakınız: #Kapat() yöntemi CloseableHttpResponse çağrıldığında sürüm 4.3 Apache HttpClient geri havuzuna bağlantıyı keser

EntityUtils.consume(response); 

beri. yöntem #Kapat() HttpComponentsClientHttpResponse.java görmek Ancak bu özellik sadece sürüm 4.0.0-RELEASE beri Bahar Web tarafından desteklenen

:

@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) { 
    } 
} 

başarının anahtarı "// Paranoya damgasını çizgidir "- açık .close() çağrısı. Aslında bağlantıyı havuza geri gönderir.

İlgili konular