2015-10-13 13 views
10

Secondo la documentazione,Riutilizzo JAX RS client in ambiente multi-threaded (con RESTEasy)

"I clienti sono oggetti pesanti che gestiscono l'infrastruttura di comunicazione client-side . Inizializzazione, così come lo smaltimento dei un'istanza client può essere un'operazione piuttosto costoso. e 'quindi consigliato di costruire solo un piccolo numero di istanze client nell'applicazione . "

Ok, sto cercando di cache del client stesso e Web le istanze di destinazione in una variabile statica, il someMethod() viene richiamato in ambiente multi-threaded:

private static Client client = ClientBuilder.newClient(); 
private static WebTarget webTarget = client.target("someBaseUrl"); 
... 
public static String someMethod(String arg1, String arg2) 
{ 
    WebTarget target = entrTarget.queryParam("arg1", arg1).queryParam("arg2", arg2); 
    Response response = target.request().get(); 
    final String result = response.readEntity(String.class); 
    response.close(); 
    return result; 
} 

Ma a volte (non sempre) Sto ottengo un'eccezione:

uso non valido di BasicClientConnManager: connessione ancora assegnata. Assicurarsi di rilasciare la connessione prima di allocarne un'altra.

In che modo Client/WebTarget può essere riutilizzato/memorizzato correttamente nella cache? È possibile con l'API client JAX RS? O devo usare alcune funzionalità specifiche del framework (resteasy/jersey) Potresti fornire qualche esempio o documentazione?

+0

Possibile duplicato di [È sicuro il thread del client JAX-RS] (http://stackoverflow.com/questions/24700798/is-jax-rs-client-thread-safe) – tddmonkey

risposta

5

L'implementazione non è thread-safe. Quando due thread accedono allo someMethod allo stesso tempo, condividono lo stesso Client e uno proverà a fare una seconda richiesta mentre il primo non è terminato.

Hai due scelte:

  • sincronizzare l'accesso alla Client e WebTarget manualmente.
  • Consentire al contenitore di gestire la concorrenza annotando il tipo di allegato con @javax.ejb.Singleton che garantisce la sicurezza del thread. (Vedi capitolo 4.8.5 del EJB specification)

Se someMethod in un ambiente gestito contenitore userei il secondo approccio.

3

Innanzitutto, non riutilizzare WebTarget. Per semplicità, puoi sempre creare un nuovo WebTarget.

In secondo luogo, se si utilizza Resteasy, è possibile aggiungere la dipendenza fornita per il client Resteasy al progetto. Esempio in Gradle:

provided 'org.jboss.resteasy:resteasy-client:3.0.14.Final' 

Quindi, è possibile creare il collegamento in questo modo:

 ResteasyClientBuilder builder = new ResteasyClientBuilder(); 
     builder.connectionPoolSize(200); 

Non v'è alcuna necessità di impostare maxPooledPerRoute, questa viene impostata automaticamente dal RESTEasy (può essere trovato nel sorgente della classe RestEasyClientBuilder codice).

Quando si imposta connectionPoolSize, non si otterrà più un errore quando il client viene riutilizzato e si può riutilizzarlo felicemente in tutta l'applicazione. Ho provato questa soluzione su molti progetti e funziona davvero bene.Ma quando distribuisci la tua applicazione in un contenitore non restitutivo (come Glassfish), il tuo codice non funzionerà e dovrai utilizzare nuovamente la classe di ClientBuilder.

5

Dal momento che questo problema è ancora aperto al momento della scrittura (versione 3.0.x) RESTEASY: deprecated Apache classes cleanup

Si può andare più in profondità per utilizzare le classi più recente, non deprecato, invece di creare voi RESTEasy cliente. Avrete anche avere più controllo su come si desidera che la piscina sia ecc

Ecco quello che ho fatto:

// This will create a threadsafe JAX-RS client using pooled connections. 
// Per default this implementation will create no more than than 2 
// concurrent connections per given route and no more 20 connections in 
// total. (see javadoc of PoolingHttpClientConnectionManager) 
PoolingHttpClientConnectionManager cm = 
     new PoolingHttpClientConnectionManager(); 

CloseableHttpClient closeableHttpClient = 
     HttpClientBuilder.create().setConnectionManager(cm).build(); 
ApacheHttpClient4Engine engine = 
     new ApacheHttpClient4Engine(closeableHttpClient); 
return new ResteasyClientBuilder().httpEngine(engine).build(); 

anche assicurarsi di rilasciare la connessione dopo aver effettuato una chiamata. Chiamare response.close() lo farà per te, quindi probabilmente lo metti in un blocco finale.