HttpClient Connection Management

1. Oversikt

I denne artikkelen vil vi gå gjennom det grunnleggende om tilkoblingsadministrasjon i HttpClient 4.

Vi vil dekke bruken av BasichttpClientConnectionManager og PoolingHttpClientConnectionManager for å håndheve en sikker, protokollkompatibel og effektiv bruk av HTTP-tilkoblinger.

2. Den BasicHttpClientConnectionManager for lavt nivå, enkelt gjengetilkobling

De BasicHttpClientConnectionManager er tilgjengelig siden HttpClient 4.3.3 som den enkleste implementeringen av en HTTP-tilkoblingsbehandling. Den brukes til å opprette og administrere en enkelt tilkobling som bare kan brukes av en tråd om gangen.

Eksempel 2.1. Få en tilkoblingsforespørsel om en lavnivåforbindelse (HttpClientConnection)

BasicHttpClientConnectionManager connManager = ny BasicHttpClientConnectionManager (); HttpRoute-rute = ny HttpRoute (ny HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = connManager.requestConnection (rute, null);

De requestConnection metoden får fra lederen en pool av forbindelser for en spesifikk rute å koble til. De rute parameter angir en rute med "proxy-humle" til målverten, eller selve målverdien.

Det er mulig å utføre en forespørsel ved hjelp av en HttpClientConnection direkte, men husk at denne tilnærmingen på lavt nivå er omfattende og vanskelig å håndtere. Forbindelser på lavt nivå er nyttige for tilgang til stikkontakt- og tilkoblingsdata som tidsavbrudd og målvertsinformasjon, men for standardutførelser er HttpClient er et mye enklere API å jobbe mot.

3. Bruke PoolingHttpClientConnectionManager å skaffe og administrere et basseng med flertrådede tilkoblinger

De PoolingHttpClientConnectionManager vil opprette og administrere en samling forbindelser for hver rute eller målvert vi bruker. Standardstørrelsen på poolen av samtidige tilkoblinger som kan være åpen av lederen er 2 for hver rute eller målvert, og 20 for totalt åpne forbindelser. Først - la oss ta en titt på hvordan du konfigurerer denne tilkoblingsadministratoren på en enkel HttpClient:

Eksempel 3.1. Sette PoolingHttpClientConnectionManager på en HttpClient

HttpClientConnectionManager poolingConnManager = ny PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). SetConnectionManager (poolingConnManager) .build (); client.execute (nye HttpGet ("/")); assertTrue (poolingConnManager.getTotalStats (). getLeased () == 1);

Neste - la oss se hvordan den samme tilkoblingsadministratoren kan brukes av to HttpClients som kjører i to forskjellige tråder:

Eksempel 3.2. Bruke to HttpClients for å koble til en målvert hver

HttpGet get1 = ny HttpGet ("/"); HttpGet get2 = ny HttpGet ("// google.com"); PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); CloseableHttpClient client1 = HttpClients.custom (). SetConnectionManager (connManager) .build (); CloseableHttpClient client2 = HttpClients.custom (). SetConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = ny MultiHttpClientConnThread (client1, get1); MultiHttpClientConnThread thread2 = ny MultiHttpClientConnThread (client2, get2); thread1.start (); thread2.start (); thread1.join (); thread2.join ();

Legg merke til at vi bruker en veldig enkel tilpasset trådimplementering - her er det:

Eksempel 3.3. Egendefinert tråd Utføre a FÅ forespørsel

offentlig klasse MultiHttpClientConnThread utvider tråd {private CloseableHttpClient-klient; private HttpGet get; // standard constructors public void run () {try {HttpResponse response = client.execute (get); EntityUtils.consume (respons.getEntity ()); } fangst (ClientProtocolException ex) {} catch (IOException ex) {}}}

Legg merke tilEntityUtils.consume (respons.getEntity) samtale - nødvendig for å konsumere hele innholdet i svaret (enhet) slik at lederen kan frigjør forbindelsen tilbake til bassenget.

4. Konfigurer Connection Manager

Standardene til tilkoblingsadministratoren for pooling er velvalgte, men - avhengig av brukssaken - kan være for små. Så - la oss ta en titt på hvordan vi kan konfigurere:

  • det totale antallet tilkoblinger
  • maksimalt antall forbindelser per (hvilken som helst) rute
  • maksimalt antall forbindelser per enkelt, spesifikk rute

Eksempel 4.1. Øke antall tilkoblinger som kan åpnes og administreres utover standardgrensene

PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); connManager.setMaxTotal (5); connManager.setDefaultMaxPerRoute (4); HttpHost-vert = ny HttpHost ("www.baeldung.com", 80); connManager.setMaxPerRoute (ny HttpRoute (vert), 5);

La oss oppsummere API:

  • setMaxTotal (int max): Still inn maksimalt antall totale åpne tilkoblinger.
  • setDefaultMaxPerRoute (int max): Angi maksimalt antall samtidige forbindelser per rute, som er 2 som standard.
  • setMaxPerRoute (int max): Still inn totalt antall samtidige tilkoblinger til en bestemt rute, som er 2 som standard.

Så uten å endre standard, vi skal nå grensene for tilkoblingsbehandling ganske enkelt - la oss se hvordan det ser ut:

Eksempel 4.2. Bruke tråder til å utføre tilkoblinger

HttpGet get = new HttpGet ("// www.baeldung.com"); PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); CloseableHttpClient-klient = HttpClients.custom (). setConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = ny MultiHttpClientConnThread (klient, get); MultiHttpClientConnThread thread2 = ny MultiHttpClientConnThread (klient, get); MultiHttpClientConnThread thread3 = ny MultiHttpClientConnThread (klient, get); thread1.start (); thread2.start (); thread3.start (); thread1.join (); thread2.join (); thread3.join ();

Som vi allerede har diskutert, per vert-tilkoblingsgrense er 2 som standard. Så i dette eksemplet prøver vi å lage tre tråder 3 forespørsler til samme vert, men bare to forbindelser vil bli tildelt parallelt.

La oss ta en titt på loggene - vi har tre tråder som kjører, men bare to leide tilkoblinger:

[Thread-0] INFO obhcMultiHttpClientConnThread - Before - Leased Connections = 0 [Thread-1] INFO obhcMultiHttpClientConnThread - Before - Leased Connections = 0 [Thread-2] INFO obhcMultiHttpClientConnThread - Before - Leased Connections = 0 INFO obhcMultiHttpClientConnThread - Etter - Leide tilkoblinger = 2 [Tråd-0] INFO obhcMultiHttpClientConnThread - Etter - Leide tilkoblinger = 2

5. Forbindelse Keep-Alive strategi

Sitere HttpClient 4.3.3. referanse: “Hvis Holde i live header er ikke til stede i svaret, HttpClient antar at forbindelsen kan holdes i live på ubestemt tid. ” (Se HttpClient-referanse).

For å komme rundt dette, og være i stand til å administrere døde forbindelser, trenger vi en tilpasset strategiimplementering og bygge den inn i HttpClient.

Eksempel 5.1. En tilpasset Keep Alive-strategi

ConnectionKeepAliveStrategy myStrategy = ny ConnectionKeepAliveStrategy () {@Override public long getKeepAliveDuration (HttpResponse respons, HttpContext context) {HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator) while (it.hasNext ()) {HeaderElement he = it.nextElement (); String param = he.getName (); Strengverdi = he.getValue (); hvis (verdi! = null && param.equalsIgnoreCase ("timeout")) {return Long.parseLong (verdi) * 1000; }} returner 5 * 1000; }};

Denne strategien vil først prøve å bruke vertsens Holde i live policy angitt i overskriften. Hvis denne informasjonen ikke er tilstede i svarhodet, vil den holde tilkoblingene i live i 5 sekunder.

Nå - la oss lage en klient med denne tilpassede strategien:

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

6. Tilkoblingspersistens / gjenbruk

HTTP / 1.1-spesifikasjonen sier at tilkoblinger kan brukes på nytt hvis de ikke er stengt - dette er kjent som tilkoblingsresistens.

Når en forbindelse er utgitt av lederen, forblir den åpen for gjenbruk. Når du bruker en BasicHttpClientConnectionManager, som bare kan ødelegge en enkelt forbindelse, må forbindelsen frigjøres før den leies tilbake igjen:

Eksempel 6.1. BasicHttpClientConnectionManagerGjenbruk av tilkobling

BasicHttpClientConnectionManager basicConnManager = ny BasicHttpClientConnectionManager (); HttpClientContext context = HttpClientContext.create (); // lavt nivå HttpRoute-rute = ny HttpRoute (ny HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = basicConnManager.requestConnection (rute, null); HttpClientConnection conn = connRequest.get (10, TimeUnit.SECONDS); basicConnManager.connect (tilkobling, rute, 1000, kontekst); basicConnManager.routeComplete (tilkobling, rute, kontekst); HttpRequestExecutor exeRequest = ny HttpRequestExecutor (); context.setTargetHost ((ny HttpHost ("www.baeldung.com", 80))); HttpGet get = new HttpGet ("// www.baeldung.com"); exeRequest.execute (get, conn, context); basicConnManager.releaseConnection (conn, null, 1, TimeUnit.SECONDS); // høyt nivå CloseableHttpClient-klient = HttpClients.custom () .setConnectionManager (basicConnManager) .build (); client.execute (get);

La oss se på hva som skjer.

Først - legg merke til at vi bruker en lavnivåforbindelse først, bare slik at vi har full kontroll over når forbindelsen blir utgitt, deretter en normal høyere nivåforbindelse med en HttpClient. Den komplekse lavnivålogikken er ikke veldig relevant her - det eneste vi bryr oss om er releaseConnection anrop. Det frigjør den eneste tilgjengelige tilkoblingen og lar den brukes på nytt.

Deretter utfører klienten GET-forespørselen igjen med suksess. Hvis vi hopper over å frigjøre forbindelsen, får vi et IllegalStateException fra HttpClient:

java.lang.IllegalStateException: Forbindelse er fortsatt tildelt o.a.h.u.Asserts.check (Asserts.java:34) på ​​o.a.h.i.c.BasicHttpClientConnectionManager.getConnection (BasicHttpClientConnectionManager.java:248)

Merk at den eksisterende forbindelsen ikke er lukket, bare utgitt og deretter brukt på nytt av den andre forespørselen.

I motsetning til eksemplet ovenfor, The PoolingHttpClientConnectionManager tillater gjenbruk av forbindelse transparent uten behov for å frigjøre en forbindelse implisitt:

Eksempel 6.2.PoolingHttpClientConnectionManager: Gjenbruk av forbindelser med tråder

HttpGet get = new HttpGet ("// echo.200please.com"); PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); connManager.setDefaultMaxPerRoute (5); connManager.setMaxTotal (5); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); MultiHttpClientConnThread [] tråder = ny MultiHttpClientConnThread [10]; for (int i = 0; i <threads.length; i ++) {threads [i] = new MultiHttpClientConnThread (client, get, connManager); } for (MultiHttpClientConnThread thread: threads) {thread.start (); } for (MultiHttpClientConnThread thread: threads) {thread.join (1000); }

Eksemplet ovenfor har 10 tråder, og utfører 10 forespørsler, men deler bare 5 tilkoblinger.

Selvfølgelig er dette eksemplet avhengig av serverens Holde i live pause. For å sikre at tilkoblingene ikke dør før de brukes på nytt, anbefales det å konfigurere klient med en Holde i live strategi (se eksempel 5.1.).

7. Konfigurere tidsavbrudd - Socket-tidsavbrudd ved hjelp av Connection Manager

Den eneste tidsavbruddet som kan angis på det tidspunktet tilkoblingsadministratoren er konfigurert, er stikkontakten:

Eksempel 7.1. Sette tidsavbrudd for stikkontakt til 5 sekunder

HttpRoute-rute = ny HttpRoute (ny HttpHost ("www.baeldung.com", 80)); PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); connManager.setSocketConfig (route.getTargetHost (), SocketConfig.custom (). setSoTimeout (5000) .build ());

For en mer inngående diskusjon av tidsavbrudd i HttpClient - se dette.

8. Koblingsutkastelse

Koblingsutsetting er vant til oppdage inaktiv og utløpte tilkoblinger og lukk dem; det er to alternativer for å gjøre dette.

  1. Stole på HttpClient for å sjekke om forbindelsen er foreldet før du utfører en forespørsel. Dette er et dyrt alternativ som ikke alltid er pålitelig.
  2. Opprett en skjermtråd for å lukke inaktiv og / eller lukkede forbindelser.

Eksempel 8.1. Sette inn HttpClient for å se etter foreldede tilkoblinger

PoolingHttpClientConnectionManager connManager = ny PoolingHttpClientConnectionManager (); CloseableHttpClient-klient = HttpClients.custom (). SetDefaultRequestConfig (RequestConfig.custom (). SetStaleConnectionCheckEnabled (true) .build ()) .setConnectionManager (connManager) .build ();

Eksempel 8.2. Bruke en foreldet tilkoblingsmonitortråd

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

De IdleConnectionMonitorThreadklassen er oppført nedenfor:

public class IdleConnectionMonitorThread extends Thread {private final HttpClientConnectionManager connectMgr; privat flyktig boolsk nedleggelse; public IdleConnectionMonitorThread (PoolingHttpClientConnectionManager connectMgr) {super (); this.connMgr = connMgr; } @ Override public void run () {prøv {mens (! Shutdown) {synkronisert (dette) {vent (1000); connMgr.closeExpiredConnections (); connMgr.closeIdleConnections (30, TimeUnit.SECONDS); }}} fangst (InterruptedException ex) {shutdown (); }} offentlig ugyldig nedleggelse () {shutdown = true; synkronisert (dette) {notifyAll (); }}}

9. Avslutning av tilkobling

En forbindelse kan lukkes elegant (et forsøk på å skylle ut utgangsbufferen før lukking er gjort), eller med kraft, ved å ringe til skru av metode (utgangsbufferen skylles ikke ut).

For å lukke forbindelser riktig må vi gjøre alt følgende:

  • forbruker og lukker svaret (hvis det kan lukkes)
  • lukk klienten
  • lukk og slå av tilkoblingsbehandling

Eksempel 8.1. Lukke tilkobling og frigjøre ressurser

connManager = ny PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); HttpGet get = new HttpGet ("// google.com"); CloseableHttpResponse respons = client.execute (get); EntityUtils.consume (respons.getEntity ()); respons.lukk (); client.close (); connManager.close (); 

Hvis lederen er stengt uten at tilkoblinger allerede er stengt - blir alle tilkoblinger stengt og alle ressurser frigitt.

Det er viktig å huske på at dette ikke vil spyle data som kan ha pågått for eksisterende forbindelser.

10. Konklusjon

I denne artikkelen diskuterte vi hvordan du bruker HTTP Connection Management API for HttpClient til å håndtere hele prosessen med å administrere forbindelser - fra å åpne og tildele dem, gjennom å administrere deres samtidige bruk av flere agenter, til endelig å lukke dem.

Vi så hvordan BasicHttpClientConnectionManager er en enkel løsning for å håndtere enkeltforbindelser, og hvordan den kan håndtere forbindelser på lavt nivå. Vi så også hvordan PoolingHttpClientConnectionManager kombinert med HttpClient API gir en effektiv og protokollkompatibel bruk av HTTP-tilkoblinger.


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