Eliminare le interruzioni di elaborazione delle attività sostituendo RabbitMQ con Apache Kafka Senza tempi di inattività

Ridimensionare l’infrastruttura di backend per gestire l’iper-crescita è una delle tante sfide interessanti del lavoro in DoorDash. A metà 2019, abbiamo dovuto affrontare importanti sfide di ridimensionamento e frequenti interruzioni che hanno coinvolto Celery e RabbitMQ, due tecnologie che alimentano il sistema che gestisce il lavoro asincrono consentendo funzionalità critiche della nostra piattaforma, tra cui il checkout degli ordini e le assegnazioni di Dasher.

Abbiamo risolto rapidamente questo problema con un semplice sistema di elaborazione delle attività asincrono basato su Apache Kafka che ha interrotto le interruzioni mentre continuavamo a iterare su una soluzione robusta. La nostra versione iniziale implementava il più piccolo set di funzionalità necessarie per ospitare una grande porzione di attività Celery esistenti. Una volta in produzione, abbiamo continuato ad aggiungere il supporto per altre funzionalità di Celery affrontando nuovi problemi sorti durante l’utilizzo di Kafka.

I problemi che abbiamo dovuto affrontare utilizzando Celery e RabbitMQ

RabbitMQ e Celery erano pezzi mission critical della nostra infrastruttura che ha alimentato oltre 900 diverse attività asincrone a DoorDash, tra cui il checkout degli ordini, la trasmissione degli ordini commerciali e l’elaborazione della posizione Dasher. Il problema affrontato da DoorDash era che RabbitMQ stava spesso scendendo a causa di un carico eccessivo. Se l’elaborazione delle attività è andato giù, DoorDash effettivamente è andato giù e gli ordini non potevano essere completati, con conseguente perdita di entrate per i nostri commercianti e Dasher, e una scarsa esperienza per i nostri consumatori. Abbiamo affrontato problemi sui seguenti fronti:

  • Disponibilità: interruzioni causate dalla riduzione della disponibilità della domanda.
  • Scalabilità: RabbitMQ non poteva scalare con la crescita del nostro business.
  • Osservabilità: RabbitMQ offriva metriche limitate e i lavoratori di Celery erano opachi.
  • Efficienza operativa: il riavvio di questi componenti è stato un processo manuale che richiedeva molto tempo.

Perché il nostro sistema di elaborazione delle attività asincrono non era altamente disponibile

Questo problema più grande che abbiamo dovuto affrontare erano le interruzioni, e spesso arrivavano quando la domanda era al suo apice. RabbitMQ sarebbe andato giù a causa di carico, eccessiva connessione churn, e altri motivi. Gli ordini verrebbero fermati e dovremmo riavviare il nostro sistema o talvolta persino far apparire un broker completamente nuovo e un failover manuale per recuperare dall’interruzione.

Per approfondire i problemi di disponibilità, abbiamo trovato i seguenti sotto-problemi:

  • Celery consente agli utenti di pianificare le attività in futuro con un conto alla rovescia o ETA. Il nostro uso pesante di questi conti alla rovescia ha comportato notevoli aumenti di carico sul broker. Alcune delle nostre interruzioni erano direttamente correlate a un aumento delle attività con conti alla rovescia. Alla fine abbiamo deciso di limitare l’uso dei conti alla rovescia a favore di un altro sistema che avevamo in atto per pianificare il lavoro in futuro.
  • Improvvise esplosioni di traffico lascerebbero RabbitMQ in uno stato degradato in cui il consumo di attività era significativamente inferiore al previsto. Nella nostra esperienza, questo potrebbe essere risolto solo con un rimbalzo RabbitMQ. RabbitMQ ha un concetto di controllo del flusso in cui ridurrà la velocità delle connessioni che vengono pubblicate troppo rapidamente in modo che le code possano tenere il passo. Il controllo del flusso è stato spesso, ma non sempre, coinvolto in queste degradazioni di disponibilità. Quando il controllo del flusso entra in gioco, i publisher lo vedono effettivamente come latenza di rete. La latenza di rete riduce i tempi di risposta; se la latenza aumenta durante il traffico di picco, possono verificarsi rallentamenti significativi che si verificano a cascata man mano che le richieste si accumulano a monte.
  • I nostri web worker python uWSGI avevano una funzionalità chiamata harakiri che era abilitata a uccidere tutti i processi che superavano un timeout. Durante interruzioni o rallentamenti, harakiri ha provocato un churn connessione ai broker RabbitMQ come i processi sono stati ripetutamente uccisi e riavviati. Con migliaia di web worker in esecuzione in un dato momento, qualsiasi lentezza che ha innescato harakiri a sua volta contribuirebbe ancora di più alla lentezza aggiungendo un carico extra a RabbitMQ.
  • In produzione abbiamo sperimentato diversi casi in cui l’elaborazione delle attività nei consumatori Sedano fermato, anche in assenza di carico significativo. I nostri sforzi di indagine non hanno prodotto prove di eventuali vincoli di risorse che avrebbero fermato l’elaborazione, e gli operai hanno ripreso l’elaborazione una volta che sono stati rimbalzati. Questo problema non è mai stato causato dalla radice, anche se sospettiamo un problema negli stessi lavoratori di Celery e non RabbitMQ.

Nel complesso, tutti questi problemi di disponibilità erano inaccettabili per noi in quanto l’alta affidabilità è una delle nostre più alte priorità. Poiché queste interruzioni ci costavano molto in termini di ordini mancati e credibilità, avevamo bisogno di una soluzione che risolvesse questi problemi il prima possibile.

Perché la nostra soluzione legacy non ha scalato

Il prossimo problema più grande era la scala. DoorDash sta crescendo rapidamente e abbiamo raggiunto rapidamente i limiti della nostra soluzione esistente. Avevamo bisogno di trovare qualcosa che tenesse il passo con la nostra continua crescita poiché la nostra soluzione legacy aveva i seguenti problemi:

Colpire il limite di ridimensionamento verticale

Stavamo usando la più grande soluzione RabbitMQ a nodo singolo disponibile che era disponibile per noi. Non c’era alcun percorso per scalare verticalmente ulteriormente e stavamo già iniziando a spingere quel nodo ai suoi limiti.

La modalità High Availability ha limitato la nostra capacità

A causa della replica, la modalità High Availability primaria-secondaria (HA) ha ridotto il throughput rispetto all’opzione single node, lasciandoci con ancora meno headroom rispetto alla soluzione single node. Non potevamo permetterci di scambiare il throughput per la disponibilità.

In secondo luogo, la modalità HA primario-secondario non ha, in pratica, ridotto la gravità delle nostre interruzioni. I failover impiegavano più di 20 minuti per essere completati e spesso si bloccavano richiedendo un intervento manuale. I messaggi sono stati spesso persi nel processo pure.

Stavamo rapidamente esaurendo l’headroom mentre DoorDash continuava a crescere e spingeva la nostra elaborazione dei compiti ai suoi limiti. Avevamo bisogno di una soluzione che potesse scalare orizzontalmente man mano che le nostre esigenze di elaborazione crescevano.

Come Celery e RabbitMQ offrivano osservabilità limitata

Sapere cosa sta succedendo in qualsiasi sistema è fondamentale per garantirne la disponibilità, la scalabilità e l’integrità operativa.

Mentre navigavamo i problemi sopra descritti, abbiamo notato che:

  • Eravamo limitati a un piccolo set di metriche RabbitMQ a nostra disposizione.
  • Avevamo una visibilità limitata sugli stessi lavoratori di Celery.

Dovevamo essere in grado di vedere le metriche in tempo reale di ogni aspetto del nostro sistema, il che significava che anche le limitazioni di osservabilità dovevano essere affrontate.

Le sfide di efficienza operativa

Abbiamo anche affrontato diversi problemi con RabbitMQ operativo:

  • Abbiamo spesso dovuto fare il failover del nostro nodo RabbitMQ a uno nuovo per risolvere il degrado persistente che abbiamo osservato. Questa operazione è stata manuale e richiede tempo per gli ingegneri coinvolti e spesso doveva essere fatto a tarda notte, al di fuori degli orari di punta.
  • Non c’erano in-house Celery o RabbitMQ esperti a DoorDash che abbiamo potuto appoggiarsi per aiutare a elaborare una strategia di ridimensionamento per questa tecnologia.

Il tempo di progettazione impiegato per gestire e mantenere RabbitMQ non era sostenibile. Avevamo bisogno di qualcosa che soddisfacesse meglio le nostre esigenze attuali e future.

Potenziali soluzioni ai nostri problemi con Celery e RabbitMQ

Con i problemi sopra descritti, abbiamo considerato le seguenti soluzioni:

  • Cambiare il broker Celery da RabbitMQ a Redis o Kafka. Ciò ci consentirebbe di continuare a utilizzare Celery, con un datastore di supporto diverso e potenzialmente più affidabile.
  • Aggiungi il supporto multi-broker alla nostra app Django in modo che i consumatori possano pubblicare su N diversi broker in base a qualsiasi logica volessimo. L’elaborazione delle attività verrà suddivisa in più broker, quindi ogni broker sperimenterà una frazione del carico iniziale.
  • Aggiornamento alle versioni più recenti di Celery e RabbitMQ. Ci si aspettava che le versioni più recenti di Celery e RabbitMQ risolvessero problemi di affidabilità, guadagnandoci tempo poiché stavamo già estraendo componenti dal nostro monolite Django in parallelo.
  • Migrare a una soluzione personalizzata supportata da Kafka. Questa soluzione richiede uno sforzo maggiore rispetto alle altre opzioni che abbiamo elencato, ma ha anche più potenziale per risolvere ogni problema che stavamo avendo con la soluzione legacy.

Ogni opzione ha i suoi pro e contro:

Opzione Pro Contro
Redis come broker
  • Migliorata la disponibilità con ElasticCache e multi-AZ supporto
  • Migliorato il broker observability con ElasticCache come il broker
  • Migliorata l’efficienza operativa
  • In-casa esperienza operativa e la competenza con Redis
  • Un broker di swap è diritto-foward come opzione supportata in Sedano
  • Harakiri connessione varianza di non compromettere in modo significativo Redis prestazioni
  • Incompatibile con Redis modalità cluster
  • Singolo nodo Redis non la scalabilità orizzontale
  • Nessun Sedano observability miglioramenti
  • Questa soluzione non risolve il osservato problema di Sedano lavoratori smesso di attività di elaborazione
Kafka come broker
  • Kafka può essere estremamente disponibile
  • Kafka è scalabile orizzontalmente
  • Migliorata observability con Kafka come il broker
  • Migliorata l’efficienza operativa
  • DoorDash aveva in casa Kafka competenza
  • Un broker di swap è diritto-foward come opzione supportata in Sedano
  • Harakiri connessione varianza di non compromettere in modo significativo Kafka prestazioni
  • Kafka non è supportato da Sedano di sicurezza
  • non indirizzo osservato problema di Sedano lavoratori interrompere le attività di elaborazione
  • Nessun sedano observability miglioramenti
  • Nonostante l’esperienza, non aveva operato Kafka a scala DoorDash.
diversi agenti
  • Migliorata la disponibilità
  • scalabilità Orizzontale
  • Non observability miglioramenti
  • Nessun efficienza operativa miglioramenti
  • Non indirizzo osservato problema di Sedano lavoratori interrompere le attività di elaborazione
  • non affrontare la questione con harakiri-indotta connessione sfornare
versioni di Aggiornamento
  • Potrebbe migliorare il problema di dove RabbitMQ diventa bloccato in uno stato degradato
  • Potrebbe migliorare il problema di Sedano lavoratori bloccati
  • Potrebbe acquistare per noi headroom per implementare una strategia a lungo termine
  • Non e ‘ garantita a fissare la nostra osservato bug
  • non immediatamente risolvere i nostri problemi con la disponibilità, la scalabilità, observability, e l’efficienza operativa
  • le versioni più Recenti di RabbitMQ e Sedano richiesto più recenti versioni di Python.
  • non affrontare la questione con harakiri-indotta connessione sfornare
Custom Kafka soluzione
  • Kafka può essere estremamente disponibile
  • Kafka è scalabile orizzontalmente
  • Migliorata observability con Kakfa come il broker
  • Migliorata l’efficienza operativa
  • In-casa Kafka competenza
  • Un broker cambiamento è diritto-foward
  • Harakiri connessione varianza di non compromettere in modo significativo Kafka prestazioni
  • Indirizzi osservato problema di Sedano lavoratori interrompere le attività di elaborazione
  • Richiede più lavori di realizzazione di tutte le altre opzioni
  • Nonostante l’esperienza, non aveva operato Kafka a scala DoorDash

la Nostra strategia per l’integrazione di Kafka

Data la nostra richiesta uptime del sistema, abbiamo ideato il nostro onboarding strategia basata sui seguenti principi per ottimizzare l’affidabilità dei benefici nel breve lasso di tempo. Questa strategia ha coinvolto tre fasi:

  • Colpire il terreno in esecuzione: Volevamo sfruttare le basi della soluzione che stavamo costruendo mentre stavamo iterando su altre parti di esso. Paragoniamo questa strategia alla guida di un’auto da corsa mentre si scambia una nuova pompa del carburante.
  • Scelte di progettazione per una perfetta adozione da parte degli sviluppatori: Volevamo ridurre al minimo lo sforzo sprecato da parte di tutti gli sviluppatori che potrebbero essere il risultato della definizione di un’interfaccia diversa.
  • Implementazione incrementale con zero tempi di inattività: Invece di una grande versione appariscente in fase di test in natura per la prima volta con una maggiore probabilità di guasti, ci siamo concentrati sulla spedizione di funzionalità indipendenti più piccole che potrebbero essere testate individualmente in natura per un periodo di tempo più lungo.

Colpire il terreno

Il passaggio a Kafka ha rappresentato un importante cambiamento tecnico nel nostro stack, ma che era assolutamente necessario. Non abbiamo avuto tempo da perdere dal momento che ogni settimana stavamo perdendo affari a causa dell’instabilità della nostra soluzione RabbitMQ legacy. La nostra priorità principale era creare un prodotto minimo vitale (MVP) per portarci stabilità provvisoria e darci l’headroom necessario per iterare e preparare una soluzione più completa con un’adozione più ampia.

Il nostro MVP consisteva in produttori che pubblicavano nomi completi delle attività (FQN) e argomenti sottaceti a Kafka mentre i nostri consumatori leggevano quei messaggi, importavano le attività dall’FQN e le eseguivano in modo sincrono con gli argomenti specificati.

L'architettura Minimal Viable Product(MVP) che abbiamo deciso di costruire includeva uno stato intermedio in cui avremmo pubblicato attività mutuamente esclusive sia per l'eredità (linee tratteggiate rosse) che per i nuovi sistemi (linee continue verdi), prima dello stato finale in cui avremmo smesso di pubblicare attività su RabbitMQ.1

Figura 1: L’architettura Minimal Viable Product(MVP) che abbiamo deciso di costruire includeva uno stato intermedio in cui avremmo pubblicato attività mutuamente esclusive sia per l’eredità (linee tratteggiate rosse) che per i nuovi sistemi (linee continue verdi), prima dello stato finale in cui avremmo smesso di pubblicare attività su RabbitMQ.

Scelte di progettazione per un’adozione senza soluzione di continuità da parte degli sviluppatori

A volte, l’adozione degli sviluppatori è una sfida più grande dello sviluppo. Lo abbiamo reso più semplice implementando un wrapper per l’annotazione @task di Celery che indirizzava dinamicamente gli invii delle attività a entrambi i sistemi in base a flag di funzionalità configurabili dinamicamente. Ora la stessa interfaccia potrebbe essere utilizzata per scrivere attività per entrambi i sistemi. Con queste decisioni in atto, i team di ingegneri non hanno dovuto fare alcun lavoro aggiuntivo per integrarsi con il nuovo sistema, salvo l’implementazione di un singolo flag di funzionalità.

Volevamo implementare il nostro sistema non appena il nostro MVP era pronto, ma non supportava ancora tutte le stesse funzionalità di Celery. Celery consente agli utenti di configurare le proprie attività con i parametri nella loro annotazione attività o quando inviano la loro attività. Per consentirci di avviare più rapidamente, abbiamo creato una whitelist di parametri compatibili e abbiamo scelto di supportare il minor numero di funzionalità necessarie per supportare la maggior parte delle attività.

Abbiamo rapidamente aumentato il volume delle attività all'MVP basato su Kafka, iniziando prima con compiti a basso rischio e a bassa priorità. Alcuni di questi erano compiti che si svolgevano in ore non di punta, il che spiega i picchi della metrica raffigurata sopra.

Figura 2: Abbiamo rapidamente aumentato il volume delle attività all’MVP basato su Kafka, iniziando prima con compiti a basso rischio e a bassa priorità. Alcuni di questi erano compiti che si svolgevano in ore non di punta, il che spiega i picchi della metrica raffigurata sopra.

Come si vede in Figura 2, con le due decisioni di cui sopra, abbiamo lanciato il nostro MVP dopo due settimane di sviluppo e raggiunto una riduzione dell ‘ 80% del carico di attività RabbitMQ un’altra settimana dopo il lancio. Abbiamo affrontato rapidamente il nostro problema principale delle interruzioni e nel corso del progetto abbiamo supportato funzionalità sempre più esoteriche per consentire l’esecuzione delle attività rimanenti.

Implementazione incrementale, zero downtime

La possibilità di cambiare cluster Kafka e passare da RabbitMQ a Kafka in modo dinamico senza impatto sul business era estremamente importante per noi. Questa capacità ci ha anche aiutato in una varietà di operazioni come la manutenzione del cluster, lo spargimento del carico e le migrazioni graduali. Per implementare questo rollout, abbiamo utilizzato i flag delle funzionalità dinamiche sia a livello di invio dei messaggi che a livello di consumo dei messaggi. Il costo di essere pienamente dinamico qui è stato quello di mantenere la nostra flotta di lavoratori in esecuzione a doppia capacità. Metà di questa flotta era dedicata a RabbitMQ e il resto a Kafka. Gestire la flotta di lavoratori a doppia capacità stava sicuramente tassando la nostra infrastruttura. A un certo punto abbiamo persino creato un cluster Kubernetes completamente nuovo solo per ospitare tutti i nostri lavoratori.

Durante la fase iniziale di sviluppo, questa flessibilità ci è servita bene. Una volta che abbiamo avuto più fiducia nel nostro nuovo sistema, abbiamo esaminato i modi per ridurre il carico sulla nostra infrastruttura, come l’esecuzione di più processi di consumo per macchina lavoratore. Durante la transizione di vari argomenti, siamo stati in grado di iniziare a ridurre i conteggi dei lavoratori per RabbitMQ mantenendo una piccola capacità di riserva.

Nessuna soluzione è perfetta, iterare se necessario

Con il nostro MVP in produzione, abbiamo avuto l’headroom necessario per iterare e lucidare il nostro prodotto. Abbiamo classificato ogni caratteristica Sedano mancante per il numero di attività che ha utilizzato per aiutarci a decidere quali implementare prima. Le funzionalità utilizzate solo da alcune attività non sono state implementate nella nostra soluzione personalizzata. Invece, abbiamo riscritto quelle attività per non utilizzare quella caratteristica specifica. Con questa strategia, alla fine abbiamo spostato tutte le attività dal sedano.

l’Utilizzo di Kafka ha anche introdotto nuovi problemi che ha bisogno la nostra attenzione:

  • Head-of-the-line di blocco che ha portato in attività di ritardi di elaborazione
  • Distribuzioni attivato la partizione di riequilibrio che ha portato anche a ritardi

Kafka head-of-the-line problema di blocco

Kafka argomenti sono suddivisi in modo tale che un singolo consumatore (per ogni gruppo di consumatori) legge i messaggi per i suoi assegnato partizioni nell’ordine in cui sono arrivati. Se un messaggio in una singola partizione richiede troppo tempo per essere elaborato, bloccherà il consumo di tutti i messaggi dietro di esso in quella partizione, come mostrato nella Figura 3, sotto. Questo problema può essere particolarmente disastroso nel caso di un argomento ad alta priorità. Vogliamo essere in grado di continuare a elaborare i messaggi in una partizione nel caso in cui si verifichi un ritardo.

Nel problema di blocco head-of-the-line di Kafka, un messaggio lento in una partizione (in rosso) blocca tutti i messaggi dietro di esso dall'elaborazione. Altre partizioni continuerebbero ad essere elaborate come previsto.

Figura 3: Nel problema di blocco head-of-the-line di Kafka, un messaggio lento in una partizione (in rosso) blocca tutti i messaggi dietro di esso dall’elaborazione. Altre partizioni continuerebbero ad essere elaborate come previsto.

Mentre il parallelismo è, fondamentalmente, un problema Python, i concetti di questa soluzione sono applicabili anche ad altri linguaggi. La nostra soluzione, illustrata nella Figura 4, era quella di ospitare un processo Kafka-consumer e più processi di esecuzione delle attività per lavoratore. Il processo Kafka-consumer è responsabile del recupero dei messaggi da Kafka e della loro collocazione in una coda locale letta dai processi di esecuzione delle attività. Continua a consumare fino a quando la coda locale raggiunge una soglia definita dall’utente. Questa soluzione consente ai messaggi nella partizione di fluire e solo un processo di esecuzione dell’attività verrà bloccato dal messaggio lento. La soglia limita anche il numero di messaggi in volo nella coda locale (che potrebbe perdersi in caso di arresto anomalo del sistema).

Figura 4: Il nostro lavoratore Kafka non bloccante è costituito da una coda di messaggi locale e da due tipi di processi: un processo kafka-consumer e più processi task-executor. Mentre un consumatore kafka può leggere da più partizioni, per semplicità ne descriveremo solo una. Questo diagramma mostra che un messaggio di elaborazione lenta (in rosso) blocca solo un singolo esecutore di attività fino al completamento, mentre altri messaggi dietro di esso nella partizione continuano ad essere elaborati da altri esecutori di attività.

Figura 4: Il nostro lavoratore Kafka non bloccante consiste in una coda di messaggi locale e due tipi di processi: un processo kafka-consumer e più processi task-executor. Mentre un consumatore kafka può leggere da più partizioni, per semplicità ne descriveremo solo una. Questo diagramma mostra che un messaggio di elaborazione lenta (in rosso) blocca solo un singolo esecutore di attività fino al completamento, mentre altri messaggi dietro di esso nella partizione continuano ad essere elaborati da altri esecutori di attività.

La disruptiveness di deploys

Distribuiamo la nostra app Django più volte al giorno. Uno svantaggio della nostra soluzione che abbiamo notato è che una distribuzione innesca un riequilibrio delle assegnazioni delle partizioni in Kafka. Nonostante l’utilizzo di un gruppo di consumatori diverso per argomento per limitare l’ambito di riequilibrio, le distribuzioni hanno comunque causato un rallentamento momentaneo nell’elaborazione dei messaggi poiché il consumo delle attività doveva interrompersi durante il riequilibrio. I rallentamenti possono essere accettabili nella maggior parte dei casi quando eseguiamo rilasci pianificati, ma possono essere catastrofici quando, ad esempio, stiamo facendo un rilascio di emergenza per correggere un bug. La conseguenza sarebbe l’introduzione di un rallentamento dell’elaborazione a cascata.

Le versioni più recenti di Kafka e client supportano il ribilanciamento cooperativo incrementale, che ridurrebbe in modo massiccio l’impatto operativo di un ribilanciamento. Aggiornare i nostri clienti per supportare questo tipo di riequilibrio sarebbe la nostra soluzione preferita in futuro. Sfortunatamente, il riequilibrio cooperativo incrementale non è ancora supportato nel nostro cliente Kafka scelto.

Key wins

Con la conclusione di questo progetto, abbiamo realizzato miglioramenti significativi in termini di uptime, scalabilità, osservabilità e decentralizzazione. Queste vittorie sono state fondamentali per garantire la continua crescita del nostro business.

Niente più interruzioni ripetute

Abbiamo interrotto le interruzioni ripetute quasi non appena abbiamo iniziato a implementare questo approccio Kafka personalizzato. Le interruzioni si sono tradotte in esperienze utente estremamente scarse.

  • Implementando solo un piccolo sottoinsieme delle funzionalità di sedano più utilizzate nel nostro MVP siamo stati in grado di spedire il codice di lavoro alla produzione in due settimane.
  • Con l’MVP in atto siamo stati in grado di ridurre significativamente il carico su RabbitMQ e Celery mentre continuavamo a indurire la nostra soluzione e implementare nuove funzionalità.

L’elaborazione delle attività non era più il fattore limitante per la crescita

Con Kafka al centro della nostra architettura, abbiamo costruito un sistema di elaborazione delle attività altamente disponibile e scalabile orizzontalmente, consentendo a DoorDash e ai suoi clienti di continuare la loro crescita.

Osservabilità massicciamente aumentata

Poiché questa era una soluzione personalizzata, siamo stati in grado di cuocere in più metriche a quasi tutti i livelli. Ogni coda, lavoratore e attività era completamente osservabile a un livello molto granulare negli ambienti di produzione e sviluppo. Questa maggiore osservabilità è stata una grande vittoria non solo in termini di produzione, ma anche in termini di produttività degli sviluppatori.

Decentramento operativo

Con i miglioramenti di osservabilità, siamo stati in grado di templatizzare i nostri avvisi come moduli Terraform e assegnare esplicitamente i proprietari a ogni singolo argomento e, implicitamente, a tutte le attività 900-plus.

Una guida operativa dettagliata per il sistema di elaborazione delle attività rende le informazioni accessibili a tutti gli ingegneri per eseguire il debug di problemi operativi con i loro argomenti e lavoratori, nonché eseguire operazioni complessive di gestione dei cluster Kafka, se necessario. Le operazioni quotidiane sono self-service e il supporto è raramente necessario dal nostro team di infrastruttura.

Conclusione

Per riassumere, abbiamo raggiunto il limite massimo della nostra capacità di scalare RabbitMQ e abbiamo dovuto cercare alternative. L’alternativa con cui siamo andati era una soluzione personalizzata basata su Kafka. Mentre ci sono alcuni inconvenienti nell’uso di Kafka, abbiamo trovato una serie di soluzioni alternative, descritte sopra.

Quando i flussi di lavoro critici si basano pesantemente sull’elaborazione asincrona delle attività, garantire la scalabilità è della massima importanza. Quando si verificano problemi simili, sentitevi liberi di prendere ispirazione dalla nostra strategia, che ci ha concesso l ‘ 80% del risultato con il 20% dello sforzo. Questa strategia, nel caso generale, è un approccio tattico per mitigare rapidamente i problemi di affidabilità e acquistare il tempo necessario per una soluzione più robusta e strategica.

Ringraziamenti

Gli autori ringraziano Clement Fang, Corry Haines, Danial Asif, Jay Weinstein, Luigi Tagliamonte, Matthew Anger, Shaohua Zhou e Yun-Yu Chen per aver contribuito a questo progetto.

Foto di tian kuan su Unsplash

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.