Modello di limitazione della velocità

Controllare la frequenza con cui l'applicazione invia richieste a un servizio in modo da rimanere entro i limiti di limitazione del servizio e la capacità complessiva. Questo approccio consente di evitare o ridurre al minimo gli errori di limitazione e di prevedere in modo più accurato la velocità effettiva.

La limitazione della frequenza è appropriata in molti scenari, ma è particolarmente utile per attività automatizzate ripetitive su larga scala, ad esempio l'elaborazione batch.

Contesto e problema

L'esecuzione di un numero elevato di operazioni su un servizio limitato può comportare un aumento del traffico e una velocità effettiva ridotta, perché è necessario tenere traccia delle richieste rifiutate e quindi ripetere le operazioni. Con l'aumentare del numero di operazioni, un limite di limitazione potrebbe richiedere più passaggi di invio di dati di nuovo, con un impatto maggiore sulle prestazioni.

Si consideri, ad esempio, il processo di ripetizione dei tentativi in caso di errore problematico seguente per l'inserimento di dati in Azure Cosmos DB:

  1. L'applicazione deve inserire 10.000 record in Azure Cosmos DB. Ogni record costa 10 unità richiesta (UR) per l'inserimento, quindi per completare il processo è necessario un totale di 100.000 UR.

  2. L'istanza di Azure Cosmos DB ha una capacità di provisioning di 20.000 UR.

  3. Tutti i 10.000 record vengono inviati ad Azure Cosmos DB. 2.000 record vengono scritti correttamente e vengono rifiutati 8.000 record.

  4. I rimanenti 8.000 record vengono inviati ad Azure Cosmos DB. 2.000 record vengono scritti correttamente e vengono rifiutati 6.000 record.

  5. Si inviano i 6.000 record rimanenti ad Azure Cosmos DB. 2.000 record vengono scritti correttamente e vengono rifiutati 4.000 record.

  6. Si inviano i 4.000 record rimanenti ad Azure Cosmos DB. 2.000 record vengono scritti correttamente e vengono rifiutati 2.000 record.

  7. Si inviano i 2.000 record rimanenti ad Azure Cosmos DB. Tutti gli elementi sono stati scritti correttamente.

Il processo di inserimento viene completato correttamente, ma solo dopo l'invio di 30.000 record a Azure Cosmos DB. L'intero set di dati è costituito da soli 10.000 record.

Esistono altri fattori da considerare in questo esempio:

  • Un numero elevato di errori può anche comportare un lavoro aggiuntivo per registrare questi errori ed elaborare i dati di log risultanti. L'approccio precedente gestisce 20.000 errori e la registrazione di questi errori potrebbe imporre un costo di elaborazione, memoria o risorsa di archiviazione.

  • Poiché non si conoscono i limiti di limitazione del servizio di inserimento, non è possibile impostare le aspettative per quanto tempo l'elaborazione dei dati richiede. La limitazione della frequenza consente di calcolare il tempo necessario per l'inserimento.

Soluzione

La limitazione della velocità può ridurre il traffico e migliorare potenzialmente la velocità effettiva riducendo il numero di record inviati a un servizio in un determinato periodo di tempo.

Un servizio può limitare le richieste in base a metriche diverse nel tempo, ad esempio:

  • Numero di operazioni, ad esempio 20 richieste al secondo.
  • Quantità di dati (ad esempio, 2 GiB al minuto).
  • Costo relativo delle operazioni (ad esempio, 20.000 UR al secondo).

Indipendentemente dalla metrica usata per la limitazione della limitazione, l'implementazione della limitazione della velocità comporta il controllo del numero e/o delle dimensioni delle operazioni inviate al servizio durante un periodo di tempo specifico. La limitazione della velocità ottimizza l'uso del servizio senza superare la capacità di limitazione.

Negli scenari in cui le API possono gestire le richieste più velocemente rispetto ai servizi di inserimento con limitazione, è necessario gestire la velocità di utilizzo del servizio. Considerare la limitazione solo come una mancata corrispondenza della velocità dei dati e mettere in coda le richieste di inserimento finché il servizio non viene ripristinato crea un rischio. Se l'applicazione smette di rispondere in questo scenario, eventuali dati memorizzati nel buffer potrebbero andarsi persi.

Per evitare questo rischio, valuta l'invio dei record a un sistema di messaggistica affidabile che possa gestire l'intera velocità di acquisizione. Servizi come Hub eventi di Azure possono gestire milioni di operazioni al secondo. È quindi possibile usare uno o più processori di processi per leggere i record dal sistema di messaggistica a una velocità controllata entro i limiti del servizio limitato. L'invio di record al sistema di messaggistica può far risparmiare memoria interna, permettendo di prelevare dalla coda solo i record che possono essere elaborati in un determinato intervallo di tempo.

Azure offre diversi servizi di messaggistica durevole che è possibile usare con questo modello, tra cui:

Diagramma che mostra un flusso di messaggistica durevole. Tre processori di processo chiamano in un servizio limitato.

Quando si inviano record, il periodo di tempo usato per il rilascio dei record potrebbe essere più granulare rispetto al periodo in cui il servizio limita. I sistemi spesso impostano limitazioni in base a intervalli di tempo che è possibile comprendere e usare facilmente. Tuttavia, per il computer che esegue un servizio, questi intervalli di tempo potrebbero essere molto lunghi rispetto alla velocità di elaborazione delle informazioni. Ad esempio, un sistema potrebbe applicare una limitazione al secondo o al minuto, ma in genere il codice viene eseguito nell'ordine dei nanosecondi o dei millisecondi.

Anche se non è necessario, è spesso consigliabile inviare più piccoli numeri di record più frequentemente per migliorare la velocità effettiva. Pertanto, invece di provare a eseguire il batch di record per una versione una volta al secondo o una volta al minuto, è possibile ottenere una maggiore granularità rispetto a quella per mantenere il flusso dell'utilizzo delle risorse (memoria, CPU e rete) a una velocità più uniforme. Questo approccio impedisce potenziali colli di bottiglia causati da picchi improvvisi di richieste. Ad esempio, se un servizio consente 100 operazioni al secondo, l'implementazione di un limite di velocità potrebbe persino ridurre le richieste rilasciando 20 operazioni ogni 200 millisecondi, come illustrato nel grafico seguente.

Grafico che mostra un flusso limitato a velocità nel tempo.

Inoltre, a volte è necessario per più processi non coordinati condividere un servizio limitato. Per implementare la limitazione della frequenza in questo scenario, è possibile partizionare logicamente la capacità del servizio e quindi usare un sistema di esclusione reciproca distribuito per gestire blocchi esclusivi su tali partizioni. I processi non coordinati possono quindi competere per i blocchi su tali partizioni ogni volta che hanno bisogno di capacità. Per ogni partizione per la quale un processo detiene un lock, viene assegnata una certa quantità di capacità.

Ad esempio, se il sistema soggetto a limitazione consente 500 richieste al secondo, è possibile creare 20 partizioni da 25 richieste al secondo ciascuna. Se un processo dovesse effettuare 100 richieste, potrebbe chiedere al sistema di esclusione reciproca distribuita quattro partizioni. Il sistema potrebbe concedere due partizioni per 10 secondi. Il processo quindi limita a 50 richieste al secondo, completa l'attività in due secondi e quindi rilascia il blocco.

Un modo per implementare questo modello consiste nell'usare Archiviazione di Azure. In questo scenario viene creato un BLOB a 0 byte per ogni partizione logica in un contenitore. Le applicazioni possono quindi ottenere contratti di leasing esclusivi direttamente su tali BLOB per un breve periodo di tempo (ad esempio, 15 secondi). Per ogni lease viene concessa un'applicazione, può usare la quantità di capacità della partizione. L'applicazione deve quindi tenere traccia del tempo di lease in modo che, alla scadenza del tempo, l'applicazione possa smettere di usare la capacità concessa. Quando si implementa questo modello, è spesso necessario che ogni processo tenti di creare un lease di una partizione casuale quando richiede capacità.

Per ridurre ulteriormente la latenza, è possibile allocare una piccola quantità di capacità esclusiva per ogni processo. Un processo cercherebbe quindi di ottenere un lease per la capacità condivisa solo se avesse bisogno di superare la capacità riservata.

Diagramma che mostra più processi in competizione per i lease esclusivi nelle partizioni BLOB in Archiviazione BLOB di Azure.

In alternativa a Archiviazione di Azure, è anche possibile implementare questo tipo di sistema di gestione lease usando tecnologie come ZooKeeper, etcd e Redis/Redsync.

Problemi e considerazioni

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

  • Anche se il modello di limitazione della frequenza può ridurre il numero di errori di limitazione, l'applicazione deve comunque gestire correttamente eventuali errori di limitazione che potrebbero verificarsi.

  • Assicurarsi che i tentativi siano coordinati con la limitazione della velocità. Tentativi ciechi o eccessivamente aggressivi possono aumentare il carico e creare tempeste di tentativi, quindi propagare segnali di back-pressure (ad esempio, HTTP 429 con Retry-After) e usare un numero limitato di tentativi con piccoli ritardi casuali tra i tentativi.

  • Se l'applicazione dispone di più flussi di lavoro che accedono allo stesso servizio limitato, è necessario integrarli tutti nella strategia di limitazione della velocità. Ad esempio, è possibile supportare il caricamento bulk di record in un database, ma anche l'esecuzione di query per i record nello stesso database. È possibile gestire la capacità assicurandosi che tutti i flussi di lavoro vengano gestiti tramite lo stesso meccanismo di limitazione della velocità. In alternativa, è possibile riservare pool di capacità separati per ogni flusso di lavoro.

  • Il servizio limitato potrebbe essere usato in più applicazioni. In alcuni casi, è possibile coordinare tale utilizzo (come illustrato in precedenza in questo articolo). Se si inizia a visualizzare un numero maggiore del previsto di errori di limitazione, tale numero potrebbe indicare una contesa tra le applicazioni che accedono a un servizio. In questo caso, potrebbe essere necessario prendere in considerazione la riduzione temporanea della velocità effettiva imposta dal meccanismo di limitazione della velocità fino a quando l'utilizzo di altre applicazioni non diminuisce.

Quando usare questo modello

Usare questo modello quando:

  • È necessario ridurre gli errori di limitazione generati da un servizio limitato a velocità.

  • Si vuole ridurre al minimo il traffico, rispetto agli approcci di ripetizione ingenui sugli errori.

  • È necessario ridurre il consumo di memoria dequeundo i record solo quando è disponibile una capacità sufficiente per elaborarli.

Questo modello potrebbe non essere adatto quando:

  • L'operazione richiede il completamento sincrono immediato e sincrono con una latenza molto bassa e non può tollerare l'accodamento o l'elaborazione posticipata.

  • Il collo di bottiglia principale non è la frequenza delle richieste, ma è invece una contesa di concorrenza o di risorse (ad esempio, saturazione della CPU o lavoro in esecuzione prolungata). In questi casi, il ridimensionamento o i controlli di concorrenza sono più appropriati.

Progettazione del carico di lavoro

Valutare come usare il modello di limitazione della frequenza 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. Questa tattica protegge il client riconoscendo e rispettando le limitazioni e i costi di comunicazione con un servizio quando il servizio preferisce evitare un utilizzo eccessivo.

- RE:07 Autoconservazione

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

Example

L'applicazione di esempio seguente consente agli utenti di inviare record di vari tipi a un'API. Ogni tipo di record ha un processore di processi univoco che esegue i passaggi seguenti:

  1. Validation
  2. Arricchimento
  3. Inserimento del record nel database

Tutti i componenti dell'applicazione (API, processore di processi A e processore di processi B) sono processi separati che possono essere ridimensionati in modo indipendente. I processi non comunicano direttamente tra loro.

Diagramma che mostra un flusso multiprocessore multi-coda con la scrittura di archiviazione lease partizionata in un database limitato.

Il diagramma mostra due utenti che inviano record tramite un'API condivisa. Ogni tipo di record viene indirizzato a una coda separata, elaborata da un processore di processi dedicato e scritta in un database limitato a una velocità controllata governata dai lease di partizione BLOB in Archiviazione di Azure. I due utenti inviano un batch di messaggi al componente API. Un utente invia 10.000 messaggi e l'altro invia 5.000 messaggi. L'API instrada i 10.000 messaggi alla coda A e i 5.000 messaggi alla coda B. La coda A si connette al processore di processi A e la coda B si connette al processore di processi B. Il processore di processi A scrive nel database a una velocità di 300 record al secondo. Il processore di processi B scrive nel database a 500 record al secondo. Entrambi i processori di processo si connettono a un componente di database con etichetta "limitato a 800 record al secondo". Sotto i due processori di processo, Archiviazione di Azure contiene otto partizioni BLOB etichettate da 0 a 7. Una nota indica che ogni partizione vale 100 record al secondo. Più frecce connettono i processori di processo alle partizioni BLOB. Frecce verdi puntano dal processore di processi A alle partizioni 0, 4 e 6, che indica che il processore contiene lease su queste tre partizioni. Questi lease riguardano la velocità di 300 record al secondo. Frecce verdi puntano dal processore di processo B alle partizioni 1, 2, 3, 5 e 7. Queste frecce indicano che questo processore contiene lease su cinque partizioni. Questi lease riguardano la velocità di 500 record al secondo. I tentativi di lease non riusciti vengono visualizzati come frecce rosse che passano a partizioni già mantenute dall'altro processore. Queste frecce rappresentano la risoluzione dei conflitti. Il diagramma mostra che la somma di tutte le partizioni mantenute in entrambi i processori è uguale a 800 record al secondo, che corrisponde al limite di limitazione del database.

In questo esempio ogni lease di BLOB rappresenta una condivisione fissa della velocità effettiva consentita del database. Un processore può eseguire la coda e scrivere solo alla velocità combinata dei lease attualmente contenuti. Man mano che i processori ottengono o perdono lease nel tempo, le modifiche consentite della frequenza di scrittura, che mantiene il traffico totale del database entro il limite configurato, consentendo comunque lo stato di avanzamento del lavoro in coda.

Questo diagramma incorpora il flusso di lavoro seguente:

  1. Un utente invia 10.000 record di tipo A all'API.
  2. L'API accoda tali 10.000 record nella coda A.
  3. Un utente invia 5.000 record di tipo B all'API.
  4. L'API accoda tali 5.000 record nella coda B.
  5. Processore di processi A rileva che la coda A contiene record e tenta di ottenere un lease esclusivo nel BLOB 2.
  6. Il processore di processi B rileva che la coda B contiene record e tenta di ottenere un lease esclusivo nel BLOB 2.
  7. Il processore di processi A non riesce a ottenere il lease.
  8. Il processore di processi B ottiene il lease nel BLOB 2 per 15 secondi. Ora può limitare le richieste al database a una velocità di 100 al secondo.
  9. Il processore di processi B rimuove dalla coda B 100 record dalla coda B e li scrive.
  10. Un secondo passa.
  11. Processore di processi A vede che la coda A ha più record e tenta di ottenere un lease esclusivo nel BLOB 6.
  12. Il processore di processi B vede che la coda B ha più record e tenta di ottenere un lease esclusivo nel BLOB 3.
  13. Processore di processi A ottiene il lease nel BLOB 6 per 15 secondi. Ora può limitare le richieste al database a una velocità di 100 al secondo.
  14. Il processore di processi B ottiene il lease nel BLOB 3 per 15 secondi. Ora può limitare le richieste al database a una velocità di 200 al secondo. Contiene anche il lease per BLOB 2.
  15. Processore di processi A rimuove dalla coda A 100 record dalla coda A e li scrive.
  16. Il processore di processi B rimuove 200 record dalla coda B e li scrive.
  17. Un secondo passa.
  18. Processore di processi A vede che la coda A ha più record e tenta di ottenere un lease esclusivo nel BLOB 0.
  19. Il processore di processi B vede che la coda B ha più record e tenta di ottenere un lease esclusivo nel BLOB 1.
  20. Processore di processi A ottiene il lease nel BLOB 0 per 15 secondi. Ora può limitare le richieste al database a una velocità di 200 al secondo. Contiene anche il lease per BLOB 6.
  21. Il processore di processi B ottiene il lease nel BLOB 1 per 15 secondi. Ora può limitare le richieste al database a una velocità di 300 al secondo. Detiene anche il lease per i blob 2 e 3.
  22. Processore di processi A rimuove dalla coda A 200 record dalla coda A e li scrive.
  23. Il processore di processi B rimuove 300 record dalla coda B e li scrive.
  24. E così via.

Dopo 15 secondi, uno o entrambi i processi non verranno ancora completati. Quando i lease scadono, un processore deve anche ridurre il numero di richieste che dequeue e scritture.

Le implementazioni di questo modello sono disponibili in linguaggi di programmazione diversi:

  • L'implementazione di Go è disponibile in GitHub.
  • L'implementazione java è disponibile in GitHub.

Passaggi successivi

Quando si implementa questo modello, possono essere rilevanti anche le indicazioni seguenti:

I modelli e le linee guida seguenti possono essere rilevanti anche quando si implementa questo modello:

  • Throttling. Il modello di limitazione della frequenza viene in genere implementato in risposta a un servizio limitato.

  • Retry. Quando le richieste a un servizio limitato generano errori di limitazione, è in genere consigliabile ripetere tali richieste dopo un intervallo appropriato.

  • Queue-Based il livellamento del carico è simile al modello di limitazione della velocità, ma differisce in diversi modi principali:

    • La limitazione della frequenza non deve necessariamente usare le code per gestire il carico, ma deve usare un servizio di messaggistica durevole. Ad esempio, un modello di limitazione della frequenza può usare servizi come Apache Kafka o Hub eventi.

    • Il modello di limitazione della velocità introduce il concetto di sistema di esclusione reciproca distribuita nelle partizioni, che consente di gestire la capacità per più processi non coordinati che comunicano con lo stesso servizio limitato.

    • Un Queue-Based modello di livellamento del carico è applicabile ogni volta che si verifica una mancata corrispondenza delle prestazioni tra i servizi o si vuole migliorare la resilienza. È quindi un modello più ampio rispetto a Rate Limit, che è più specifico per l'accesso efficiente a un servizio limitato.