2015-10-13 21 views
10

Zgodnie z dokumentacją,Ponowne JAX RS Client w środowisku wielowątkowym (z resteasy)

„Klienci są obiekty o dużej masie, które zarządzają infrastrukturą komunikacyjną po stronie klienta . Inicjowanie jak utylizacja instancja Klient może być dość kosztowna operacja. jest zatem powinni konstruować tylko niewielką liczbę instancji klienta w aplikacji . "

Ok, próbuję buforować samego klienta i Web instancje docelowe w zmiennej statycznej SomeMethod() jest wywoływana w środowisku wielowątkowym:

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

Ale czasami (nie zawsze) mam dostać wyjątek:

Nieprawidłowe użycie BasicClientConnManager: połączenie nadal przydzielone. Pamiętaj, aby zwolnić połączenie przed przydzieleniem innego.

W jaki sposób Client/WebTarget może być ponownie użyty/buforowany poprawnie? Czy jest to możliwe dzięki JAX RS Client API? Albo muszę użyć pewnych funkcji związanych z ramami (resteasy/jersey). Czy możesz podać przykład lub dokumentację?

+0

Prawdopodobny duplikat [jest bezpiecznikiem wątku klienta JAX-RS] (http://stackoverflow.com/questions/24700798/is-jax-rs-client-thread-safe) – tddmonkey

Odpowiedz

5

Twoje wdrożenie nie jest bezpieczne dla wątków. Gdy dwa wątki uzyskają dostęp do someMethod w tym samym czasie, będą one miały ten sam Client, a jeden spróbuje wykonać drugie żądanie, podczas gdy pierwszy nie zostanie zakończony.

Masz dwie możliwości:

  • synchronizować dostęp do ręcznie Client i WebTarget.
  • Pozwolić kontu zarządzać współbieżnością, opisując typ obejmujący z @javax.ejb.Singleton, który gwarantuje bezpieczeństwo wątku. (patrz rozdz. 4.8.5 z EJB specification)

Jeśli w środowisku zarządzanym przez kontener używałbym someMethod, korzystałbym z drugiego podejścia.

3

Po pierwsze, nie używaj ponownie WebTarget. Dla uproszczenia zawsze możesz stworzyć nowy WebTarget.

Po drugie, jeśli używasz Resteasy, możesz dodać do projektu zależną zależność dla klienta Resteasy. Przykład w Gradle:

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

Następnie można utworzyć połączenia tak:

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

Nie ma potrzeby, aby ustawić maxPooledPerRoute ten jest ustawiany automatycznie przez RestEasy (można znaleźć w RestEasyClientBuilder źródła klasy kod).

Po ustawieniu metody connectionPoolSize nie otrzymasz błędu, gdy Klient zostanie ponownie użyty i możesz z powodzeniem używać ich ponownie w całej aplikacji. Próbowałem tego rozwiązania w wielu projektach i faktycznie działa dobrze.Ale kiedy wdrożysz swoją aplikację do łatwego pojemnika (np. Glassfish), twój kod nie zadziała i będziesz musiał ponownie użyć klasy ClientBuilder.

5

Ponieważ kwestia ta jest wciąż otwarta w momencie pisania (wersja 3.0.x) RESTEASY: deprecated Apache classes cleanup

Można pójść głębiej, aby skorzystać z nowszej, non-nieaktualnych klas zamiast tworzyć ty resteasy klienta. Będziesz także mieć większą kontrolę nad tym, jak chcesz być basen itp

Oto co zrobiłem:

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

także upewnij się zwolnić połączenie Po dokonaniu połączenia. Wywołanie metody response.close() zrobi to za Ciebie, więc prawdopodobnie umieścisz to w bloku finally.