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ť.
- 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á.
- 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.