Come evitare "riprovare tempeste" nei servizi distribuiti?


10

Una "tempesta di tentativi" si verifica quando i client sono configurati per riprovare un determinato numero di volte prima di arrendersi, è necessario un criterio di ripetizione a causa della perdita di pacchetti durante il normale funzionamento di un servizio.

Prendi questo esempio:

Architettura di esempio

Se, ad esempio, i servizi nel loro insieme fossero ridimensionati per supportare 80.000 richieste al secondo e funzionassero a circa l'80% della capacità, un picco del traffico che causava la ricezione di 101.000 richieste al secondo causerebbe il fallimento di 1.000 di tali richieste.

Quando le politiche di riprovare prendono il via, si ottengono ulteriori 1.000 richieste, a seconda di dove è stato rilevato l'errore che porterebbe il servizio a un totale di 102.000 richieste al secondo - da lì il servizio passa in una spirale mortale raddoppiando il numero di richieste non riuscite ogni secondo.

Oltre all'enorme provisioning eccessivo di servizi oltre la prevista transazione di picco, sarebbe inefficiente. Quali strategie puoi adottare per evitare "riprovare tempeste"?


Se 100kQPS rappresenta l'80% della capacità, 101kQPS non dovrebbe comportare guasti 1k, ma dovrebbe comportare zero guasti - non è forse questo il punto di overprovisioning?
Adrian,

@Adrian hai ragione, è stato un esempio inventato per spiegare il punto: stavo cercando di essere abbastanza riduttivo da chiarire il mio punto senza essere eccessivamente astratto. Ho corretto il "ridimensionato per supportare 100.000" a "ridimensionato per supportare 80.000".
Richard Slater,

Risposte:


7

Dipende da cosa stai cercando di evitare.

Se stai cercando di evitare qualsiasi interruzione del servizio di qualcosa che è un servizio veramente critico (sto pensando in termini di "persone moriranno se la mia chiamata all'API non viene adeguatamente servita"), devi solo fare un bilancio per le enormi inefficienze che provengono ampiamente da risorse dedicate di provisioning. E sì, devono essere dedicati, nulla di tutto ciò consente di imporre picchi di traffico, l'aggiunta di più servizi causerebbe quindi un'interruzione.

Nello scenario di gran lunga più probabile che il servizio venga interrotto sarebbe scomodo, è possibile affrontare il problema sia dal lato client che dal lato server. Anche se vale la pena notare che è logicamente impossibile risolvere effettivamente il problema di molto traffico perché senza elaborare il traffico (che consuma risorse) non è possibile sapere se si tratta di un nuovo tentativo, se si tratta di un nuovo tentativo per una richiesta che ha avuto esito positivo ma gestita in modo errato dal cliente, se si tratta di un DDOS, ecc. Ma puoi mitigare l'impatto.

Nel codice client scrivere una logica di tentativi sensati che abbia un limite superiore e un meccanismo per un errore corretto. In questo modo non attacchi i tuoi utenti in un ciclo infinito di richieste non riuscite e dai loro un errore dicendo loro di provare qualunque cosa abbiano appena fatto in poco tempo.

Per la tua infrastruttura lato server la soluzione più semplice è quella di limitare. Limiti rigidi alle richieste, soprattutto se puoi provare a diffonderle logicamente in base al tuo caso d'uso specifico (ad es. Se hai un servizio centralizzato prendere alcune decisioni difficili, vuoi iniziare a bloccare le richieste geograficamente distanti che potrebbero comportare il blocco dei thread lato server? O vuoi distribuire uniformemente la tua interruzione inevitabile ma minore? ecc.) Fondamentalmente si riduce al fatto che restituire intenzionalmente un 503 da un gateway è molto più economico che lasciare passare la richiesta e inviare un 504 Comunque. Fondamentalmente forzare i clienti a comportarsi in base a ciò che è attualmente possibile fornire e fornire le risposte corrette in modo che i clienti possano reagire in modo appropriato.


5

Un modo per prevenire questi tentativi è utilizzare i meccanismi di backoff.

Dalla sezione Implementa backoff on retry della Guida alla progettazione di Google App Engine for Scale :

Il tuo codice può riprovare in caso di errore, sia chiamando un servizio come Cloud Datastore o un servizio esterno utilizzando URL Fetch o l'API Socket. In questi casi, è sempre necessario implementare una politica di backoff esponenziale randomizzata al fine di evitare il tuono del gregge . È inoltre necessario limitare il numero totale di tentativi e gestire gli errori dopo aver raggiunto il limite massimo di tentativi.

La maggior parte delle API GAE ha già tali meccanismi / criteri di backoff abilitati per impostazione predefinita.


Grazie, l'implementazione dei meccanismi di backoff è un ottimo consiglio, di solito vado per backoff esponenziale configurabile usando il Transient Fault Handling Application Block . Tuttavia, attraverso oltre 5 anni di esperienza operativa nel funzionamento di applicazioni su più scale in Azure, anche con backoff esponenziali in atto, i "tentativi di riprovare" si verificano ancora abbastanza spesso - non sono mai stato in grado di trovare una strategia praticabile per evitarli.
Richard Slater,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.