Už roky používam HttpClient nesprávne, ale nakoniec prišla nočná mora. Moja webová stránka bola nestabilná a moji zákazníci boli veľmi nahnevaní, a jednoduchým riešením sa výkon výrazne zlepšil a nestabilita bola odstránená.
Zároveň som dokonca zlepšil výkon svojej aplikácie efektívnejším využitím socketu.
Mikroslužby môžu byť náročným problémom na riešenie. Ako sa pridáva viac služieb a monolitické aplikácie sa rozkladajú, zvyčajne je medzi službami čoraz viac komunikačných ciest. Existuje mnoho možností komunikácie, ale HTTP je veľmi populárna možnosť. Ak je mikroservis postavený v C# alebo akomkoľvek .NET jazyku, je pravdepodobné, že už používate HttpClient.
Problém je
Príkaz using je funkcia v jazyku C#, ktorá spracováva jednorazové objekty. Keď je použitý blok dokončený, jednorazový objekt (v tomto prípade HttpClient) je mimo rozsahu a zlikvidovaný. Zavolajte metódu likvidácie a vyčistite všetky použité zdroje. Toto je veľmi typický vzor v .NET, ktorý používame na všetko od databázy až po flow writer. V skutočnosti každý objekt s externým zdrojom, ktorý je potrebné vyčistiť, používa toto IDisposable rozhranie.
A nemôžeš byť vinený za to, že to chceš zabaliť do užívania. Po prvé, považuje sa to za dobrú prax. V skutočnosti oficiálna dokumentácia Microsoftu používa:
Vo všeobecnosti, pri použití objektu IDisposable by mal byť deklarovaný a inštanciovaný v príkaze používanie. Po druhé, všetok kód, ktorý ste možno videli...... Začiatok HttpClient vám povie použiť blok príkazov using vrátane najnovšej dokumentácie ASP.NET samotnej stránke. To isté sa hovorí v článku na internete.
Ale HttpClient je iný. Hoci implementuje rozhranie IDisposable, v skutočnosti ide o zdieľaný objekt. To znamená, že v zákulisí je to reentrant a bezpečné pre thread. Mali by ste zdieľať inštanciu HttpClient počas celej životnosti aplikácie, namiesto vytvárania novej inštancie pre každé spustenie. HttpClient, pozrime sa prečo.
Presvedčte sa sami
Tu je jednoduchý program na demonštráciu HttpClient:
Toto bude smerované na http://aspnetmonsters.comOtvorte 10 požiadaviek a urobte GET, jednoducho vytlačíme stavový kód, takže vieme, že to funguje. Výstup bude:
Počkaj, je toho viac!
Všetka práca a všetko je správne pre svet. Lenže nie je. Ak vyberieme nástroj netstat a pozrieme sa na stav socketu na stroji, ktorý ho beží, uvidíme:
C:\code\socket>NETSTAT.EXE ... Proto miestna adresa Zahraničná adresa štát TCP 10.211.55.6:12050 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12051 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12053 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12054 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12055 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12056 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12057 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12058 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12059 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12060 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12061 waws-prod-bay-017:http TIME_WAIT TCP 10.211.55.6:12062 waws-prod-bay-017:http TIME_WAIT TCP 127.0.0.1:1695 SIMONTIMMS742B:1696 ZALOŽENÝ ... No, je to zvláštne...... Aplikácia už bola ukončená, ale stále je otvorených veľa týchto pripojení na Azure stroj, ktorý hostí webovú stránku ASP.NET Monsters. Sú vTIME_WAITStav, čo znamená, že spojenie bolo uzavreté na jednej strane (na našej), ale stále čakáme, či neprídu ďalšie pakety, pretože mohli byť oneskorené niekde v sieti. Tu je diagram stavu TCP/IP:
Windows zostanú pripojené v tomto stave 240 sekúnd (ako je nastavené nastavením [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]). Windows má limit, ako rýchlo môžete otvoriť nový socket, takže ak vám dôjdu zásoby pripojení, môžete vidieť chybu ako táto:
Nepodarilo sa pripojiť k vzdialenému serveru
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted. Hľadanie na Google vám dá zlé rady, ako znížiť časové limity pripojenia. V skutočnosti, ak správne bežíte na serveri s HttpClient alebo podobne vytvorenou aplikáciou, zníženie časových limitov môže viesť k ďalším negatívnym dôsledkom. Musíme pochopiť, čo znamená "správne" a vyriešiť základný problém, nie sa hrať s premennými na úrovni stroja.
Oprav to
Ak zdieľame inštanciu HttpClient, môžeme znížiť plytvanie socketmi opätovným použitím:
Upozorňujeme, že pre celú aplikáciu máme len jednu zdieľanú inštanciu. HttpClient stále funguje ako predtým (vlastne o niečo rýchlejšie vďaka opakovanému používaniu socketu). Netstat teraz zobrazuje len:
TCP 10.211.55.6:12254 waws-prod-bay-017:http ZALOŽENÉ V produkčnom scenári mám priemerný počet socketov okolo 4000 a vrchol nad 5000, čo efektívne stlačí dostupné zdroje na serveri a spôsobí pád služby. Po zavedení tejto zmeny počet používaných pätic klesol z priemeru viac ako 4000 na konzistentne menej ako 400, typicky okolo 100.
Toto je časť grafu z nášho monitorovacieho nástroja, ktorý ukazuje, čo sa deje po nasadení obmedzeného počtu dôkazov opráv na vybraný počet mikroservisov.
Bolo to dramatické. Ak máte akúkoľvek záťaž, musíte mať na pamäti tieto dve veci:
Urobte svoj HttpClient statický. Nevyhadzujte ani nebalte svoje použitie, pokiaľ výslovne nehľadáte konkrétne správanie (napríklad spôsobenie zlyhania služby). HttpClient
súhrn
Problém s vyčerpaním pätice, s ktorým sme sa trápili už mesiace, je preč a naši zákazníci majú virtuálny sprievod. Nemôžem podceňovať, aká nejasná je táto chyba. Za tie roky sme si zvykli pracovať s implementovanými objektmi, IDisposable a mnohé refaktorovacie nástroje ako R# a CodeRush vás dokonca varujú, ak to neurobíte. V tomto prípade je likvidácia HttpClient nesprávnym prístupom. HttpClient implementuje IDisposable a podporuje zlé správanie, čo je nešťastné
Originál:Prihlásenie na hypertextový odkaz je viditeľné.
|