Cloud Tasks ti consente di inviare grandi volumi di attività asincrone controllando al contempo la frequenza con cui il traffico raggiunge le code e i servizi downstream. Applicando pattern di aumento graduale e controlli della frequenza, puoi scalare i workload in modo fluido senza attivare picchi di latenza della coda o sovraccarico della destinazione quando il traffico aumenta più velocemente di quanto l'infrastruttura possa adattarsi.
Panoramica
Questo documento fornisce linee guida sulle best practice per mantenere prestazioni elevate di Cloud Tasks nelle code con traffico elevato. Una coda con TPS elevato è una coda con 500 o più attività create o inviate al secondo (TPS). Un gruppo di code con TPS elevato è un insieme contiguo di code, ad esempio
[queue0001, queue0002, …, queue0099], che hanno almeno 2000 attività
create o inviate al secondo in totale. Puoi visualizzare le TPS storiche di una coda o di un gruppo di code utilizzando le metriche di Cloud Monitoring, api/request_count per le operazioni CreateTask e queue/task_attempt_count per i tentativi di attività. Le code e i gruppi di code con traffico elevato sono soggetti a due diverse classi generali di errori:
Il sovraccarico della coda si verifica quando la creazione e l'invio di attività a una singola coda o a un gruppo di code aumentano più velocemente di quanto l'infrastruttura della coda possa adattarsi. L'infrastruttura di Google utilizza livelli adattivi che riallocano dinamicamente il carico in base ai pattern di traffico e questo adattamento richiede tempo. Analogamente, il sovraccarico del target si verifica quando la frequenza di invio delle attività causa picchi di traffico nell'infrastruttura target downstream. In entrambi i casi, segui un pattern 500/50/5: quando esegui lo scale up oltre 500 TPS, aumenta il traffico di non più del 50% ogni 5 minuti.
A differenza del pattern 500/50/5 per i workload con un numero elevato di TPS, l'avvio graduale della coda iniziale influisce su tutte le code quando iniziano da uno stato di inattività o di inattività, incluse le code configurate per frequenze di invio moderate. Questo documento esamina diversi scenari che possono introdurre rischi di scalabilità e fornisce esempi di come gestire l'avvio graduale della coda e applicare il pattern 500/50/5.
Aumento iniziale della coda
Il pattern 500/50/5 si applica alle code con un numero elevato di TPS che operano già in uno stato stabile. L'avvio iniziale della coda è un meccanismo di sistema separato che si applica a tutte le code, incluse quelle configurate per frequenze di invio moderate, ad esempio 20-100 TPS, durante la transizione da uno stato di inattività o di inattività a un'operazione in stato stabile.
Una coda entra in uno stato di inattività o di inattività in una delle seguenti situazioni:
La coda è stata appena creata o ripresa dopo essere stata messa in pausa.
La coda è rimasta inattiva anche solo per pochi minuti senza invii attivi.
La coda è in fase di ripristino dopo il backoff dalle risposte di errore HTTP
429o5xxdownstream. Per saperne di più su come il backoff degli errori influisce sulle frequenze di invio, consulta Errori di backoff e frequenze applicate.
Quando le attività arrivano in una coda inattiva o non utilizzata, Cloud Tasks non le invia immediatamente alla frequenza max_dispatches_per_second configurata. Per proteggere le destinazioni downstream da picchi di traffico improvvisi, Cloud Tasks inizia l'invio a una frequenza di base bassa, ad esempio circa 1 TPS. Quindi, aumenta progressivamente la frequenza di invio applicata a intervalli di valutazione periodici. La frequenza applicata continua ad aumentare finché la domanda della coda rimane satura e i gestori di destinazione restituiscono risposte HTTP 2xx. A seconda della frequenza target, il raggiungimento di valori max_dispatches_per_second elevati può richiedere diversi minuti.
Se il tuo workload invia traffico irregolare separato da brevi periodi di inattività di alcuni minuti o più, la coda viene reimpostata su uno stato cold tra i burst e ripete questo processo di aumento graduale per ogni burst. Per ridurre al minimo i ritardi di avvio per i workload sensibili ai burst, segui queste best practice:
Mantieni un flusso costante di traffico di base per mantenere attive le code sensibili alla latenza tra i burst.
Riutilizza le code esistenti anziché eliminarle e ricrearle di frequente.
Esegui il provisioning di una capacità target downstream sufficiente in modo che i gestori restituiscano risposte HTTP
2xxed evitino di restituire risposte di errore HTTP429o5xxche reimpostano la frequenza di invio applicata.
Diagnostica della limitazione della coda
Se la coda invia le attività più lentamente della frequenza
max_dispatches_per_second
configurata, verifica se il collo di bottiglia è dovuto all'avvio della coda inattiva o cold,
al backoff per congestione della destinazione o ai limiti di contemporaneità.
Avvio di una coda inattiva o non utilizzata
La causa è probabilmente l'aumento iniziale della coda se si applicano tutte le seguenti condizioni:
- La coda è stata creata di recente, ripresa dopo essere stata messa in pausa o è rimasta inattiva per alcuni minuti prima dell'arrivo del traffico.
- I tentativi di esecuzione delle attività vengono completati senza errori di destinazione, mostrando
response_code="OK"nella metrica di Monitoringcloudtasks.googleapis.com/queue/task_attempt_count. - I dispatch in corso rimangono al di sotto di
max_concurrent_dispatches, mentrecloudtasks.googleapis.com/queue/depthocloudtasks.googleapis.com/queue/task_attempt_delayssono elevati. Allo stesso tempo, la frequenza di invio incloudtasks.googleapis.com/queue/task_attempt_countaumenta progressivamente a intervalli di valutazione periodici.
Per saperne di più sulle metriche di Monitoring, consulta Invia metriche a Monitoring.
Sovraccarico del target e backoff per congestione
Cloud Tasks riduce dinamicamente le frequenze di invio se la destinazione downstream restituisce risposte di errore HTTP 429 Too Many Requests o HTTP 5xx, ad esempio HTTP 503 Service Unavailable. Questo backoff per congestione impedisce il sovraccarico della destinazione.
Per verificare se gli errori di destinazione causano la limitazione degli invii da parte di Cloud Tasks, visualizza la metrica di Monitoring cloudtasks.googleapis.com/queue/task_attempt_count raggruppata per l'etichetta response_code. Se il logging della coda è abilitato, puoi anche esaminare i log delle risposte ai tentativi in Esplora log:
resource.type="cloud_tasks_queue"
jsonPayload.attemptResponseLog.status!="OK"
Sia in response_code che in jsonPayload.attemptResponseLog.status, Cloud Tasks registra i codici di stato gRPC canonici anziché i codici di stato HTTP numerici. Ad esempio, HTTP 429 viene mappato a RESOURCE_EXHAUSTED, HTTP 503 viene mappato a UNAVAILABLE, HTTP 500 viene mappato a INTERNAL e HTTP 504 o un timeout viene mappato a DEADLINE_EXCEEDED.
Se osservi RESOURCE_EXHAUSTED, UNAVAILABLE o altri stati di errore del server dalla destinazione, scala le istanze del servizio di destinazione o aumenta la contemporaneità della destinazione prima di aumentare i limiti di invio della coda.
Per saperne di più sull'abilitazione dei log delle code, consulta Utilizza Cloud Logging con le code.
Collo di bottiglia della concorrenza
In base alla legge di Little, la latenza target downstream limita il throughput di invio massimo:
Max Throughput (tasks/sec) = min(
max_dispatches_per_second,
max_concurrent_dispatches / Downstream Latency in seconds
)
Ad esempio, se la latenza di esecuzione downstream è di 2 secondi e max_concurrent_dispatches è impostato su 100, la velocità effettiva è limitata a 50 attività al secondo, anche se max_dispatches_per_second è configurato su 500.
Per diagnosticare se la concorrenza è il collo di bottiglia, dividi max_concurrent_dispatches per la latenza di risposta target registrata nel campo jsonPayload.attemptResponseLog.attemptDuration in Logging. Confronta questa soglia calcolata con la frequenza di invio effettiva nella metrica di Monitoring cloudtasks.googleapis.com/queue/task_attempt_count. Se i dispacci effettivi si stabilizzano vicino a questa soglia mentre cloudtasks.googleapis.com/queue/depth o cloudtasks.googleapis.com/queue/task_attempt_delays cresce, esegui una delle seguenti azioni:
- Se la destinazione downstream ha capacità di riserva, ad esempio quando le attività trascorrono la maggior parte della loro durata in attesa di I/O esterno, aumenta
max_concurrent_dispatches. - Se il target downstream ha già un utilizzo elevato delle risorse, l'aumento di
max_concurrent_dispatchespuò peggiorare il sovraccarico del target. In alternativa, fai lo scale out delle istanze target o riduci la latenza di esecuzione del gestore.
Sovraccarico della coda
Le code o i gruppi di code possono sovraccaricarsi ogni volta che il traffico aumenta improvvisamente. Di conseguenza, queste code possono subire:
- Aumento della latenza di creazione delle attività
- Tasso di errori di creazione delle attività aumentato
- Frequenza di invio ridotta
Per difenderti da questo problema, stabilisci dei controlli in qualsiasi situazione in cui la frequenza di creazione o invio di una coda o di un gruppo di code può aumentare improvvisamente. Inizia con un massimo di 500 operazioni al secondo in una coda o un gruppo di code a freddo, quindi aumenta il traffico del 50% ogni 5 minuti. In teoria, puoi arrivare a 740.000 operazioni al secondo dopo 90 minuti utilizzando questa pianificazione di aumento graduale. Ci sono diverse circostanze in cui questo può accadere.
Ad esempio:
- Lancio di nuove funzionalità che fanno un uso intensivo di Cloud Tasks
- Spostamento del traffico tra le code
- Ribilanciamento del traffico su un numero maggiore o minore di code
- Esecuzione di job batch che inseriscono un numero elevato di attività
In questi e altri casi, segui il pattern 500/50/5.
Utilizza la suddivisione del traffico di App Engine
Se un'app App Engine crea le attività, puoi sfruttare la suddivisione del traffico di App Engine (ambiente standard App Engine o ambiente flessibile App Engine) per attenuare gli aumenti del traffico. Suddividendo il traffico tra le versioni (ambiente standard o ambiente flessibile), puoi aumentare nel tempo le richieste con gestione della frequenza per proteggere l'integrità della coda. Ad esempio, considera il caso di un aumento del traffico verso un gruppo di code appena ampliato: supponiamo che [queue0000, queue0199] sia una sequenza di code con un numero elevato di TPS che ricevono un totale di 100.000 creazioni di TPS al picco.
Supponiamo che [queue0200, queue0399] sia una sequenza di nuove code. Dopo aver spostato tutto il traffico, il numero di code nella sequenza raddoppia e il nuovo intervallo di code riceve il 50% del traffico totale della sequenza.
Quando esegui il deployment della versione che aumenta il numero di code, aumenta gradualmente il traffico verso la nuova versione e, di conseguenza, verso le nuove code, utilizzando la suddivisione del traffico:
- Inizia a spostare l'1% del traffico verso la nuova release. Ad esempio, il 50% dell'1% di 100.000 TPS produce 500 TPS per l'insieme di nuove code.
- Ogni 5 minuti, aumenta del 50% il traffico inviato alla nuova release, come descritto nella tabella seguente:
| Minuti dall'inizio del deployment | % del traffico totale trasferito alla nuova versione | % del traffico totale verso le nuove code | % del traffico totale verso le code precedenti |
|---|---|---|---|
| 0 | 1.0 | 0,5 | 99,5 |
| 5 | 1,5 | 0,75 | 99,25 |
| 10 | 2.3 | 1,15 | 98,85 |
| 15 | 3,4 | 1,7 | 98,3 |
| 20 | 5.1 | 2,55 | 97,45 |
| 25 | 7,6 | 3,8 | 96,2 |
| 30 | 11.4 | 5.7 | 94,3 |
| 35 | 17.1 | 8,55 | 91,45 |
| 40 | 25,6 | 12,8 | 87,2 |
| 45 | 38,4 | 19,2 | 80,8 |
| 50 | 57,7 | 28,85 | 71,15 |
| 55 | 86,5 | 43,25 | 56,75 |
| 60 | 100 | 50 | 50 |
Picchi di traffico dovuti alle release
Quando lanci una release che aumenta in modo significativo il traffico verso una coda o un gruppo di code, il rilascio graduale è, ancora una volta, un meccanismo importante per attenuare gli aumenti. Implementa gradualmente le istanze in modo che il lancio iniziale non superi le 500 operazioni totali al secondo per le nuove code, aumentando di non più del 50% ogni 5 minuti.
Nuove code o gruppi di code con TPS elevato
Le code appena create sono particolarmente vulnerabili. I gruppi di code, ad esempio [queue0000, queue0001, …, queue0199], sono sensibili quanto le singole code durante le fasi iniziali dell'implementazione. Per queste code, il rilascio graduale è una strategia importante. Lancia nuovi servizi o servizi aggiornati che creano code o gruppi di code con un numero elevato di TPS in più fasi. Mantieni il carico iniziale al di sotto di 500 TPS e aumenta gradualmente del 50% o meno a intervalli di almeno 5 minuti.
Gruppi di code appena espansi
Quando aumenti la capacità totale di un gruppo di code, ad esempio espandendo [queue0000-queue0199 a queue0000-queue0399], segui il pattern 500/50/5. Per le procedure di implementazione, i nuovi gruppi di code si comportano esattamente come le singole code. Applica il pattern 500/50/5 al nuovo gruppo nel suo complesso, non solo alle singole code all'interno del gruppo.
Per queste espansioni dei gruppi di coda, il rilascio graduale è una strategia importante. Se la sorgente del traffico è App Engine, puoi utilizzare la suddivisione del traffico. Quando esegui la migrazione del servizio per aggiungere attività al numero maggiore di code, implementa gradualmente le istanze in modo che il lancio iniziale non superi le 500 operazioni totali nelle nuove code e aumenta il traffico di non più del 50% ogni 5 minuti.
Espansione del gruppo di code di emergenza
In alcuni casi, potresti voler espandere un gruppo di code esistente, ad esempio perché prevedi che le attività vengano aggiunte al gruppo di code più velocemente di quanto il gruppo possa inviarle. Se distribuisci i nomi delle nuove code in modo uniforme tra i nomi delle code esistenti in ordine lessicografico, puoi inviare immediatamente il traffico a queste code. Assicurati che non ci sia più del 50% di nuove code con interleaving e che il traffico verso ogni coda sia inferiore a 500 TPS. Questo metodo è un'alternativa all'utilizzo della suddivisione del traffico e del lancio graduale.
Per ottenere questo tipo di denominazione intercalata, aggiungi un suffisso alle code che terminano con numeri pari. Ad esempio, se hai 200 code esistenti [queue0000-queue0199] e vuoi creare 100 nuove code, scegli [queue0000a, queue0002a, queue0004a, …, queue0198a] come nomi delle nuove code, anziché [queue0200-queue0299].
Se hai bisogno di un ulteriore aumento, puoi comunque intercalare fino al 50% di code in più ogni 5 minuti.
Inserimenti in coda di attività su larga scala o in batch
Quando devi aggiungere un numero elevato di attività, ad esempio milioni o miliardi, può essere utile un pattern di doppia inserzione. Anziché creare attività da un singolo job, utilizza una coda di inserimento. Ogni attività aggiunta alla coda dell'iniettore si espande e aggiunge 100 attività alla coda o al gruppo di code pertinente. Puoi accelerare la coda dell'iniettore nel tempo, ad esempio iniziando con 5 TPS per generare 500 TPS nella coda o nel gruppo di code di destinazione, quindi aumentando la frequenza del 50% ogni 5 minuti.
Attività denominate
Quando crei una nuova attività, Cloud Tasks le assegna un nome univoco per impostazione predefinita. Puoi assegnare un nome personalizzato a un'attività utilizzando il parametro name. Tuttavia, ciò comporta un overhead significativo delle prestazioni, con conseguente aumento delle latenze e potenzialmente dei tassi di errore associati alle attività denominate. I nomi delle attività sequenziali, come i timestamp, possono aumentare notevolmente questo overhead.
Se assegni i tuoi nomi, utilizza un prefisso ben distribuito per i nomi delle attività, ad esempio un hash dei contenuti. Per ulteriori informazioni sulla denominazione di un'attività, consulta il
riferimento dei campi Task.name.
Sovraccarico del target
Cloud Tasks può sovraccaricare i servizi e le risorse downstream che utilizzi, come App Engine, Firestore in modalità Datastore (Datastore) e la tua rete, se gli invii in coda aumentano improvvisamente. Se si è accumulato un backlog di attività, la riattivazione delle code potrebbe sovraccaricare questi servizi. Per proteggerti dal sovraccarico della destinazione, segui il pattern 500/50/5: se una coda invia più di 500 TPS, aumenta il traffico attivato da una coda di non più del 50% ogni 5 minuti. Per monitorare in modo proattivo gli aumenti del traffico, utilizza le metriche di Monitoring. Puoi utilizzare gli avvisi di Monitoring per rilevare situazioni potenzialmente pericolose.
Riattiva o riprendi le code con TPS elevato
Quando riprendi una coda in pausa, Cloud Tasks applica l'avvio graduale della coda iniziale e aumenta gradualmente la frequenza di invio da una base di riferimento bassa verso la frequenza max_dispatches_per_second configurata della coda. Tuttavia, se si accumula un backlog di attività di grandi dimensioni in una coda con un numero elevato di TPS o in più code che riprendi contemporaneamente, la frequenza di invio può comunque aumentare più velocemente di quanto i servizi di destinazione downstream possano scalare. Per evitare il sovraccarico della destinazione quando riprendi le code con un backlog di grandi dimensioni, scagliona la ripresa di più code. In alternativa, riduci temporaneamente la frequenza di max_dispatches_per_second e aumentala gradualmente seguendo il pattern 500/50/5. Per saperne di più su come Cloud Tasks limita e aumenta l'invio quando le code vengono riprese, consulta Errori di backoff e frequenze applicate.
Attività pianificate collettive
Anche un numero elevato di attività pianificate per l'invio contemporaneo può comportare il rischio di sovraccarico della destinazione. Se devi avviare un numero elevato di attività contemporaneamente, valuta la possibilità di utilizzare i controlli della frequenza della coda per aumentare gradualmente la frequenza di invio o di eseguire il provisioning della capacità target in anticipo.
Fan-out aumentato
Quando aggiorni i servizi chiamati da Cloud Tasks, l'aumento del numero di chiamate remote può creare rischi di produzione. Ad esempio, supponiamo che le attività in una coda con un numero elevato di TPS chiamino il gestore /task-foo. Una nuova release può aumentare in modo significativo il costo della chiamata di /task-foo se, ad esempio, aggiunge al gestore diverse chiamate Datastore costose.
Il risultato netto di una release di questo tipo è un aumento massiccio del traffico di Datastore, direttamente correlato alle variazioni del traffico degli utenti. Utilizza il rilascio graduale o la suddivisione del traffico per gestire l'implementazione.
Nuovi tentativi
Il codice può riprovare in caso di errore durante le chiamate all'API Cloud Tasks. Tuttavia, quando una percentuale significativa di richieste non va a buon fine con errori lato server, una frequenza di nuovi tentativi elevata può sovraccaricare ulteriormente le code e rallentare il ripristino. Limita la quantità di traffico in uscita se il client rileva che una percentuale significativa di richieste non va a buon fine con errori lato server. Ad esempio, puoi utilizzare l'algoritmo di limitazione adattiva descritto nel capitolo Handling Overload del libro Site Reliability Engineering. Le librerie client gRPC di Google implementano una variazione di questo algoritmo.