2013-02-14 21 views
36

Sto effettuando una chiamata all'URL REST e cercando di misurare quanto tempo è necessario per ottenere la risposta.Uso non valido di BasicClientConnManager: connessione ancora allocata

Sto utilizzando DefaultHttpClient per ottenere la risposta dal REST URL.

Nel mio programma di seguito, ogni thread lavorerà su un determinato intervallo. Come ogni thread funzionerà tra 1 - 100 e il secondo thread funzionerà tra 101 - 200 ecc.

SO nel mio codice qui sotto, per la prima volta funziona bene. Ma per la seconda volta, si sta gettando eccezione su questa linea httpclient.execute per la seconda volta as-

java.lang.IllegalStateException: Invalid use of BasicClientConnManager: connection still allocated. 
Make sure to release the connection before allocating another one. 

qualcosa che non va che sto facendo qui? -

Qui di seguito è la mia code-

class Task implements Runnable { 

    private DefaultHttpClient httpclient = new DefaultHttpClient(); 
    private HttpGet httpGet; 
    private HttpResponse response; 

    @Override 
    public void run() { 

     try { 

      httpGet = new HttpGet(
        "http://localhost:8080/service/BEService/v1/get/USERID=10000/profile.ACCOUNT.SERVICE 
      httpGet.getRequestLine(); 

      for (int userId = id; userId < id + noOfTasks; userId++) { 

        long start = System.nanoTime(); 

        response = httpclient.execute(httpGet); 

        long end = System.nanoTime() - start; 
       } 
     } catch (Exception e) { 
      LOG.error("Threw a Exception in " + getClass().getSimpleName(), e); 
     } 
    } 
} 

Codice aggiornato: -

Se lo faccio qualcosa come questo-

012.351.
class Task implements Runnable { 

    private DefaultHttpClient httpclient = new DefaultHttpClient(); 
    private HttpGet httpGet; 
    private HttpResponse response; 

    @Override 
    public void run() { 

     try { 

      for (int userId = id; userId < id + noOfTasks; userId++) { 

       httpGet = new HttpGet("http://localhost:8080/service/BEService/v1/get/USERID=10000/profile.ACCOUNT.SERVICE"); 
       httpGet.getRequestLine(); 

       long start = System.nanoTime(); 

       response = httpclient.execute(httpGet); 

       long end = System.nanoTime() - start; 

       HttpEntity entity = response.getEntity(); 
       EntityUtils.consume(entity); 
       } 
     } catch (Exception e) { 
      LOG.error("Threw a Exception in " + getClass().getSimpleName(), e); 
     } 
    } 
} 

quindi va bene o no?

risposta

37

C'è qualcosa di sbagliato che sto facendo qui?

Sì. Come indicato nel the docs:

BasicClientConnectionManager è una semplice gestione connessione che mantiene una sola connessione alla volta. Anche se questa classe è thread-safe, dovrebbe essere utilizzata solo da un thread di esecuzione. BasicClientConnectionManager si impegna a riutilizzare la connessione per le richieste successive con lo stesso percorso. Sarà, , tuttavia, chiudere la connessione esistente e riaprirla per il dato itinerario , se il percorso della connessione persistente non corrisponde a quello della richiesta di connessione. Se la connessione è già stata assegnata a , viene lanciata java.lang.IllegalStateException.

BasicClientConnectionManager viene utilizzato da HttpClient per impostazione predefinita.

Vedi "Multithreaded request execution" su come utilizzare un pooling connection manager in grado di gestire le richieste in più thread.

+0

Grazie Ryan per il suggerimento. Ho aggiornato il codice con le modifiche. Se lo faccio in questo modo, avrò qualche problema? – AKIWEB

+1

È difficile dirlo dai frammenti che hai mostrato. Sembra che tu abbia un HttpClient che attraversa i thread, cosa che non è consentita con il gestore delle connessioni di base. Suppongo che potrebbe anche essere che non hai correttamente [rilasciare le risorse di basso livello] (http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html#d5e139) nel primo Astuccio. –

+0

Correggetemi se ho torto ma nel suo codice l'httpClient non è condiviso. In C# un thread è un metodo ma in Java è l'intera classe (jj09.net/multithreading-csharp-vs-java). Quindi sì, il problema è il rilascio delle risorse (https://www.securecoding.cert.org/confluence/x/9gFqAQ, https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache /http/util/EntityUtils.html#consume(org.apache.http.HttpEntity)). –

37

Supponendo che si stia utilizzando la vaniglia DefaultHttpClient (che utilizza BasicClientConnectionManager internamente), è necessario prima consumare la risposta in sospeso/ultima.

EntityUtils.consumeQuietly(httpResponse.getEntity());

Altrimenti, è possibile riallocare DefaultHttpClient ogni volta.

Fonte: Workaround to not shutdown DefaultHttpClient() each time after usage

+1

Prima assicurati che solo un singolo thread usi DefaultHttpClient, quindi per ogni esecuzione, non dimenticare httpResponse.getEntity(). Sarà ok. – Codefor

+1

Il primo consiglio è forte. Il secondo non lo è, perché la riallocazione delle tolleranze del client Http non è economica – asgs

1

Questa è la mia configurazione per RestTemplate utilizzo piscina connection manager. Funziona molto bene in altri 5 thread simultanei.

<!-- RestTemplate --> 
<beans:bean id="restTemplateYT" class="org.springframework.web.client.RestTemplate"> 
    <beans:constructor-arg ref="httpRequestFactoryYT" /> 
</beans:bean> 

<beans:bean id="httpRequestFactoryYT" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"> 
    <beans:constructor-arg> 
     <beans:bean class="org.apache.http.impl.client.DefaultHttpClient"> 
      <beans:constructor-arg> 
       <beans:bean class="org.apache.http.impl.conn.PoolingClientConnectionManager"/> 
      </beans:constructor-arg> 
     </beans:bean> 
    </beans:constructor-arg> 
    <beans:property name="connectTimeout" value="5000" /> 
</beans:bean> 

versione Primavera: 3.1.0

Problemi correlati