Abbiamo un'API implementata utilizzando ServiceStack che è ospitato in IIS. Durante l'esecuzione del test di carico dell'API abbiamo scoperto che i tempi di risposta sono buoni ma che si deteriorano rapidamente non appena raggiungiamo circa 3.500 utenti simultanei per server. Abbiamo due server e quando li colpiamo con 7000 utenti i tempi di risposta medi si situano sotto i 500 ms per tutti gli endpoint. Le scatole sono dietro un bilanciamento del carico in modo da ottenere 3.500 concorrenti per server. Tuttavia, non appena aumentiamo il numero di utenti simultanei totali, vediamo un aumento significativo dei tempi di risposta. Aumentare gli utenti simultanei a 5.000 per server ci dà un tempo di risposta medio per endpoint di circa 7 secondi.
La memoria e la CPU sui server sono piuttosto basse, sia quando i tempi di risposta sono buoni sia quando si deteriorano. Al picco con 10.000 utenti simultanei, la CPU è in media appena inferiore al 50% e la RAM si attesta su 3-4 GB su 16. Questo ci lascia pensare che stiamo raggiungendo un qualche limite. Lo screenshot seguente mostra alcuni contatori chiave in perfmon durante un test di carico con un totale di 10.000 utenti simultanei. Il contatore evidenziato è richieste / secondo. A destra dello screenshot puoi vedere il grafico delle richieste al secondo diventare davvero irregolari. Questo è l'indicatore principale per i tempi di risposta lenti. Non appena vediamo questo schema notiamo tempi di risposta lenti nel test di carico.
Come possiamo risolvere questo problema di prestazioni? Stiamo cercando di identificare se si tratta di un problema di codifica o di configurazione. Esistono impostazioni in web.config o IIS che potrebbero spiegare questo comportamento? Il pool di applicazioni esegue .NET v4.0 e la versione IIS è 7.5. L'unica modifica apportata dalle impostazioni predefinite è aggiornare il valore Lunghezza coda pool di applicazioni da 1.000 a 5.000. Abbiamo anche aggiunto le seguenti impostazioni di configurazione al file Aspnet.config:
<system.web>
<applicationPool
maxConcurrentRequestsPerCPU="5000"
maxConcurrentThreadsPerCPU="0"
requestQueueLimit="5000" />
</system.web>
Più dettagli:
Lo scopo dell'API è quello di combinare i dati provenienti da varie fonti esterne e restituirli come JSON. Attualmente utilizza un'implementazione della cache InMemory per memorizzare nella cache singole chiamate esterne a livello di dati. La prima richiesta a una risorsa recupererà tutti i dati richiesti e qualsiasi richiesta successiva per la stessa risorsa otterrà risultati dalla cache. Abbiamo un "runner cache" che viene implementato come processo in background che aggiorna le informazioni nella cache a determinati intervalli prestabiliti. Abbiamo aggiunto il blocco attorno al codice che recupera i dati dalle risorse esterne. Abbiamo anche implementato i servizi per recuperare i dati dalle fonti esterne in modo asincrono in modo tale che l'endpoint dovrebbe essere lento solo come la chiamata esterna più lenta (a meno che ovviamente non abbiamo dati nella cache). Questo viene fatto usando la classe System.Threading.Tasks.Task.Potremmo essere colpiti da una limitazione in termini di numero di thread disponibili per il processo?