Modello di controllo del flusso

Limitare le risorse che un'istanza dell'applicazione, un singolo tenant o un intero servizio può utilizzare. Questo consente al sistema di funzionare e di soddisfare i propri obiettivi di livello di servizio (SLO) in presenza di carichi improvvisi o prolungati.

Contesto e problema

Il carico in un'applicazione cloud varia nel tempo in base agli utenti attivi e alle relative attività. Più utenti accedono durante l'orario di ufficio e il sistema esegue analisi dispendiose a livello di calcolo alla fine di ogni mese. Si verificano anche picchi improvvisi. Se la domanda di elaborazione supera la capacità disponibile, il sistema rallenta o non riesce. Quando il sistema ha un livello di servizio concordato, tale errore viola l'SLO.

Diverse strategie gestiscono il carico variabile, a seconda degli obiettivi aziendali dell'applicazione. Una strategia è l'autoscaling, che adegua le risorse provisionate alla domanda corrente e consente di controllare i costi. Tuttavia, il provisioning di nuove risorse richiede tempo e aggiunge costi. La domanda che supera la crescita della capacità o il budget crea un deficit di risorse.

Soluzione

Un'alternativa alla scalabilità automatica consiste nel limitare l'uso delle risorse e limitare le richieste quando l'utilizzo supera tale limite. Il carico di lavoro monitora l'uso delle proprie risorse e limita le richieste da uno o più utenti quando l'utilizzo supera la soglia. Il sistema continua a funzionare e a soddisfare i propri obiettivi del livello di servizio (SLO).

La limitazione è un ciclo di controllo, non una singola decisione di ammissione. Il sistema richiede segnali a bassa latenza a tre livelli: utilizzo dell'infrastruttura, stato dell'applicazione e contatori per entità. Misura continuamente la saturazione, applica limiti a limiti ben definiti e adatta tali limiti man mano che cambiano i modelli di traffico. L'overload è una modalità operativa normale da cui un sistema maturo rileva e recupera. Il throttling offre funzionalità di autoprotezione nel tuo carico di lavoro.

Il sistema può implementare diverse strategie di limitazione o correlate:

  • Limiti di frequenza per entità: Rifiutare le richieste da un utente che ha già superato la frequenza configurata in una finestra definita. Questa strategia richiede che il sistema attribuisca ogni richiesta a un soggetto e contabilizzi l'uso delle risorse in relazione a tale soggetto. Per i carichi di lavoro multi-tenant, vedere Misurare l'utilizzo di ogni tenant.

  • Degrado controllato delle funzionalità: Disattivare o ridurne le prestazioni per le funzionalità non essenziali, in modo che quelle essenziali dispongano di risorse sufficienti. Questa strategia privilegia la disponibilità a scapito della completezza della risposta. Ad esempio, un'applicazione di streaming video può passare a una risoluzione inferiore.

  • Livellamento del carico: Uniforma il volume di attività utilizzando una coda. In un ambiente multi-tenant, il livellamento riduce le prestazioni per ogni tenant. Quando i tenant hanno accordi sul livello di servizio (SLA) diversi, elabora immediatamente il lavoro per i tenant ad alto valore e rimanda il lavoro a priorità inferiore finché l’arretrato non si riduce. Implementare questo approccio usando il modello di coda priorità o esponendo endpoint separati per ogni livello di priorità.

  • Posticipo in base alla priorità: Posticipare le operazioni relative ad applicazioni o tenant con priorità inferiore. Sospendere o limitare le operazioni e restituire un'eccezione che indica al tenant di riprovare in un secondo momento.

  • Limiti di velocità in uscita: Limitare le chiamate in uscita quando una dipendenza esterna ha esito negativo o restituisce errori. Ridurre il numero di richieste in corso per evitare di inondare i log e i costi dei nuovi tentativi verso una dipendenza non sana. Ripristinare il flusso normale delle richieste dopo il ripristino della dipendenza. Ad esempio, NServiceBus implementa questa funzionalità.

Il grafico seguente mostra l'uso delle risorse (una combinazione di memoria, CPU, larghezza di banda e altri fattori) nel tempo per un'applicazione che usa tre funzionalità, etichettate A, B e C. Una funzionalità è un'area specifica di funzionalità, ad esempio un componente che esegue un set specifico di attività, un frammento di codice che esegue un calcolo complesso o un elemento che fornisce un servizio, ad esempio una cache in memoria.

Grafico che mostra l'uso delle risorse rispetto al tempo per le applicazioni eseguite per conto di tre utenti.

Un grafico a linee traccia l'utilizzo delle risorse sull'asse y rispetto al tempo sull'asse x. Tre linee colorate rappresentano la Funzionalità A, la Funzionalità B e la Funzionalità C, con la linea della Funzionalità A più in basso, quella della Funzionalità B al centro e quella della Funzionalità C più in alto. Una linea orizzontale continua nella parte superiore del grafico contrassegna la capacità massima e una linea orizzontale tratteggiata sotto di essa contrassegna il limite flessibile di utilizzo delle risorse. Due linee tratteggiate verticali contrassegnano gli istanti T1 e T2. Prima di T1, le curve di tutte e tre le funzionalità fluttuano e quella della Funzionalità C sale e supera il limite soft. In T1, la riga della funzionalità B scende a zero e rimane a zero fino a T2 perché la funzionalità B viene sospesa per liberare risorse per la funzionalità A e la funzionalità C. La riga C della funzionalità C scende al di sotto del limite flessibile tra T1 e T2 mentre la funzionalità A continua normalmente. A T2, la funzionalità B riprende e tutte e tre le linee continuano a fluttuare al di sotto del limite morbido.

Il grafico è un grafico ad area sovrapposta. L'area sotto la riga della funzionalità A mostra le risorse utilizzate dalla funzionalità A, l'area tra le righe della funzionalità A e della funzionalità B mostra le risorse utilizzate dalla funzionalità B e l'area tra le righe della funzionalità B e le righe di Feature C mostra le risorse utilizzate dalla funzionalità C. La linea della funzionalità C è nella parte superiore del grafico in pila, quindi mostra anche l'utilizzo totale delle risorse di sistema nel tempo.

Il grafico mostra una degradazione graduale delle funzionalità. Poco prima del tempo T1, l'uso totale delle risorse si avvicina alla soglia e rischia di esaurire la capacità disponibile. La funzionalità B è meno critica rispetto alla funzionalità A o alla funzionalità C, quindi il sistema disattiva la funzionalità B e rilascia le relative risorse. Tra i tempi T1 e T2, la funzionalità A e la funzionalità C continuano normalmente. Entro il tempo T2, l'utilizzo complessivo delle risorse si riduce abbastanza da riattivare la funzionalità B.

È possibile combinare il ridimensionamento automatico, la degradazione graduale e la limitazione del traffico per mantenere le applicazioni reattive e conformi agli SLA. Quando si prevede che la domanda rimanga elevata, la limitazione mantiene la stabilità mentre il sistema aumenta il numero di istanze. Al termine del ridimensionamento, il sistema ripristina tutte le funzionalità.

Il grafico successivo mostra l'utilizzo complessivo delle risorse nel tempo e come la limitazione delle prestazioni si combina con la scalabilità automatica e altri controlli compensativi.

Grafico che mostra gli effetti della combinazione tra la limitazione del traffico e la scalabilità automatica.

Un grafico a linee traccia l'utilizzo delle risorse per tutte le applicazioni sull'asse y rispetto al tempo sull'asse x. Due linee di riferimento orizzontali contrassegnano il limite flessibile di utilizzo delle risorse e la capacità massima prima della scalabilità automatica. Una linea orizzontale superiore, che inizia all'ora T2, contrassegna la capacità massima dopo la scalabilità automatica. La linea di utilizzo aumenta e fluttua nel tempo. Supera il limite flessibile al momento T1, che è il punto in cui la scalabilità automatica inizia. Tra T1 e T2, il sistema è soggetto a limitazione durante l'autoscaling e l'utilizzo rimane al di sotto della capacità massima precedente all'autoscaling. Al momento T2, il ridimensionamento automatico è completato, la limitazione viene allentata e la curva dell'utilizzo sale bruscamente e continua a fluttuare al di sotto della nuova capacità massima, più elevata.

Al tempo T1, il sistema raggiunge il limite soft e inizia a scalare orizzontalmente. Se le nuove risorse non arrivano in tempo, la domanda può esaurire le risorse disponibili e il sistema può andare in errore. Il throttling rifiuta le richieste in eccesso durante l'espansione orizzontale per mantenere l'utilizzo delle risorse al di sotto del limite massimo, quindi revoca tali restrizioni dopo che la nuova capacità entra in funzione.

Tip

I controlli perimetrali e il modello di limitazione della velocità rispondono a problemi diversi. Controlli perimetrali, come Azure DDoS Protection e le regole di limitazione della frequenza del web application firewall (WAF), operano al perimetro della rete e bloccano il traffico volumetrico o dannoso prima che raggiunga l'applicazione. Il modello di limitazione della velocità opera all'interno dell'applicazione e regola il traffico legittimo in base ai limiti definiti dall'applicazione. Usare entrambi i livelli insieme. La protezione DDoS non impedisce a un utente legittimo di sovraccaricare il servizio e la limitazione delle applicazioni non assorbe un attacco volumetrico.

Problemi e considerazioni

Quando si decide come implementare questo modello, tenere presente quanto segue:

  • Prendere decisioni di limitazione in anticipo. La limitazione del traffico è una decisione architetturale che influisce sull'intero sistema. L'adeguamento in un secondo momento è costoso.

  • Allinea i limiti di throttling con il componente che si satura per primo.

    Il tasso di richieste è il parametro più comune da limitare, ma il vero collo di bottiglia è spesso rappresentato dalle richieste simultanee in corso, dalla profondità della coda, dall'utilizzo di CPU o memoria oppure dai limiti di una dipendenza a valle. Un limite di richieste al secondo non protegge un sistema il cui collo di bottiglia è la concorrenza in un punto di diramazione.

    In corrispondenza di ogni punto di applicazione del throttling, ad esempio il gateway, il servizio, una partizione o una dipendenza a valle, identificare quale componente si satura per primo e impostare il limite su quella dimensione. Per la protezione a concorrenza limitata nei punti di diramazione, vedere il pattern Bulkhead, che completa il throttling.

  • Selezionare un algoritmo di limitazione intenzionalmente. Trova la corrispondenza con la tolleranza del componente che stai proteggendo.

    Algoritmo Comportamento e adattamento ottimale
    Bucket di token Supporta picchi fino a una dimensione configurata e applica una frequenza di riempimento costante. Utilizzare per i gateway che devono assorbire picchi di breve durata.
    Secchio bucato Emette a un tasso costante. Da utilizzare per i back-end che richiedono un tasso di ingresso costante.
    Finestra fissa Semplice da implementare, ma ammette picchi back-to-back ai limiti delle finestre.
    Finestra scorrevole Attenua il problema ai bordi della finestra nelle finestre fisse, al costo di una maggiore quantità di stato.
  • Decidere chi influisce sul limite. La limitazione a un limite grossolano, ad esempio un gateway a livello di area, può influire su molti utenti non correlati quando solo alcuni di essi determinano il carico.

  • Decidere dove risiede il contatore quando un limite si estende su più nodi. I contatori locali sono veloci ma sottostimano il conteggio quando lo stesso chiamante raggiunge più repliche. Un contatore centralizzato in un archivio condiviso come Redis vede ogni richiesta, ma aggiunge latenza a ogni decisione. Per approssimare una velocità globale, dividere il limite tra le repliche e riconciliare periodicamente.

  • Prendere rapidamente decisioni di limitazione. Il sistema deve rilevare l'aumento del carico, reagire e tornare alla normalità dopo che il carico si riduce. Questo processo richiede un monitoraggio continuo delle prestazioni.

  • Riduci il carico in modo proattivo, non quando sei sull'orlo del collasso. Un meccanismo di limitazione che rifiuta le richieste solo dopo che un componente si è saturato fa aumentare bruscamente la latenza prima che i client percepiscano qualsiasi contropressione.

    Quando l'utilizzo si avvicina al limite rigido, iniziare a rifiutare una frazione crescente di richieste. Il rifiuto precoce segnala ai chiamanti di rallentare e previene il collasso della latenza che limiti improvvisi spesso innescano. Utilizza la latenza p99 rispetto al tuo SLO come trigger principale. L'utilizzo medio può sembrare nella norma anche se p99 ha già superato la soglia.

    Laddove sia possibile distinguere il valore delle richieste, scartate prima il lavoro di minor valore o quello più facilmente ripetibile. Per altre informazioni, vedere Modello di coda priorità.

  • Restituire un codice di stato che indica al client quando un rifiuto temporaneo è dovuto alla limitazione delle richieste:

    • HTTP 429 (troppe richieste): Il chiamante supera una frequenza di richiesta configurata su una finestra definita.
    • HTTP 503 (Servizio non disponibile): Il servizio non è in grado di gestire la richiesta in questo momento, spesso a causa di un picco di carico imprevisto.

    Includere un'intestazione Retry-After HTTP in modo che il client possa scegliere una strategia di ripetizione dei tentativi. Restituisce un contesto sufficiente per il chiamante per riprovare deliberatamente invece di indovinare. Ad esempio, denominare il limite superato dal chiamante, chiarire l'ambito interessato o suggerire una frequenza che abbia esito positivo. I rifiuti inspiegabili non aiutano i chiamanti ad adattarsi.

  • Propaga i segnali di sovraccarico provenienti dalle dipendenze invece di assorbirli. Un servizio che limita i chiamanti deve anche rispettare le risposte di limitazione ricevute dalle proprie dipendenze downstream. Se il servizio nasconde una risposta downstream 429 o 503 riprovando senza segnalarlo o restituendo una risposta HTTP 500 generica (errore interno del server), i client non possono rallentare, i tentativi si moltiplicano e il sovraccarico si propaga a cascata verso monte. L'antipattern Retry Storm descrive questa modalità di errore. Propagare la back-pressure ai chiamanti a monte, in modo che l'intera catena di chiamate riduca il carico in modo coordinato.

  • Rendere più economico il rifiuto rispetto al lavoro che impedisce. Se il rifiuto di una richiesta richiede un'autenticazione intensa, un'analisi approfondita o una valutazione complessa dei criteri, un'inondazione delle richieste rifiutate può comunque saturare il sistema. Rifiutate il prima possibile nella pipeline delle richieste e sottoponete a test di carico il flusso di rifiuto stesso.

  • Prevedi i casi in cui la limitazione del traffico non riesce a guadagnare abbastanza tempo per consentire la scalabilità automatica. Se la domanda cresce più rapidamente di quanto la nuova capacità entri in funzione, anche un sistema soggetto a limitazione può fallire. Quando tale risultato è inaccettabile, mantenere riserve di capacità maggiori e configurare una scalabilità automatica più aggressiva.

  • Non usare la cache come sostituto della limitazione della velocità. Una cache riduce il carico medio sull'origine, ma non riduce il carico di picco. Ogni mancato riscontro nella cache passa attraverso l'origine e quando una chiave popolare scade sotto traffico intenso, molti chiamanti possono correre per ricaricarlo. Utilizzare la cache per ridurre il carico normale e la limitazione della velocità per contenere lo scenario peggiore. Per altre informazioni, vedere il modello diCache-Aside.

  • Normalizzare i costi delle risorse per operazioni diverse perché in genere non comportano costi di esecuzione uguali. Ad esempio, i limiti di limitazione della velocità potrebbero essere più elevati per le operazioni di lettura e più bassi per le operazioni di scrittura. Ignorare il costo per operazione può esaurire la capacità e creare un vettore di attacco.

  • Consentire la modifica della configurazione del throttling durante l'esecuzione. Quando si verifica un carico anomalo, è necessario adeguare i limiti senza un rilascio. Le distribuzioni sono lente e rischiose durante un evento imprevisto. Il pattern di archivio di configurazione esterna esternalizza la configurazione in modo da consentirne la modifica in fase di esecuzione.

  • Prendere in considerazione i limiti adattivi anziché i limiti statici. Alcuni SDK per il throttling reagiscono ai segnali di latenza o di lunghezza della coda, così che il limite si adatti alle condizioni reali dei componenti. Associare sempre un limiter adattivo a un valore massimo impostato.

  • Rivedere i limiti man mano che il carico di lavoro si evolve. I limiter adattivi non possono tenere traccia di ogni tipo di deviazione, ad esempio modifiche di SLO, modifiche alla capacità di dipendenza o spostamenti in costi per operazione. Pianificare la revisione periodica dell'operatore in base a tali input.

Quando usare questo modello

Usare questo modello:

  • Per mantenere un sistema entro i propri SLO.

  • Per impedire a un singolo tenant di monopolizzare le risorse dell'applicazione.

  • Per gestire i picchi di attività.

  • Per limitare il livello massimo di risorse necessario a un sistema.

  • Per ridurre l'elaborazione a basso valore durante i periodi di elevata intensità di carbonio della rete elettrica.

Progettazione del carico di lavoro

Valutare come usare il modello di limitazione nella progettazione di un carico di lavoro per soddisfare gli obiettivi e i principi trattati nei pilastri di Azure Well-Architected Framework. La tabella seguente fornisce indicazioni su come questo modello supporta gli obiettivi di ogni pilastro.

Pilastro Come questo modello supporta gli obiettivi di pilastro
decisioni di progettazione dell'affidabilità consentono al carico di lavoro di diventare resiliente a un malfunzionamento e assicurano che ripristini a uno stato completamente funzionante dopo che si verifica un guasto. Si progettano i limiti per evitare l'esaurimento delle risorse che potrebbero causare malfunzionamenti. È anche possibile usare questo modello come meccanismo di controllo in un piano di riduzione delle prestazioni normale.

- RE:07 Autoconservazione
Le decisioni di progettazione della sicurezza consentono di garantire la riservatezza, l'integrità e la disponibilità dei dati e dei sistemi del carico di lavoro. È possibile progettare i limiti per evitare l'esaurimento delle risorse che potrebbe causare abusi automatici del sistema.

- Controlli di rete SE:06
- Indurimento delle risorse SE:08
L'ottimizzazione dei costi è incentrata sul mantenimento e sul miglioramento del ritorno del carico di lavoro sugli investimenti. I limiti applicati possono informare la modellazione dei costi e possono essere direttamente collegati al modello aziendale dell'applicazione. Inseriscono anche limiti superiori chiari sull'utilizzo, che possono essere inseriti nel dimensionamento delle risorse.

- Modello di costo CO:02
- CO:12 Costi di ridimensionamento
l'efficienza delle prestazioni consente al carico di lavoro soddisfare in modo efficiente le richieste tramite ottimizzazioni di ridimensionamento, dati e codice. Quando il sistema è sottoposto a una domanda elevata, questo modello consente di ridurre la congestione che può causare un collo di bottiglia delle prestazioni. È anche possibile usarlo per evitare in modo proattivo scenari di vicini rumorosi.

- PE:02 Pianificazione della capacità
- PE:05 Ridimensionamento e partizionamento

Se questo modello introduce compromessi all'interno di un pilastro, considerarli contro gli obiettivi degli altri pilastri.

Example

Il diagramma seguente illustra la limitazione della velocità in un sistema multi-tenant.

Diagramma che illustra la limitazione della velocità in un'applicazione multi-tenant.

Tre utenti contrassegnati sulla sinistra rappresentano i tenant di un'applicazione Surveys multi-tenant: Adatum, Fabrikam e Contoso. Ogni utente invia richieste tramite un dominio personalizzato specifico del tenant, che l'applicazione usa per identificare il tenant. Adatum invia 5 richieste al secondo tramite surveys.adatum.com, Fabrikam invia 10 richieste al secondo tramite surveys.fabrikam.com e Contoso invia 150 richieste al secondo tramite surveys.contoso.com. A destra, il ruolo web dell'applicazione sondaggi misura il tasso di richieste al secondo per ogni tenant. I flussi di richiesta Adatum e Fabrikam passano attraverso l'applicazione. Il flusso di richiesta Contoso è bloccato da una risposta Error: Throttled perché la frequenza supera il limite per tenant.

Gli utenti di diverse organizzazioni tenant accedono a un'applicazione ospitata nel cloud per compilare e inviare sondaggi. L'applicazione contiene la strumentazione che monitora la frequenza con cui gli utenti di ogni tenant inviano richieste.

Per impedire agli utenti di un tenant di ridurre la velocità di risposta e la disponibilità per gli utenti in altri tenant, l'applicazione limita la frequenza di richieste al secondo che qualsiasi tenant singolo può inviare. L'applicazione blocca le richieste che superano questo limite.

Passo successivo