HttpClient Connection Management

1. Oversigt

I denne artikel vil vi gennemgå det grundlæggende i forbindelsesadministration i HttpClient 4.

Vi dækker brugen af BasichttpClientConnectionManager og PoolingHttpClientConnectionManager at håndhæve en sikker, protokoloverensstemmende og effektiv brug af HTTP-forbindelser.

2. Den BasicHttpClientConnectionManager til et lavt niveau, enkelt gevindforbindelse

Det BasicHttpClientConnectionManager er tilgængelig siden HttpClient 4.3.3 som den enkleste implementering af en HTTP-forbindelsesadministrator. Det bruges til at oprette og administrere en enkelt forbindelse, der kun kan bruges af en tråd ad gangen.

Eksempel 2.1. Få en forbindelsesanmodning om en lavt niveau-forbindelse (HttpClientConnection)

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

Det requestConnection metoden får fra lederen en pulje af forbindelser til en bestemt rute at oprette forbindelse til. Det rute parameter angiver en rute med "proxy-humle" til målværten eller selve målværten.

Det er muligt at udføre en anmodning ved hjælp af en HttpClientConnection direkte, men husk på, at denne tilgang på lavt niveau er detaljeret og vanskelig at styre. Forbindelser på lavt niveau er nyttige til at få adgang til stikkontakter og forbindelsesdata såsom timeouts og målværtsinformation, men til standardudførelser er HttpClient er en meget lettere API at arbejde imod.

3. Brug af PoolingHttpClientConnectionManager at få og administrere en pulje af multitrådede forbindelser

Det PoolingHttpClientConnectionManager opretter og administrerer en pool af forbindelser for hver rute eller målvært, vi bruger. Standardstørrelsen på den samtidige pool forbindelser der kan være åben af ​​manager er 2 for hver rute eller målværtog 20 i alt åbne forbindelser. Først - lad os se på, hvordan du opretter denne forbindelsesadministrator på en simpel HttpClient:

Eksempel 3.1. Indstilling af PoolingHttpClientConnectionManager på en HttpClient

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

Næste - lad os se, hvordan den samme forbindelsesadministrator kan bruges af to HttpClients, der kører i to forskellige tråde:

Eksempel 3.2. Brug af to HttpClients til at oprette forbindelse til en målvært 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 ();

Bemærk, at vi bruger en meget enkel brugerdefineret trådimplementering - her er det:

Eksempel 3.3. Tilpasset tråd Udførelse af en FÅ anmodning

offentlig klasse MultiHttpClientConnThread udvider tråd {privat CloseableHttpClient-klient; private HttpGet get; // standard konstruktører offentlig ugyldig kørsel () {prøv {HttpResponse respons = client.execute (get); EntityUtils.consume (respons.getEntity ()); } fange (ClientProtocolException ex) {} catch (IOException ex) {}}}

Læg mærke tilEntityUtils.consume (respons.getEntity) opkald - nødvendigt for at forbruge hele indholdet af svaret (enhed), så lederen kan frigør forbindelsen tilbage til puljen.

4. Konfigurer Connection Manager

Standarderne for pooling-forbindelsesadministratoren er velvalgte, men - afhængigt af din brugssag - kan det være for lille. Så - lad os se på, hvordan vi kan konfigurere:

  • det samlede antal forbindelser
  • det maksimale antal forbindelser pr. (enhver) rute
  • det maksimale antal forbindelser pr. en enkelt, specifik rute

Eksempel 4.1. Forøgelse af antallet af forbindelser, der kan åbnes og styres ud over standardgrænserne

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

Lad os sammenfatte API:

  • setMaxTotal (int max): Indstil det maksimale antal samlede åbne forbindelser.
  • setDefaultMaxPerRoute (int max): Indstil det maksimale antal samtidige forbindelser pr. Rute, som standard er 2.
  • setMaxPerRoute (int max): Indstil det samlede antal samtidige forbindelser til en bestemt rute, som standard er 2.

Så uden at ændre standard, vi når grænserne for forbindelsesadministratoren ganske let - lad os se hvordan det ser ud:

Eksempel 4.2. Brug af tråde til at udføre forbindelser

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 diskuteret, grænsen pr. vært er 2 som standard. Så i dette eksempel forsøger vi at få 3 tråde til at lave 3 anmodninger til den samme vært, men kun 2 forbindelser tildeles parallelt.

Lad os se på logfilerne - vi kører tre tråde, men kun 2 lejede forbindelser:

[Tråd-0] INFO obhcMultiHttpClientConnThread - Før - Leasingforbindelser = 0 [Tråd-1] INFO obhcMultiHttpClientConnThread - Før - Leasede forbindelser = 0 [Tråd-2] INFO obhcMultiHttpClientConnThread - Før - Leasetilslutninger = 0 INFO obhcMultiHttpClientConnThread - Efter - Leasingforbindelser = 2 [Tråd-0] INFO obhcMultiHttpClientConnThread - Efter - Leasede forbindelser = 2

5. Forbindelse Keep-Alive strategi

Citering af HttpClient 4.3.3. reference: “Hvis den Holde i live header er ikke til stede i svaret, HttpClient antager, at forbindelsen kan holdes i live på ubestemt tid. ” (Se HttpClient Reference).

For at omgå dette og være i stand til at styre døde forbindelser har vi brug for en tilpasset strategiimplementering og bygger den ind i HttpClient.

Eksempel 5.1. En brugerdefineret Keep Alive-strategi

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

Denne strategi vil først forsøge at anvende værtsens Holde i live politik angivet i overskriften. Hvis disse oplysninger ikke er til stede i svarhovedet, holder de forbindelser i live i 5 sekunder.

Lad os nu oprette en klient med denne brugerdefinerede strategi:

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

6. Tilslutningspersistens / genbrug

HTTP / 1.1 Spec angiver, at forbindelser kan genbruges, hvis de ikke er blevet lukket - dette er kendt som forbindelses persistens.

Når en forbindelse er frigivet af lederen, forbliver den åben til genbrug. Når du bruger en BasicHttpClientConnectionManager, som kun kan mange en enkelt forbindelse, forbindelsen skal frigives, før den leases tilbage:

Eksempel 6.1. BasicHttpClientConnectionManagerGenbrug af forbindelse

BasicHttpClientConnectionManager basicConnManager = ny BasicHttpClientConnectionManager (); HttpClientContext context = HttpClientContext.create (); // HttpRoute-rute på lavt niveau = ny HttpRoute (ny HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = basicConnManager.requestConnection (rute, null); HttpClientConnection conn = connRequest.get (10, TimeUnit.SECONDS); basicConnManager.connect (forbindelse, rute, 1000, kontekst); basicConnManager.routeComplete (forbindelse, 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 (konn, null, 1, TimeUnit.SECONDS); // højt niveau CloseableHttpClient-klient = HttpClients.custom () .setConnectionManager (basicConnManager) .build (); client.execute (get);

Lad os se på, hvad der sker.

Først - bemærk, at vi først bruger en forbindelse på lavt niveau, bare så vi har fuld kontrol over, hvornår forbindelsen frigøres, derefter en normal højere niveauforbindelse med en HttpClient. Den komplekse lavniveaulogik er ikke særlig relevant her - det eneste, vi holder af, er releaseConnection opkald. Det frigiver den eneste tilgængelige forbindelse og gør det muligt at genbruge den.

Derefter udfører klienten GET-anmodningen igen med succes. Hvis vi springer over frigivelsen af ​​forbindelsen, får vi en IllegalStateException fra HttpClient:

java.lang.IllegalStateException: Forbindelse tildeles stadig ved o.a.h.u.Asserts.check (Asserts.java:34) ved o.a.h.i.c.BasicHttpClientConnectionManager.getConnection (BasicHttpClientConnectionManager.java:248)

Bemærk, at den eksisterende forbindelse ikke er lukket, bare frigivet og derefter genbrugt af den anden anmodning.

I modsætning til ovenstående eksempel, The PoolingHttpClientConnectionManager tillader genbrug af forbindelse transparent uden behov for at frigive en forbindelse implicit:

Eksempel 6.2.PoolingHttpClientConnectionManager: Genbrug af forbindelser med tråde

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 [] threads = ny MultiHttpClientConnThread [10]; for (int i = 0; i <threads.length; i ++) {threads [i] = ny MultiHttpClientConnThread (klient, get, connManager); } for (MultiHttpClientConnThread thread: threads) {thread.start (); } for (MultiHttpClientConnThread thread: threads) {thread.join (1000); }

Eksemplet ovenfor har 10 tråde, der udfører 10 anmodninger, men kun deler 5 forbindelser.

Selvfølgelig er dette eksempel afhængig af serverens Holde i live tiden er gået. For at sikre, at forbindelserne ikke dør, før de genbruges, anbefales det at konfigurere klient med en Holde i live strategi (se eksempel 5.1.).

7. Konfiguration af timeouts - Socket-timeout ved hjælp af Connection Manager

Den eneste timeout, der kan indstilles på det tidspunkt, hvor forbindelsesadministrator er konfigureret, er sokkel-timeout:

Eksempel 7.1. Indstilling af sokkeltimeout 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 mere detaljeret diskussion af timeouts i HttpClient - se dette.

8. Udkobling af forbindelse

Forbindelse bortkastning er vant til registrere inaktive og udløbne forbindelser og luk dem; der er to muligheder for at gøre dette.

  1. Stole på HttpClient for at kontrollere, om forbindelsen er gammel, før du udfører en anmodning. Dette er en dyr mulighed, der ikke altid er pålidelig.
  2. Opret en skærmtråd for at lukke inaktiv og / eller lukkede forbindelser.

Eksempel 8.1. Indstilling af HttpClient for at kontrollere for uaktuelle forbindelser

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

Eksempel 8.2. Brug af en gammel forbindelsesmonitortråd

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

Det IdleConnectionMonitorThreadklasse er angivet nedenfor:

offentlig klasse IdleConnectionMonitorThread udvider tråd {privat final HttpClientConnectionManager connectMgr; privat flygtig boolsk nedlukning; public IdleConnectionMonitorThread (PoolingHttpClientConnectionManager connectMgr) {super (); this.connMgr = connMgr; } @ Override public void run () {prøv {mens (! Shutdown) {synkroniseret (dette) {vent (1000); connMgr.closeExpiredConnections (); connMgr.closeIdleConnections (30, TimeUnit.SECONDS); }}} fange (InterruptedException ex) {shutdown (); }} offentlig ugyldig nedlukning () {shutdown = true; synkroniseret (dette) {notifyAll (); }}}

9. Afslutning af forbindelse

En forbindelse kan lukkes yndefuldt (et forsøg på at skylle outputbufferen inden lukning foretages) eller kraftigt ved at ringe til lukke ned metode (outputbufferen skylles ikke).

For at lukke forbindelser korrekt skal vi gøre alt følgende:

  • forbruge og lukke svaret (hvis det kan lukkes)
  • luk klienten
  • luk og luk forbindelsesadministratoren

Eksempel 8.1. Lukning af forbindelse og frigivelse af ressourcer

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.close (); client.close (); connManager.close (); 

Hvis lederen lukkes, uden at forbindelser allerede er lukket, lukkes alle forbindelser, og alle ressourcer frigives.

Det er vigtigt at huske på, at dette ikke vil skylle data, der måtte have været igangværende for de eksisterende forbindelser.

10. Konklusion

I denne artikel diskuterede vi, hvordan du bruger HTTP Connection Management API fra HttpClient til at håndtere hele processen med styring af forbindelser - fra at åbne og tildele dem gennem styring af deres samtidige brug af flere agenter til endelig at lukke dem.

Vi så, hvordan BasicHttpClientConnectionManager er en enkel løsning til håndtering af enkeltforbindelser, og hvordan den kan håndtere forbindelser på lavt niveau. Vi så også, hvordan PoolingHttpClientConnectionManager kombineret med HttpClient API giver en effektiv og protokoloverensstemmende anvendelse af HTTP-forbindelser.


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