Използвах HttpClient неправилно от години, но накрая дойде кошмарът. Уебсайтът ми беше нестабилен и клиентите ми бяха много ядосани, а с едно просто решение производителността значително се подобри и нестабилността беше премахната.
В същото време всъщност подобрих производителността на приложението си с по-ефективно използване на сокета.
Микроуслугите могат да бъдат труден проблем за справяне. С добавянето на повече услуги и разпадането на монолитни приложения, има все повече комуникационни пътища между услугите. Има много възможности за комуникация, но HTTP е много популярен вариант. Ако микросървизът е изграден на C# или на който и да е .NET език, вероятно вече използвате HttpClient.
Проблемът е
Операторът using е C# функция, която обработва еднократни обекти. След като използващият блок е завършен, еднократният обект (в този случай HttpClient) излиза от обхвата и се унищожава. Обадете се на метода за изхвърляне и почистете използваните ресурси. Това е много типичен модел в .NET, който използваме за всичко – от базата данни до flow writer. Всъщност всеки обект с външен ресурс, който трябва да бъде почистен, използва този IDisposable интерфейс.
И не можеш да бъдеш виновен, че искаш да го обвиеш в употреба. Първо, това се счита за добра практика. Всъщност официалната документация на Microsoft използва:
Обикновено, при използване на IDisposable обект, той трябва да бъде деклариран и инстанциран в използващото изявление. Второ, целият код, който може би сте виждали...... Началото на HttpClient ще ви каже да използвате блока за използване, включително най-новата документация ASP.NET самия сайт. Същото се казва и в статията в интернет.
Но HttpClient е различен. Въпреки че реализира IDisposable интерфейса, всъщност е споделен обект. Това означава, че зад кулисите е реентрант и защитен от нишки. Трябва да споделяте инстанция на HttpClient през целия живот на вашето приложение, вместо да създавате нова инстанция за всяко изпълнение. HttpClient, нека видим защо.
Виж сам
Ето една проста програма за демонстрация на HttpClient:
Това ще бъде насочено към http://aspnetmonsters.comОтваряме 10 заявки и правим GET, просто отпечатваме кода на статуса, за да знаем, че работи. Изходът ще бъде:
Чакай, има още!
Всяка работа и всичко е правилно за света. Освен че не е така. Ако извадим netstat инструмента и погледнем състоянието на сокета на машината, която го пуска, ще видим:
C:\code\socket>NETSTAT.EXE ... Състояние на протолокален чуждестранен адрес 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 УЧРЕДЕНА ... Странно е...... Приложението вече е излязло, но все още има много от тези връзки към машината Azure, която хоства уебсайта на ASP.NET Monsters. Те са вътреTIME_WAITстатус, което означава, че връзката е затворена от едната страна (нашата), но все още чакаме да видим дали ще пристигнат други пакети, защото може да са закъснели някъде в мрежата. Ето диаграма на статуса на TCP/IP:
Windows ще остане свързан в това състояние за 240 секунди (както е зададено чрез настройка [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]). Windows има ограничение колко бързо можеш да отвориш нов сокет, така че ако свършат пуловете за връзки, може да видиш грешка като тази:
Не мога да се свържа с отдалечения сървър
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted. Търсенето в Google ще ти даде лоши съвети за намаляване на тайм-аутите на връзката. Всъщност, когато се работи на сървър с HttpClient или подобно конструирано приложение, намаляването на тайм-аутите може да доведе до други неблагоприятни последици. Трябва да разберем какво означава "правилно" и да решим основния проблем, а не да се занимаваме с променливи на машинно ниво.
Оправи го
Ако споделим инстанция на HttpClient, можем да намалим разхода от сокет чрез повторна употреба:
Обърнете внимание, че за цялото приложение имаме само един споделен инстанс. HttpClient все още работи както преди (всъщност малко по-бързо заради повторната употреба на сокета). Netstat вече показва само:
TCP 10.211.55.6:12254 waws-prod-bay-017:http УСТАНОВЕНО В производствен сценарий средният брой сокети е около 4000 и достига връх над 5000, което ефективно изтощава наличните ресурси на сървъра и причинява срив на услугата. След въвеждането на промяната, броят на използваните гнезда спадна от средно над 4000 до постоянно под 400, обикновено около 100.
Това е част от графика от нашия инструмент за мониторинг, която показва какво се случва след като разположим ограничен брой доказателства за поправки към избран брой микросервизи.
Беше драматично. Ако имате какъвто и да е товар, трябва да имате предвид тези две неща:
Направете HttpClient статичен. Не изхвърляйте и не опаковайте използването си, освен ако не търсите конкретно поведение (например да причините провал на услугата). HttpClient
резюме
Проблемът с изтощението на гнездото, с който се борим от месеци, вече е изчезнал и нашите клиенти имат виртуален парад. Не мога да подценя колко неочевидна е тази грешка. През годините сме свикнали да работим с реализирани обекти, IDisposable, а много инструменти за рефакториране като R# и CodeRush всъщност ви предупреждават, ако не го направите. В този случай премахването на HttpClient е грешният подход. HttpClient реализира IDisposable и насърчава лошото поведение, за съжаление
Оригинален:Входът към хиперлинк е видим.
|