HttpClient jsem používal špatně už roky, ale nakonec přišel ten noční můra. Moje webové stránky byly nestabilní a zákazníci velmi naštvaní, a díky jednoduché opravě se výkon výrazně zlepšil a nestabilita byla odstraněna.
Zároveň jsem skutečně zlepšil výkon své aplikace díky efektivnějšímu využití socketů.
Mikroslužby mohou být obtížný problém. Jak přibývá více služeb a monolitické aplikace se rozkládají, obvykle existuje stále více komunikačních cest mezi službami. Existuje mnoho možností komunikace, ale HTTP je velmi oblíbená volba. Pokud je mikroservis postavený v C# nebo jakémkoli .NET jazyce, je pravděpodobné, že už používáte HttpClient.
Problém je
Příkaz using je funkce v C#, která zpracovává jednorázové objekty. Jakmile je blok použití dokončen, jednorázový objekt (v tomto případě HttpClient) je mimo rozsah a zlikvidován. Zavolejte metodu likvidace a ukliďte všechny používané zdroje. Toto je velmi typický vzorec v .NET, který používáme pro vše od databáze až po flow writer. Ve skutečnosti každý objekt s externím zdrojem, který je třeba vyčistit, používá toto IDisposable rozhraní.
A nemůžete být obviněni, že to chcete zabalit do používání. Za prvé, je považováno za dobrou praxi. Oficiální dokumentace Microsoftu ve skutečnosti používá:
Obecně platí, že při použití objektu IDisposable by měl být deklarován a instancován v příkazu using (použití). Za druhé, všechen kód, který jste možná viděli...... Na začátku HttpClient se objeví příkazový blok používající, včetně nejnovější dokumentace ASP.NET samotném webu. Totéž je uvedeno v článku na internetu.
Ale HttpClient je jiný. Ačkoliv implementuje rozhraní IDisposable, ve skutečnosti jde o sdílený objekt. To znamená, že v zákulisí je to reentrant a bezpečné pro vlákna. Měli byste sdílet instanci HttpClient po celou dobu životnosti aplikace, místo abyste vytvářeli novou instanci pro každé vykonání. HttpClient, podívejme se proč.
Přesvědčte se sami
Tady je jednoduchý program, který demonstruje HttpClient:
Toto bude směrováno na http://aspnetmonsters.comOtevřete 10 požadavků a uděláte GET, jen vytiskneme stavový kód, takže víme, že to funguje. Výstup bude:
Počkej, je toho víc!
Všechno a všechno je správné pro svět. Jenže není. Pokud vytáhneme nástroj netstat a podíváme se na stav socketu na stroji, který ho běží, uvidíme:
C:\code\socket>NETSTAT.EXE ... Proto místní adresa Zahraniční adresa stá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ŽENO ... No, je to divné...... Aplikace už byla ukončena, ale stále je otevřeno spousta těchto připojení na Azure stroji, který hostuje web ASP.NET Monsters. Jsou vTIME_WAITcož znamená, že spojení bylo na jedné straně (naše) uzavřeno, ale stále čekáme, jestli nepřijdou další pakety, protože mohly být někde v síti zpožděné. Zde je diagram stavu TCP/IP:
Windows zůstanou připojena v tomto stavu po dobu 240 sekund (jak je nastaveno nastavením [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]). Windows má omezení, jak rychle můžete otevřít nový socket, takže pokud vám dojdou pooly připojení, můžete se setkat s chybou jako tato:
Nemohu se připojit k vzdálenému serveru
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted. Hledání na Googlu vám dá špatné rady, jak snížit časové výpadky připojení. Ve skutečnosti, pokud správně běží na serveru s HttpClientem nebo podobně navrženou aplikací, může snížení časových limitů vést k dalším negativním důsledkům. Musíme pochopit, co znamená "správné" a vyřešit základní problém, ne si hrát s proměnnými na úrovni strojů.
Oprav to
Pokud sdílíme instanci HttpClient, můžeme snížit plýtvání sockety jejich opětovným použitím:
Všimněte si, že pro celou aplikaci máme pouze jednu sdílenou instanci. HttpClient stále funguje jako dřív (vlastně o něco rychleji díky opětovnému použití socketu). Netstat nyní ukazuje jen:
TCP 10.211.55.6:12254 waws-prod-bay-017:http ZALOŽENO V produkčním scénáři mám průměrný počet socketů kolem 4000 a vrchol přes 5000, což efektivně stlačuje dostupné zdroje na serveru a způsobuje pád služby. Po zavedení této změny počet používaných socketů klesl z průměru přes 4000 na konzistentně méně než 400, obvykle kolem 100.
Toto je část grafu z našeho monitorovacího nástroje, který ukazuje, co se děje poté, co nasadíme omezený počet důkazů oprav na vybraný počet mikroservisů.
Bylo to dramatické. Pokud máte jakoukoli zátěž, musíte mít na paměti tyto dvě věci:
Nastavte svůj HttpClient statický. Nevyhazujte ani nebalte své použití, pokud výslovně nehledáte konkrétní chování (například způsobení selhání služby). HttpClient
shrnutí
Problém s vyčerpáním socketu, se kterým jsme bojovali měsíce, je pryč a naši zákazníci mají virtuální průvod. Nemohu podcenit, jak nezřejmá je tato chyba. Během let jsme byli zvyklí pracovat s implementovanými objekty, IDisposable a mnoho refaktorovacích nástrojů jako R# a CodeRush vás dokonce varuje, pokud to neuděláte. V tomto případě je likvidace HttpClient špatným přístupem. HttpClient implementuje IDisposable a podporuje špatné chování je bohužel
Původní:Přihlášení k hypertextovému odkazu je viditelné.
|