Správa pripojenia HttpClient

1. Prehľad

V tomto článku si prejdeme základné informácie o správe pripojení v rámci HttpClient 4.

Pokryjeme použitie BasichttpClientConnectionManager a PoolingHttpClientConnectionManager vynútiť bezpečné, protokolom vyhovujúce a efektívne využitie pripojení HTTP.

2. The BasicHttpClientConnectionManager pre nízkoúrovňové pripojenie s jedným závitom

The BasicHttpClientConnectionManager je k dispozícii od verzie HttpClient 4.3.3 ako najjednoduchšia implementácia správcu pripojenia HTTP. Používa sa na vytvorenie a správu jedného spojenia, ktoré môže súčasne používať iba jedno vlákno.

Príklad 2.1. Získanie žiadosti o pripojenie na nízkoúrovňové pripojenie (HttpClientConnection)

BasicHttpClientConnectionManager connManager = nový BasicHttpClientConnectionManager (); Trasa HttpRoute = nová HttpRoute (nová HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = connManager.requestConnection (route, null);

The requestConnection metóda získa od manažéra skupinu pripojení pre konkrétny trasa pripojiť sa k. The trasa parameter určuje cestu „proxy chmeľu“ k cieľovému hostiteľovi alebo k samotnému cieľovému hostiteľovi.

Je možné vykonať požiadavku pomocou HttpClientConnection priamo, ale nezabudnite, že tento prístup na nízkej úrovni je podrobný a ťažko zvládnuteľný. Nízkoúrovňové pripojenia sú užitočné na prístup k údajom o sokete a pripojení, ako sú napríklad časové limity a informácie o cieľovom hostiteľovi, ale pri štandardných spusteniach HttpClient je oveľa jednoduchšie pracovať s API.

3. Pomocou PoolingHttpClientConnectionManager na získanie a správu skupiny viacvláknových pripojení

The PoolingHttpClientConnectionManager vytvorí a spravuje skupinu pripojení pre každú trasu alebo cieľového hostiteľa, ktorú používame. Predvolená veľkosť spoločného fondu spojenia ktoré môže manažér otvoriť, je 2 pre každú trasu alebo cieľového hostiteľaa 20 celkom otvorené spojenia. Najprv - poďme sa pozrieť na to, ako nastaviť tohto správcu pripojenia na jednoduchom HttpClient:

Príklad 3.1. Nastavenie PoolingHttpClientConnectionManager na HttpClient

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

Ďalej - pozrime sa, ako môžu toho istého správcu pripojení použiť dvaja klienti HttpClients spustení v dvoch rôznych vláknach:

Príklad 3.2. Používanie dvoch klientov Http na pripojenie k jednému cieľovému hostiteľovi

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

Všimnite si, že používame veľmi jednoduchá implementácia vlastného vlákna - tu to je:

Príklad 3.3. Vlastné vlákno Vykonávanie a ZÍSKAJTE žiadosť

verejná trieda MultiHttpClientConnThread rozširuje vlákno {private CloseableHttpClient klient; súkromné ​​HttpGet get; // štandardné konštruktory public void run () {try {HttpResponse response = client.execute (get); EntityUtils.consume (response.getEntity ()); } catch (ClientProtocolException ex) {} catch (IOException ex) {}}}

Všimnite siEntityUtils.consume (response.getEntity) volanie - potrebné spotrebovať celý obsah odpovede (entity) tak, aby to manažér mohol uvoľnite spojenie späť do bazéna.

4. Nakonfigurujte Správcu pripojení

Predvolené nastavenia správcu pripojenia k združeniu sú dobre zvolené, ale - v závislosti od vášho prípadu použitia - môžu byť príliš malé. Poďme sa teda pozrieť na to, ako môžeme nakonfigurovať:

  • celkový počet pripojení
  • maximálny počet pripojení na (ľubovoľnú) trasu
  • maximálny počet pripojení na jednu konkrétnu trasu

Príklad 4.1. Zvyšovanie počtu pripojení, ktoré je možné otvoriť a spravovať nad predvolené limity

PoolingHttpClientConnectionManager connManager = nový PoolingHttpClientConnectionManager (); connManager.setMaxTotal (5); connManager.setDefaultMaxPerRoute (4); HttpHost hostiteľ = nový HttpHost ("www.baeldung.com", 80); connManager.setMaxPerRoute (nový HttpRoute (hostiteľ), 5);

Zrekapitulujme si API:

  • setMaxTotal (int max): Nastavte maximálny počet celkových otvorených pripojení.
  • setDefaultMaxPerRoute (int max): Nastavte maximálny počet súbežných pripojení na trase, ktorý je predvolene 2.
  • setMaxPerRoute (int max): Nastaví celkový počet súbežných pripojení na konkrétnu trasu, ktorá je predvolene 2.

Takže bez zmeny predvoleného nastavenia dosiahneme limity správcu spojenia celkom ľahko - pozrime sa, ako to vyzerá:

Príklad 4.2. Používanie vlákien na uskutočňovanie pripojení

HttpGet get = new HttpGet ("// www.baeldung.com"); PoolingHttpClientConnectionManager connManager = nový PoolingHttpClientConnectionManager (); CloseableHttpClient client = HttpClients.custom (). setConnectionManager (connManager) .build (); MultiHttpClientConnThread thread1 = nový MultiHttpClientConnThread (klient, získať); MultiHttpClientConnThread thread2 = nový MultiHttpClientConnThread (klient, získať); MultiHttpClientConnThread thread3 = nový MultiHttpClientConnThread (klient, získať); thread1.start (); thread2.start (); thread3.start (); thread1.join (); thread2.join (); thread3.join ();

Ako sme už diskutovali, limit pripojenia na hostiteľa je 2 predvolene. V tomto príklade sa teda snažíme o vytvorenie troch vlákien 3 požiadavky na toho istého hostiteľa, ale paralelne budú pridelené iba 2 spojenia.

Poďme sa pozrieť na protokoly - máme spustené tri vlákna, ale iba 2 prenajaté pripojenia:

[Thread-0] INFO obhcMultiHttpClientConnThread - Before - Lease Connections = 0 [Thread-1] INFO obhcMultiHttpClientConnThread - Before - Lease Connections = 0 [Thread-2] INFO obhcMultiHttpClientConnThread - Before - Lease Connections = 0 INFO obhcMultiHttpClientConnThread - po - prenajaté pripojenia = 2 [vlákno-0] INFO obhcMultiHttpClientConnThread - po - prenajaté pripojenia = 2

5. Stratégia Keep-Alive spojenia

Citácia HttpClient 4.3.3. odkaz: „Ak Udržať nažive hlavička v odpovedi nie je, HttpClient predpokladá, že spojenie môže zostať pri živote neurčito. “ (Pozri referenciu HttpClient).

Aby sme to obišli a boli schopní spravovať mŕtve spojenia, potrebujeme prispôsobenú implementáciu stratégie a zabudujeme ju do HttpClient.

Príklad 5.1. Vlastná stratégia Keep Alive

ConnectionKeepAliveStrategy myStrategy = new ConnectionKeepAliveStrategy () {@Override public long getKeepAliveDuration (HttpResponse response, HttpContext context) {HeaderElementIterator it = new BasicHeaderElementIterator (HTTP.headerIterator (response.headerIterator) while (it.hasNext ()) {HeaderElement he = it.nextElement (); Reťazec param = he.getName (); Hodnota reťazca = he.getValue (); if (value! = null && param.equalsIgnoreCase ("timeout")) {return Long.parseLong (value) * 1000; }} vrátiť 5 * 1000; }};

Táto stratégia sa najskôr pokúsi použiť hostiteľa Udržať nažive politika uvedená v záhlaví. Ak táto informácia nie je v hlavičke odpovede, bude spojenie udržiavané na 5 sekúnd.

Teraz - vytvorme klienta s touto vlastnou stratégiou:

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

6. Perzistencia / opätovné použitie spojenia

Špecifikácia HTTP / 1.1 uvádza, že spojenia je možné znova použiť, ak neboli ukončené - toto sa označuje ako perzistencia spojenia.

Keď správca uvoľní pripojenie, zostane otvorené na opätovné použitie. Pri použití a BasicHttpClientConnectionManager, ktoré môžu spravovať iba jedno pripojenie, musí byť pripojenie uvoľnené skôr, ako bude znovu prenajaté:

Príklad 6.1. BasicHttpClientConnectionManagerOpätovné použitie pripojenia

BasicHttpClientConnectionManager basicConnManager = nový BasicHttpClientConnectionManager (); HttpClientContext context = HttpClientContext.create (); // low level HttpRoute route = new HttpRoute (new HttpHost ("www.baeldung.com", 80)); ConnectionRequest connRequest = basicConnManager.requestConnection (trasa, null); HttpClientConnection conn = connRequest.get (10, TimeUnit.SECONDS); basicConnManager.connect (conn, route, 1000, context); basicConnManager.routeComplete (conn, route, context); HttpRequestExecutor exeRequest = nový HttpRequestExecutor (); context.setTargetHost ((nový HttpHost ("www.baeldung.com", 80))); HttpGet get = new HttpGet ("// www.baeldung.com"); exeRequest.execute (get, conn, context); basicConnManager.releaseConnection (conn, null, 1, TimeUnit.SECONDS); // vysoká úroveň klienta CloseableHttpClient = HttpClients.custom () .setConnectionManager (basicConnManager) .build (); client.execute (get);

Pozrime sa, čo sa stane.

Najprv - všimnite si, že najskôr používame pripojenie na nízkej úrovni, len aby sme mali úplnú kontrolu nad tým, kedy sa pripojenie uvoľní, potom normálne pripojenie na vyššej úrovni s HttpClient. Komplexná logika na nízkej úrovni tu nie je veľmi relevantná - jediné, na čom nám záleží, je releaseConnection hovor. Uvoľní sa tak jediné dostupné pripojenie a umožní sa jeho opätovné použitie.

Potom klient úspešne vykoná požiadavku GET. Ak preskočíme uvoľnenie spojenia, dostaneme IllegalStateException z HttpClient:

java.lang.IllegalStateException: Pripojenie je stále alokované na o.a.h.u.Asserts.check (Asserts.java:34) na o.a.h.i.c.BasicHttpClientConnectionManager.getConnection (BasicHttpClientConnectionManager.java:248)

Upozorňujeme, že existujúce pripojenie nie je ukončené, iba vydané a potom znova použité druhou požiadavkou.

Na rozdiel od vyššie uvedeného príkladu PoolingHttpClientConnectionManager umožňuje transparentné opakované použitie spojenia bez nutnosti implicitného spojenia:

Príklad 6.2.PoolingHttpClientConnectionManager: Opätovné použitie pripojení pomocou vlákien

HttpGet get = new HttpGet ("// echo.200please.com"); PoolingHttpClientConnectionManager connManager = nový PoolingHttpClientConnectionManager (); connManager.setDefaultMaxPerRoute (5); connManager.setMaxTotal (5); CloseableHttpClient client = HttpClients.custom () .setConnectionManager (connManager) .build (); MultiHttpClientConnThread [] vlákna = nové MultiHttpClientConnThread [10]; for (int i = 0; i <threads.length; i ++) {threads [i] = new MultiHttpClientConnThread (client, get, connManager); } pre (vlákno MultiHttpClientConnThread: vlákna) {thread.start (); } pre (vlákno MultiHttpClientConnThread: vlákna) {thread.join (1000); }

Vyššie uvedený príklad má 10 vlákien, vykonáva 10 požiadaviek, ale zdieľa iba 5 pripojení.

Tento príklad sa samozrejme spolieha na server Udržať nažive čas vypršal. Aby ste sa uistili, že spojenia nezomrú pred opätovným použitím, odporúča sa nakonfigurovať zákazník s Udržať nažive stratégia (pozri príklad 5.1.).

7. Konfigurácia časových limitov - časový limit zásuvky pomocou Správcu pripojení

Jediný časový limit, ktorý je možné nastaviť v čase, keď je nakonfigurovaný správca pripojenia, je časový limit soketu:

Príklad 7.1. Nastavenie časového limitu zásuvky na 5 sekúnd

Trasa HttpRoute = nová HttpRoute (nová HttpHost ("www.baeldung.com", 80)); PoolingHttpClientConnectionManager connManager = nový PoolingHttpClientConnectionManager (); connManager.setSocketConfig (route.getTargetHost (), SocketConfig.custom (). setSoTimeout (5000) .build ());

Podrobnejšiu diskusiu o časových limitoch v HttpClient nájdete tu.

8. Vysťahovanie spojenia

Vysťahovanie spojenia je zvyknuté zistiť nečinné a vypršané spojenia a zavrieť ich; existujú dve možnosti, ako to urobiť.

  1. Spoliehajúc sa na HttpClient pred vykonaním žiadosti skontrolovať, či je pripojenie zastarané. Toto je drahá možnosť, ktorá nie je vždy spoľahlivá.
  2. Vytvorte vlákno monitora na uzavretie nečinných a / alebo uzavretých pripojení.

Príklad 8.1. Nastavenie HttpClient skontrolovať staré pripojenie

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

Príklad 8.2. Používanie vlákna monitora zastaraného pripojenia

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

The IdleConnectionMonitorThreadtrieda je uvedená nižšie:

verejná trieda IdleConnectionMonitorThread rozširuje vlákno {private final HttpClientConnectionManager connMgr; súkromné ​​nestabilné boolovské vypnutie; public IdleConnectionMonitorThread (PoolingHttpClientConnectionManager 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 (); }} public void shutdown () {shutdown = true; synchronized (this) {notifyAll (); }}}

9. Uzatváranie spojenia

Pripojenie je možné elegantne uzavrieť (uskutoční sa pokus o vyprázdnenie výstupnej medzipamäte pred zatvorením) alebo násilne zavolaním vypnúť metóda (výstupný buffer nie je vyprázdnený).

Na správne uzavretie pripojení musíme urobiť všetko nasledovne:

  • spotrebujte a zatvorte odpoveď (ak je to možné)
  • zavrieť klienta
  • zatvorte a vypnite správcu pripojenia

Príklad 8.1. Ukončenie pripojenia a uvoľnenie zdrojov

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

Ak je správca vypnutý bez toho, aby už boli uzavreté spojenia - všetky spojenia budú uzavreté a všetky zdroje uvoľnené.

Je dôležité mať na pamäti, že to nevyprázdni žiadne údaje, ktoré mohli prebiehať pre existujúce pripojenia.

10. Záver

V tomto článku sme diskutovali o tom, ako používať rozhranie HTTP Connection Management API rozhrania HttpClient na spracovanie celý proces správy pripojení - od ich otvorenia a pridelenia cez správu ich súbežného použitia viacerými agentmi až po ich definitívne uzavretie.

Videli sme, ako BasicHttpClientConnectionManager je jednoduché riešenie na zvládnutie jednotlivých pripojení a na to, ako dokáže spravovať pripojenia na nízkej úrovni. Tiež sme videli, ako PoolingHttpClientConnectionManager v kombinácii s HttpClient API poskytuje efektívne a protokolovo kompatibilné použitie pripojení HTTP.


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