Jag har använt HttpClient fel i flera år, men till slut kom mardrömmen. Min webbplats var instabil och mina kunder var mycket arga, och med en enkel lösning förbättrades prestandan avsevärt och instabiliteten försvann.
Samtidigt förbättrade jag faktiskt prestandan i min applikation med effektivare socket-användning.
Mikrotjänster kan vara ett svårt problem att hantera. När fler tjänster läggs till och monolitiska applikationer bryts ner, tenderar det att finnas fler och fler kommunikationsvägar mellan tjänster. Det finns många kommunikationsalternativ, men HTTP är ett mycket populärt alternativ. Om mikrotjänsten är byggd i C# eller något annat .NET-språk är chansen stor att du redan använder HttpClient.
Problemet ligger
Satsen using är en C#-funktion som hanterar engångsobjekt. När blocket är klart är engångsobjektet (i detta fall HttpClient) utanför omfattningen och tas bort. Kalla på avfallsmetoden och rensa upp alla resurser som används. Detta är ett mycket typiskt mönster i .NET som vi använder för allt från databasen till flödesskrivaren. Faktum är att alla objekt med en extern resurs som måste rensas använder det där IDISPOSABLE-gränssnittet.
Och man kan inte klandra dig för att vilja slå in det i att använda. För det första anses det vara en god praxis att göra det. Faktum är att Microsofts officiella dokumentation använder:
Generellt bör ett IDisposable-objekt deklareras och instansieras i using-satsen. För det andra, all kod du kanske har sett...... I början av HttpClient kommer du att säga att du ska använda blocket using statement, inklusive den senaste dokumentationen ASP.NET själva sidan. Samma sak sägs i artikeln på internet.
Men HttpClient är annorlunda. Även om det implementerar det IDISPOSABLE-gränssnittet är det faktiskt ett delat objekt. Detta innebär att det bakom kulisserna är återkommit och trådsäkert. Du bör dela en instans av HttpClient under hela applikationens livstid, istället för att skapa en ny instans för varje körning. HttpClient, låt oss se varför.
Se själv
Här är ett enkelt program för att demonstrera HttpClient:
Detta kommer att riktas till http://aspnetmonsters.comÖppna 10 förfrågningar och gör GET, vi skriver bara ut statuskoden så vi vet att det fungerar. Resultatet blir:
Vänta, det finns mer!
Allt arbete och allt är rätt för världen. Men det är det inte. Om vi tar fram netstat-verktyget och tittar på socket-statusen på maskinen som kör det, ser vi:
C:\code\socket>NETSTAT.EXE ... Proto lokal adress Utländsk adress Delstat 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 ETABLERAD ... Det är konstigt...... Applikationen har avslutats, men det finns fortfarande många av dessa anslutningar öppna till Azure-maskinen som är värd för ASP.NET Monsters-webbplatsen. De är inneTIME_WAITstatus, vilket betyder att anslutningen har stängts på ena sidan (vår), men vi väntar fortfarande på att se om några andra paket kommer in eftersom de kan ha blivit försenade någonstans i nätverket. Här är ett diagram över TCP/IP-statusen:
Windows förblir anslutet i detta tillstånd i 240 sekunder (enligt inställning av [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]). Windows har en gräns för hur snabbt du kan öppna en ny socket, så om du får slut på anslutningspooler kan du se ett fel som detta:
Kan inte ansluta till fjärrservern
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted. Att söka på Google ger dig dåliga råd om hur man minskar anslutningstider. Faktum är att när man korrekt körs på en server med HttpClient eller en liknande applikation kan minskade timeouts leda till andra negativa konsekvenser. Vi behöver förstå vad "rätt" betyder och lösa det underliggande problemet, inte pilla med maskinnivåvariabler.
Fixa det
Om vi delar en instans av HttpClient kan vi minska socket-slöseri genom att återanvända dem:
Observera att vi för hela applikationen bara har en delad instans. HttpClient fungerar fortfarande som tidigare (faktiskt lite snabbare på grund av socket-återanvändning). Netstat visar nu bara:
TCP 10.211.55.6:12254 waws-prod-bay-017:http ETABLERAD I ett produktionsscenario ligger mitt socket-antal i genomsnitt runt 4000 och når sin topp på över 5000, vilket effektivt pressar de tillgängliga resurserna på servern och orsakar att tjänsten kraschar. Efter att förändringen implementerats sjönk antalet uttag i bruk från ett genomsnitt på över 4000 till konsekvent färre än 400, vanligtvis runt 100.
Detta är en del av ett diagram från vårt övervakningsverktyg som visar vad som händer efter att vi distribuerat ett begränsat antal fixbevis till ett utvalt antal mikrotjänster.
Det var dramatiskt. Om du har någon typ av belastning måste du ha dessa två saker i åtanke:
Gör din HttpClient statisk. Kassera eller paketera inte din användning om du inte uttryckligen letar efter ett specifikt beteende (som att orsaka att din tjänst misslyckas). HttpClient
sammanfattning
Problemet med uttagsutmattning som vi har kämpat med i månader är borta, och våra kunder har en virtuell parad. Jag kan inte underskatta hur ouppenbart detta misstag är. Under åren har vi vant oss vid att hantera implementerade objekt, IDisposable, och många refaktoreringsverktyg som R# och CodeRush varnar dig faktiskt om du inte gör det. I det här fallet är det fel tillvägagångssätt att göra sig av med HttpClient. HttpClient implementerar IDisposable och uppmuntrar dåligt beteende är olyckligt
Original:Inloggningen med hyperlänken är synlig.
|