Upravljanje povezivanjem HttpClient

1. Pregled

U ovom ćemo članku razmotriti osnove upravljanja vezama unutar HttpClienta 4.

Objasnit ćemo upotrebu BasichttpClientConnectionManager i SpajanjeHttpClientConnectionManager kako bi se nametnula sigurna, protokolarna i učinkovita upotreba HTTP veza.

2. The BasicHttpClientConnectionManager za niskorazinski jednostruki navoj

The BasicHttpClientConnectionManager dostupan je od HttpClient 4.3.3 kao najjednostavnija implementacija upravitelja HTTP veza. Koristi se za stvaranje i upravljanje jedinstvenom vezom koju istodobno može koristiti samo jedna nit.

Primjer 2.1. Dobivanje zahtjeva za povezivanje za vezu niske razine (HttpClientConnection)

BasicHttpClientConnectionManager connManager = novi BasicHttpClientConnectionManager (); Ruta HttpRoute = nova HttpRoute (nova HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = connManager.requestConnection (ruta, null);

The requestConnection metoda dobiva od upravitelja skup veza za određenu ruta za povezivanje s. The ruta parametar specificira rutu "proxy skokova" do ciljnog domaćina ili samog ciljnog hosta.

Moguće je izvršiti zahtjev pomoću HttpClientConnection izravno, ali imajte na umu da je ovaj pristup na niskoj razini opširan i težak za upravljanje. Niskorazinske veze korisne su za pristup podacima utičnice i veze, poput vremenskih ograničenja i podataka o ciljanom hostu, ali za standardna izvršavanja HttpClient je puno lakši API za rad.

3. Korištenje SpajanjeHttpClientConnectionManager da biste dobili i upravljali skupom višenitnih veza

The SpajanjeHttpClientConnectionManager stvorit će i upravljati skupom veza za svaku rutu ili ciljni domaćin koje koristimo. Zadana veličina spremišta istodobnih veze koje upravitelj može otvoriti je 2 za svaku rutu ili ciljnog domaćina, i Ukupno 20 otvorene veze. Prvo - pogledajmo kako postaviti ovaj upravitelj veza na jednostavnom HttpClientu:

Primjer 3.1. Postavljanje PoolingHttpClientConnectionManager na HttpClient

HttpClientConnectionManager poolingConnManager = novo SpajanjeHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). SetConnectionManager (poolingConnManager) .build (); client.execute (novi HttpGet ("/")); assertTrue (poolingConnManager.getTotalStats (). getLeased () == 1);

Dalje - pogledajmo kako isti upravitelj veza mogu koristiti dva HttpClienta koja se izvode u dvije različite niti:

Primjer 3.2. Korištenje dva HttpClients za povezivanje s jednim ciljnim hostom

HttpGet get1 = novi HttpGet ("/"); HttpGet get2 = novi HttpGet ("// google.com"); PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); CloseableHttpClient client1 = HttpClients.custom (). SetConnectionManager (connManager) .build (); CloseableHttpClient client2 = HttpClients.custom (). SetConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = novi MultiHttpClientConnThread (klijent1, get1); MultiHttpClientConnThread thread2 = novi MultiHttpClientConnThread (klijent2, get2); thread1.start (); thread2.start (); nit1.join (); nit2.join ();

Primijetite da koristimo vrlo jednostavna prilagođena implementacija niti - Evo ga:

Primjer 3.3. Prilagođena nit Izvršenje a GET Zahtjev

javna klasa MultiHttpClientConnThread proširuje Thread {private CloseableHttpClient client; privatni HttpGet get; // standardni konstruktori public void run () {try {HttpResponse response = client.execute (get); EntityUtils.consume (response.getEntity ()); } catch (ClientProtocolException ex) {} catch (IOException ex) {}}}

PrimijetiteEntityUtils.consume (response.getEntity) poziv - potreban za konzumiranje cjelokupnog sadržaja odgovora (entiteta) tako da upravitelj može otpustite vezu natrag na bazen.

4. Konfigurirajte Connection Manager

Zadane postavke upravitelja veza za spremanje dobro su odabrane, ali - ovisno o vašem slučaju upotrebe - možda su premale. Pa - pogledajmo kako možemo konfigurirati:

  • ukupan broj veza
  • maksimalan broj veza po (bilo kojoj) ruti
  • maksimalan broj veza po pojedinoj određenoj ruti

Primjer 4.1. Povećavanje broja veza koje se mogu otvoriti i njima se može upravljati izvan zadanih ograničenja

PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); connManager.setMaxTotal (5); connManager.setDefaultMaxPerRoute (4); HttpHost domaćin = novi HttpHost ("www.baeldung.com", 80); connManager.setMaxPerRoute (novi HttpRoute (domaćin), 5);

Sažmimo API:

  • setMaxTotal (int max): Postavite maksimalni broj ukupno otvorenih veza.
  • setDefaultMaxPerRoute (int max): Postavite maksimalni broj istodobnih veza po ruti, što je prema zadanim postavkama 2.
  • setMaxPerRoute (int max): Postavite ukupan broj istodobnih veza na određenu rutu, što je prema zadanim postavkama 2.

Dakle, bez promjene zadanog, doseći ćemo ograničenja upravitelja veze prilično lako - da vidimo kako to izgleda:

Primjer 4.2. Korištenje niti za izvršavanje veza

HttpGet get = novi HttpGet ("// www.baeldung.com"); PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). setConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = novi MultiHttpClientConnThread (klijent, nabavi); MultiHttpClientConnThread thread2 = novi MultiHttpClientConnThread (klijent, nabavi); MultiHttpClientConnThread thread3 = novi MultiHttpClientConnThread (klijent, nabavi); thread1.start (); thread2.start (); thread3.start (); nit1.join (); nit2.join (); nit3.join ();

Kao što smo već razgovarali, ograničenje veze po hostu je 2 prema zadanim postavkama. Dakle, u ovom primjeru pokušavamo napraviti 3 niti 3 zahtjeva za istog domaćina, ali paralelno će se dodijeliti samo 2 veze.

Pogledajmo zapisnike - rade tri niti, ali samo 2 unajmljene veze:

[Thread-0] INFO obhcMultiHttpClientConnThread - Before - Leasing Connections = 0 [Thread-1] INFO obhcMultiHttpClientConnThread - Before - Leasing Connections = 0 [Thread-2] INFO obhcMultiHttpClientConad-0-Thread 0 INFO obhcMultiHttpClientConnThread - Nakon - zakupljene veze = 2 [Tema-0] INFO obhcMultiHttpClientConnThread - nakon - zakupljene veze = 2

5. Strategija održavanja veze

Citiranje HttpClienta 4.3.3. referenca: „Ako je Držati na životu zaglavlje nije prisutno u odgovoru, HttpClient pretpostavlja da se veza može održavati na neodređeno vrijeme. " (Pogledajte referencu HttpClient).

Da bismo to zaobišli i mogli upravljati mrtvim vezama, potrebna nam je prilagođena implementacija strategije i ugrađivanje u HttpClient.

Primjer 5.1. Prilagođena strategija za održavanje života

ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy () {@ Prevedi javni long getKeepAliveDuration (odgovor HttpResponse, kontekst HttpContext) {HeaderElementIterator it = novi BasicHeaderElementIterator (response.headerkekeper while (it.hasNext ()) {HeaderElement he = it.nextElement (); Niz param = he.getName (); Vrijednost niza = he.getValue (); if (value! = null && param.equalsIgnoreCase ("timeout")) {return Long.parseLong (value) * 1000; }} povratak 5 * 1000; }};

Ova strategija prvo će pokušati primijeniti domaćinsku Držati na životu politika navedena u zaglavlju. Ako tih informacija nema u zaglavlju odgovora, održat će veze 5 sekundi.

Sad - stvarajmo klijent s ovom prilagođenom strategijom:

PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setKeepAliveStrategy (myStrategy) .setConnectionManager (connManager) .build ();

6. Postojanost / ponovna upotreba veze

HTTP / 1.1 Spec navodi da se veze mogu ponovno koristiti ako nisu zatvorene - to je poznato kao trajnost veze.

Jednom kada upravitelj pusti vezu, ona ostaje otvorena za ponovnu upotrebu. Kada koristite BasicHttpClientConnectionManager, koja može upravljati samo jednom vezom, veza se mora osloboditi prije ponovnog iznajmljivanja:

Primjer 6.1. BasicHttpClientConnectionManagerPonovna upotreba veze

BasicHttpClientConnectionManager basicConnManager = novi BasicHttpClientConnectionManager (); HttpClientContext context = HttpClientContext.create (); // ruta HttpRoute niske razine = novi HttpRoute (novi HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = basicConnManager.requestConnection (ruta, null); HttpClientConnection conn = connRequest.get (10, TimeUnit.SECONDS); basicConnManager.connect (conn, route, 1000, context); basicConnManager.routeComplete (conn, route, context); HttpRequestExecutor exeRequest = novi HttpRequestExecutor (); context.setTargetHost ((novi HttpHost ("www.baeldung.com", 80))); HttpGet get = novi HttpGet ("// www.baeldung.com"); exeRequest.execute (get, conn, context); basicConnManager.releaseConnection (conn, null, 1, TimeUnit.SECONDS); // klijent visoke razine CloseableHttpClient = HttpClients.custom () .setConnectionManager (basicConnManager) .build (); client.execute (get);

Pogledajmo što se događa.

Prvo - primijetite da prvo koristimo vezu na niskoj razini, samo kako bismo imali potpunu kontrolu nad time kada se veza oslobodi, a zatim normalnu vezu više razine s HttpClientom. Kompleksna logika niske razine ovdje nije vrlo relevantna - jedino što nas zanima je releaseConnection poziv. To oslobađa jedinu dostupnu vezu i omogućuje ponovnu upotrebu.

Zatim, klijent ponovno uspješno izvršava GET zahtjev. Ako preskočimo puštanje veze, dobit ćemo IllegalStateException iz HttpClienta:

java.lang.IllegalStateException: Veza se i dalje dodjeljuje na o.a.h.u.Asserts.check (Asserts.java:34) na o.a.h.i.c.BasicHttpClientConnectionManager.getConnection (BasicHttpClientConnectionManager.java:248)

Imajte na umu da postojeća veza nije zatvorena, samo je puštena i zatim je ponovno koristi drugi zahtjev.

Za razliku od gornjeg primjera, The SpajanjeHttpClientConnectionManager omogućuje ponovnu upotrebu veze bez potrebe za implicitnim puštanjem veze:

Primjer 6.2.SpajanjeHttpClientConnectionManager: Ponovna upotreba veza s nitima

HttpGet get = novi HttpGet ("// echo.200please.com"); PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); connManager.setDefaultMaxPerRoute (5); connManager.setMaxTotal (5); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); MultiHttpClientConnThread [] teme = novi MultiHttpClientConnThread [10]; for (int i = 0; i <thread.length; i ++) {niti [i] = novi MultiHttpClientConnThread (klijent, get, connManager); } za (MultiHttpClientConnThread nit: niti) {thread.start (); } za (MultiHttpClientConnThread nit: niti) {thread.join (1000); }

Gornji primjer ima 10 niti, izvršavajući 10 zahtjeva, ali dijeleći samo 5 veza.

Naravno, ovaj se primjer oslanja na poslužiteljski Držati na životu pauza. Kako biste bili sigurni da veze ne umiru prije ponovne upotrebe, preporuča se konfigurirati klijent s Držati na životu strategije (vidi primjer 5.1.).

7. Konfiguriranje vremenskih ograničenja - Istek vremena utičnice pomoću upravitelja veze

Jedino vremensko ograničenje koje se može postaviti u vrijeme kada je konfiguriran upravitelj veze je vremensko ograničenje utičnice:

Primjer 7.1. Postavljanje vremena čekanja utičnice na 5 sekundi

Ruta HttpRoute = nova HttpRoute (nova HttpHost ("www.baeldung.com", 80)); PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); connManager.setSocketConfig (route.getTargetHost (), SocketConfig.custom (). setSoTimeout (5000) .build ());

Za detaljniju raspravu o vremenskim ograničenjima u HttpClientu - pogledajte ovo.

8. Iseljavanje veze

Izbacivanje veze naviklo je na otkrijte neaktivne i istekle veze i zatvorite ih; postoje dvije mogućnosti za to.

  1. Oslanjajući se na HttpClient da biste provjerili je li veza ustajala prije izvršavanja zahtjeva. Ovo je skupa opcija koja nije uvijek pouzdana.
  2. Stvorite nit monitora za zatvaranje praznih i / ili zatvorenih veza.

Primjer 8.1. Postavljanje HttpClient kako biste provjerili postoje li veze

PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). SetDefaultRequestConfig (RequestConfig.custom (). SetStaleConnectionCheckEnabled (true) .build ()) .setConnectionManager (connManager) .build ();

Primjer 8.2. Korištenje navoja zastarjele veze

PoolingHttpClientConnectionManager connManager = novo PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); IdleConnectionMonitorThread staleMonitor = novo IdleConnectionMonitorThread (connManager); staleMonitor.start (); staleMonitor.join (1000);

The IdleConnectionMonitorThreadrazred naveden je u nastavku:

javna klasa IdleConnectionMonitorThread proširuje Thread {private final HttpClientConnectionManager connMgr; privatno hlapljivo booleovsko isključivanje; javni IdleConnectionMonitorThread (SpajanjeHttpClientConnectionManager connMgr) {super (); this.connMgr = connMgr; } @Override public void run () {try {while (! Shutdown) {synchronized (this) {wait (1000); connMgr.closeExpiredConnections (); connMgr.closeIdleConnections (30, TimeUnit.SECONDS); }}} catch (InterruptedException ex) {shutdown (); }} javno void shutdown () {shutdown = true; sinkronizirano (ovo) {notifyAll (); }}}

9. Zatvaranje veze

Veza se može elegantno zatvoriti (vrši se pokušaj ispiranja izlaznog međuspremnika prije zatvaranja) ili prisilno pozivanjem ugasiti metoda (izlazni međuspremnik se ne ispire).

Da bismo pravilno zatvorili veze, moramo učiniti sve sljedeće:

  • potrošiti i zatvoriti odgovor (ako je dostupan)
  • zatvorite klijenta
  • zatvorite i isključite upravitelj veze

Primjer 8.1. Zatvaranje veze i oslobađanje resursa

connManager = novi PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); HttpGet get = novi HttpGet ("// google.com"); CloseableHttpResponse odgovor = client.execute (get); EntityUtils.consume (response.getEntity ()); response.close (); client.close (); connManager.close (); 

Ako se upravitelj isključi, a veze već nisu zatvorene - sve veze će se zatvoriti i svi resursi će se osloboditi.

Važno je imati na umu da ovo neće ukloniti podatke koji su možda bili u tijeku za postojeće veze.

10. Zaključak

U ovom smo članku razgovarali o tome kako koristiti HTTP API za upravljanje vezama HttpClienta za rukovanje cjelokupni proces upravljanja vezama - od otvaranja i dodjele, preko upravljanja istodobnom upotrebom višestrukih agenata, do konačnog zatvaranja.

Vidjeli smo kako BasicHttpClientConnectionManager je jednostavno rješenje za rukovanje pojedinačnim vezama i kako može upravljati niskorazinskim vezama. Također smo vidjeli kako SpajanjeHttpClientConnectionManager u kombinaciji s HttpClient API pružaju učinkovitu uporabu HTTP veza u skladu s protokolom.


$config[zx-auto] not found$config[zx-overlay] not found