Uso HttpClient in modo errato per anni, ma alla fine è arrivato l'incubo. Il mio sito era instabile e i miei clienti erano molto arrabbiati e con una semplice soluzione le prestazioni miglioravano molto e l'instabilità era stata eliminata.
Allo stesso tempo, ho effettivamente migliorato le prestazioni della mia applicazione con un uso più efficiente dei socket.
I microservizi possono essere un problema difficile da gestire. Man mano che vengono aggiunti più servizi e le applicazioni monolitiche si decompongono, tendono a sorgere sempre più percorsi di comunicazione tra i servizi. Ci sono molte opzioni di comunicazione, ma HTTP è una scelta molto popolare. Se il microservizio è costruito in C# o in qualsiasi linguaggio .NET, probabilmente stai già usando HttpClient.
Il problema sta
L'istruzione using è una caratteristica C# che gestisce oggetti monouso. Una volta completato il blocco di utilizzo, l'oggetto monouso (in questo caso HttpClient) esce dall'ambito e viene eliminato. Chiama il metodo di smaltimento e ripulisci eventuali risorse utilizzate. Questo è un pattern molto tipico in .NET che usiamo per tutto, dal database allo script di flusso. In effetti, qualsiasi oggetto con una risorsa esterna che deve essere pulita utilizza quell'interfaccia IDisposable.
E non si può biasimare se vuoi incapsularlo nell'uso. Innanzitutto, è considerato una buona pratica farlo. Infatti, la documentazione ufficiale di Microsoft utilizza:
In generale, quando si utilizza un oggetto IDisposable, dovrebbe essere dichiarato e istanziato nell'istruzione usando. Secondo, tutto il codice che potresti aver visto...... L'inizio dell'HttpClient ti dirà di usare il blocco dell'istruzione usando, inclusa la documentazione più recente ASP.NET il sito stesso. Lo stesso si dice nell'articolo su Internet.
Ma HttpClient è diverso. Sebbene implementi l'interfaccia IDisposable, in realtà è un oggetto condiviso. Questo significa che dietro le quinte è reentrante e sicuro per il filo. Dovresti condividere un'istanza di HttpClient per tutta la durata della tua applicazione, invece di creare una nuova istanza per ogni esecuzione. HttpClient, vediamo perché.
Guardate voi stessi
Ecco un semplice programma per dimostrare HttpClient:
Questo sarà rivolto a http://aspnetmonsters.comApriamo 10 richieste e eseguiamo GET, stampiamo semplicemente il codice di stato, così sappiamo che funziona. L'output sarà:
Aspetta, c'è dell'altro!
Tutto il lavoro e tutto è giusto per il mondo. Tranne che non è così. Se tiriamo fuori lo strumento netstat e guardiamo lo stato del socket della macchina che lo esegue, vedremo:
C:\code\socket>NETSTAT.EXE ... Proto Indirizzo Locale Stato Indirizzo Straniero 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 FONDATO ... Beh, è strano...... L'applicazione è stata chiusa, ma ci sono ancora molte di queste connessioni aperte sulla macchina Azure che ospita il sito web ASP.NET Monsters. Sono inTIME_WAITStato, il che significa che la connessione è stata chiusa da un lato (il nostro), ma stiamo ancora aspettando di vedere se arrivano altri pacchetti perché potrebbero essere stati in ritardo da qualche parte sulla rete. Ecco un diagramma dello stato TCP/IP:
Windows rimarrà connesso in questo stato per 240 secondi (come impostato impostando [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]). Windows ha un limite su quanto velocemente puoi aprire un nuovo socket, quindi se esaurisci i pool di connessioni, potresti vedere un errore come questo:
Impossibile connettersi al server remoto
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted. Cercarlo su Google ti darà qualche consiglio sbagliato su come ridurre i timeout delle connessioni. Infatti, quando si esegue correttamente su un server con HttpClient o su un'applicazione costruita in modo simile, ridurre i timeout può portare ad altre conseguenze negative. Dobbiamo capire cosa significa "giusto" e risolvere il problema di fondo, non smanettare con variabili a livello macchina.
Sistemalo
Se condividiamo un'istanza di HttpClient, possiamo ridurre lo spreo di socket riutilizzandoli:
Nota che per tutta l'applicazione abbiamo una sola istanza condivisa. HttpClient funziona ancora come prima (in realtà un po' più veloce grazie al riutilizzo dei socket). Netstat ora mostra solo:
TCP 10.211.55.6:12254 waws-prod-bay-017:http FONDATO In uno scenario di produzione, il mio numero di socket è in media intorno a 4000 e raggiunge un picco a oltre 5000, comprimendo di fatto le risorse disponibili sul server e causando il crash del servizio. Dopo l'implementazione del cambiamento, il numero di socket in uso è sceso da una media di oltre 4000 a costantemente inferiore a 400, tipicamente intorno a 100.
Questa è parte di un grafico del nostro strumento di monitoraggio che mostra cosa succede dopo aver distribuito un numero limitato di prove di correzione su un numero selezionato di microservizi.
È stato drammatico. Se hai qualsiasi tipo di carico, devi tenere a mente queste due cose:
Rendi il tuo HttpClient statico. Non scartare o confezionare il tuo utilizzo a meno che tu non stia cercando esplicitamente un comportamento specifico (come causare il fallimento del servizio). HttpClient
sommario
Il problema di esaurimento delle prese con cui abbiamo lottato da mesi è sparito, e i nostri clienti fanno una parata virtuale. Non posso sottovalutare quanto sia poco evidente questo errore. Nel corso degli anni, siamo abituati a gestire oggetti implementati, IDisposable, e molti strumenti di refactoring come R# e CodeRush ti avvertono se non lo fai. In questo caso, eliminare HttpClient è l'approccio sbagliato. HttpClient implementa IDisposable e incoraggia comportamenti scorretti è sfortunato
Originale:Il login del link ipertestuale è visibile.
|