Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Cosa potrebbe costringere un’azienda così grande come Lamoda, con un processo snello e decine di servizi interconnessi, a cambiare significativamente il proprio approccio? La motivazione può essere completamente diversa: da quella legislativa al desiderio di sperimentare insito in tutti i programmatori.

Ma questo non significa che non puoi contare su ulteriori vantaggi. Sergey Zaika ti dirà esattamente cosa puoi vincere se implementi l'API basata sugli eventi su Kafka (fewald). Si parlerà sicuramente anche di pezzi grossi e di scoperte interessanti: l'esperimento non può farne a meno.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Dichiarazione di non responsabilità: questo articolo si basa sui materiali di un incontro che Sergey ha tenuto a novembre 2018 su HighLoad++. L'esperienza dal vivo di Lamoda nel lavorare con Kafka ha attirato gli ascoltatori non meno di altri servizi in programma. Pensiamo che questo sia un ottimo esempio del fatto che si possono e si devono sempre trovare persone che la pensano allo stesso modo, e gli organizzatori di HighLoad++ continueranno a cercare di creare un'atmosfera favorevole a questo.

Informazioni sul processo

Lamoda è una grande piattaforma di e-commerce che dispone di un proprio contact center, servizio di consegna (e molti affiliati), uno studio fotografico, un enorme magazzino e tutto questo funziona con il proprio software. Esistono decine di metodi di pagamento, partner b2b che possono utilizzare alcuni o tutti questi servizi e desiderano conoscere informazioni aggiornate sui loro prodotti. Inoltre, Lamoda opera in tre paesi oltre alla Federazione Russa e lì tutto è un po' diverso. In totale, ci sono probabilmente più di cento modi per configurare un nuovo ordine, che deve essere elaborato a modo suo. Tutto questo funziona con l’aiuto di decine di servizi che a volte comunicano in modi non ovvi. Esiste anche un sistema centrale la cui responsabilità principale è lo stato degli ordini. La chiamiamo BOB, lavoro con lei.

Strumento di rimborso con API basata sugli eventi

La parola guidato dagli eventi è abbastanza banale; un po' più avanti definiremo più in dettaglio cosa si intende con questo. Inizierò con il contesto in cui abbiamo deciso di provare l'approccio API basato sugli eventi in Kafka.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

In qualsiasi negozio, oltre agli ordini per i quali i clienti pagano, ci sono momenti in cui il negozio è tenuto a restituire denaro perché il prodotto non è adatto al cliente. Si tratta di un processo relativamente breve: chiariamo le informazioni, se necessario, e trasferiamo il denaro.

Ma la restituzione è diventata più complicata a causa dei cambiamenti legislativi e abbiamo dovuto implementare un microservizio separato.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

La nostra motivazione:

  1. Legge FZ-54 - in breve, la legge impone di segnalare all'ufficio delle imposte ogni transazione monetaria, sia essa una dichiarazione o una ricevuta, entro uno SLA abbastanza breve di pochi minuti. Noi, come azienda di e-commerce, svolgiamo molte operazioni. Tecnicamente, ciò significa una nuova responsabilità (e quindi un nuovo servizio) e miglioramenti in tutti i sistemi coinvolti.
  2. BOB si è diviso è un progetto interno dell'azienda per sollevare BOB da un gran numero di responsabilità non fondamentali e ridurne la complessità complessiva.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Questo schema mostra i principali sistemi Lamoda. Ora la maggior parte di loro sono di più una costellazione di 5-10 microservizi attorno a un monolite sempre più piccolo. Stanno crescendo lentamente, ma stiamo cercando di rimpicciolirli, perché posizionare il frammento selezionato al centro è spaventoso: non possiamo permettergli di cadere. Siamo costretti a prenotare tutti gli scambi (frecce) e tenere conto del fatto che qualcuno di essi potrebbe rivelarsi non disponibile.

BOB ha anche molti scambi: sistemi di pagamento, sistemi di consegna, sistemi di notifica, ecc.

Tecnicamente BOB è:

  • ~150 righe di codice + ~100 righe di test;
  • php7.2 + Zend 1 e componenti Symfony 3;
  • >100 API e ~50 integrazioni in uscita;
  • 4 paesi con la propria logica di business.

L'implementazione di BOB è costosa e dolorosa, la quantità di codice e di problemi che risolve è tale che nessuno riesce a metterselo tutto in testa. In generale, ci sono molte ragioni per semplificarlo.

Processo di restituzione

Inizialmente, nel processo sono coinvolti due sistemi: BOB e Pagamento. Ora ne compaiono altri due:

  • Servizio Fiscalizzazione, che si occuperà dei problemi legati alla fiscalità e alla comunicazione con i servizi esterni.
  • Strumento di rimborso, che contiene semplicemente nuovi scambi per non gonfiare il BOB.

Ora il processo è simile al seguente:

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

  1. BOB riceve una richiesta di rimborso.
  2. BOB parla di questo strumento di rimborso.
  3. Lo strumento di rimborso dice al pagamento: "Restituisci il denaro".
  4. Il pagamento restituisce il denaro.
  5. Refund Tool e BOB sincronizzano gli stati tra loro, perché per ora ne hanno bisogno entrambi. Non siamo ancora pronti per passare completamente allo strumento di rimborso, poiché BOB ha un'interfaccia utente, report per la contabilità e in generale molti dati che non possono essere trasferiti così facilmente. Devi sederti su due sedie.
  6. Scappa la richiesta di fiscalizzazione.

Di conseguenza, abbiamo realizzato una sorta di bus degli eventi su Kafka: bus degli eventi, su cui tutto è iniziato. Evviva, ora abbiamo un unico punto di fallimento (il sarcasmo).

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

I pro e i contro sono abbastanza ovvi. Abbiamo realizzato un autobus, il che significa che ora tutti i servizi dipendono da esso. Ciò semplifica la progettazione, ma introduce un singolo punto di guasto nel sistema. Kafka andrà in crash, il processo si fermerà.

Che cos'è un'API basata sugli eventi

Una buona risposta a questa domanda si trova nel rapporto di Martin Fowler (GOTO 2017) "I molteplici significati dell'architettura guidata dagli eventi".

Brevemente cosa abbiamo fatto:

  1. Concludi tutti gli scambi asincroni tramite memorizzazione degli eventi. Invece di informare ogni consumatore interessato sul cambiamento di stato attraverso la rete, scriviamo un evento sul cambiamento di stato in un archivio centralizzato e i consumatori interessati all'argomento leggono tutto ciò che appare da lì.
  2. L'evento in questo caso è una notifica (notifiche) che qualcosa è cambiato da qualche parte. Ad esempio, lo stato dell'ordine è cambiato. Un consumatore interessato ad alcuni dati che accompagnano il cambiamento di stato non inclusi nella notifica può scoprirne lo stato da solo.
  3. L'opzione massima è il sourcing di eventi a tutti gli effetti, trasferimento statale, nel qual caso contiene tutte le informazioni necessarie per l'elaborazione: da dove provengono e in quale stato sono andati, come sono cambiati esattamente i dati, ecc. L'unica domanda è la fattibilità e la quantità di informazioni che puoi permetterti di archiviare.

Nell'ambito del lancio dello strumento di rimborso, abbiamo utilizzato la terza opzione. Ciò ha semplificato l'elaborazione degli eventi poiché non era necessario estrarre informazioni dettagliate, inoltre ha eliminato lo scenario in cui ogni nuovo evento genera un'ondata di richieste di chiarimento da parte dei consumatori.

Servizio strumento di rimborso non caricato, quindi per Kafka c'è più un gusto della penna che una necessità. Non penso che se il servizio di rimborso diventasse un progetto ad alto carico, le imprese sarebbero contente.

Scambio asincrono COSÌ COM'È

Per gli scambi asincroni, il dipartimento PHP utilizza solitamente RabbitMQ. Abbiamo raccolto i dati per la richiesta, li abbiamo messi in coda e il consumatore dello stesso servizio li ha letti e li ha inviati (o non li ha inviati). Per l'API stessa, Lamoda utilizza attivamente Swagger. Progettiamo un'API, la descriviamo in Swagger e generiamo codice client e server. Usiamo anche un JSON RPC 2.0 leggermente migliorato.

In alcuni luoghi vengono utilizzati i bus ESB, alcuni vivono su activeMQ, ma, in generale, ConiglioMQ - standard.

Scambio asincrono TO BE

Quando si progetta lo scambio tramite bus di eventi, si può tracciare un'analogia. Allo stesso modo descriviamo il futuro scambio di dati attraverso la descrizione della struttura degli eventi. Il formato yaml, abbiamo dovuto generare noi stessi il codice, il generatore crea DTO secondo le specifiche e insegna a client e server a lavorare con essi. La generazione va in due lingue - golang e php. Ciò aiuta a mantenere le librerie coerenti. Il generatore è scritto in golang, motivo per cui ha preso il nome gogi.

L'event-sourcing su Kafka è una cosa tipica. Esiste una soluzione dalla versione aziendale principale di Kafka Confluent nakadi, una soluzione dei nostri fratelli di dominio Zalando. Nostro motivazione per iniziare con Vanilla Kafka - questo significa lasciare libera la soluzione fino a quando non decideremo se la utilizzeremo ovunque, e anche lasciarci spazio di manovra e di miglioramento: vogliamo supporto per i nostri JSONRPC 2.0, generatori per due lingue e vediamo cos'altro.

È ironico che anche in un caso così felice, quando esiste un’azienda più o meno simile, Zalando, che ha realizzato una soluzione più o meno simile, non possiamo usarla in modo efficace.

Lo schema architettonico al momento del lancio è il seguente: leggiamo direttamente da Kafka, ma scriviamo solo tramite events-bus. C'è molto da leggere in Kafka: broker, bilanciatori, ed è più o meno pronto per il ridimensionamento orizzontale, volevo preservarlo. Volevamo completare la registrazione attraverso un Gateway ovvero Events-bus, ed ecco perché.

Autobus-eventi

O un autobus per eventi. Questo è semplicemente un gateway http stateless, che assume diversi ruoli importanti:

  • Produrre validazione — controlliamo che gli eventi rispettino le nostre specifiche.
  • Sistema master degli eventi, cioè questo è il principale e unico sistema in azienda che risponde alla domanda su quali eventi con quali strutture sono considerate valide. La convalida coinvolge semplicemente tipi di dati ed enumerazioni per specificare rigorosamente il contenuto.
  • Funzione hash per lo sharding: la struttura del messaggio Kafka è valore-chiave e utilizzando l'hash della chiave viene calcolato dove inserirlo.

Perché

Lavoriamo in una grande azienda con un processo snello. Perché cambiare qualcosa? Questo è un esperimentoe prevediamo di ottenere numerosi vantaggi.

Scambi 1:n+1 (uno a molti)

Kafka semplifica la connessione di nuovi consumatori all'API.

Supponiamo che tu abbia una directory che devi mantenere aggiornata in più sistemi contemporaneamente (e in alcuni nuovi). In precedenza, abbiamo inventato un pacchetto che implementava set-API e il sistema principale veniva informato degli indirizzi dei consumatori. Ora il sistema principale invia aggiornamenti all'argomento e tutti coloro che sono interessati lo leggono. È apparso un nuovo sistema: lo abbiamo registrato per l'argomento. Sì, anche bundle, ma più semplice.

Nel caso dello strumento di rimborso, che è un pezzo di BOB, è conveniente per noi mantenerli sincronizzati tramite Kafka. Il pagamento dice che il denaro è stato restituito: BOB, RT lo hanno scoperto, hanno cambiato il loro stato, il Servizio di fiscalizzazione lo ha scoperto ed ha emesso un assegno.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Abbiamo in programma di creare un servizio di notifiche unificato che avviserà il cliente sulle novità riguardanti i suoi ordini/resi. Ora questa responsabilità è ripartita tra i sistemi. Ci basterà insegnare al Servizio Notifiche a catturare informazioni rilevanti da Kafka e a rispondere ad esse (e disabilitare queste notifiche in altri sistemi). Non saranno necessari nuovi scambi diretti.

Basato sui dati

Le informazioni tra i sistemi diventano trasparenti, indipendentemente dalla "dannata impresa" che hai e non importa quanto sia pesante il tuo arretrato. Lamoda dispone di un dipartimento di Data Analytics che raccoglie i dati dai sistemi e li inserisce in una forma riutilizzabile, sia per il business che per i sistemi intelligenti. Kafka ti consente di fornire loro rapidamente molti dati e mantenere aggiornato il flusso di informazioni.

Registro di replica

I messaggi non scompaiono dopo essere stati letti, come in RabbitMQ. Quando un evento contiene informazioni sufficienti per l'elaborazione, abbiamo una cronologia delle modifiche recenti all'oggetto e, se lo si desidera, la possibilità di applicare tali modifiche.

Il periodo di archiviazione del registro di replica dipende dall'intensità della scrittura su questo argomento; Kafka consente di impostare in modo flessibile limiti sul tempo di archiviazione e sul volume dei dati. Per argomenti intensivi, è importante che tutti i consumatori abbiano il tempo di leggere le informazioni prima che scompaiano, anche in caso di inoperabilità a breve termine. Di solito è possibile memorizzare i dati per unità di giorni, che è abbastanza per il supporto.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Successivamente, una piccola rivisitazione della documentazione, per coloro che non hanno familiarità con Kafka (anche l'immagine proviene dalla documentazione)

AMQP ha code: scriviamo messaggi in una coda per il consumatore. In genere, una coda viene elaborata da un sistema con la stessa logica aziendale. Se devi notificare più sistemi, puoi insegnare all'applicazione a scrivere su più code o configurare lo scambio con il meccanismo di fanout, che le clona da sola.

Kafka ha un'astrazione simile argomento, in cui scrivi messaggi, ma non scompaiono dopo la lettura. Per impostazione predefinita, quando ti connetti a Kafka, ricevi tutti i messaggi e hai la possibilità di salvare da dove eri rimasto. Cioè, leggi in sequenza, non puoi contrassegnare il messaggio come letto, ma salvare l'id da cui poi potrai continuare a leggere. L'ID che hai scelto si chiama offset e il meccanismo è commit offset.

Di conseguenza, è possibile implementare una logica diversa. Ad esempio, abbiamo BOB in 4 istanze per diversi paesi: Lamoda è in Russia, Kazakistan, Ucraina, Bielorussia. Poiché vengono distribuiti separatamente, hanno configurazioni leggermente diverse e una propria logica aziendale. Indichiamo nel messaggio a quale Paese si riferisce. Ogni consumatore BOB in ciascun paese legge con un groupId diverso e, se il messaggio non si applica a lui, lo salta, ad es. impegna immediatamente l'offset +1. Se lo stesso argomento viene letto dal nostro Servizio di Pagamento, lo fa con un gruppo separato e quindi gli offset non si intersecano.

Requisiti dell'evento:

  • Completezza dei dati. Vorrei che l'evento avesse dati sufficienti per poterlo elaborare.

  • Integrità. Deleghiamo ad Events-bus la verifica che l'evento sia coerente e possa elaborarlo.
  • L'ordine è importante. In caso di ritorno siamo costretti a lavorare con la storia. Con le notifiche l'ordine non è importante, se sono notifiche omogenee la mail sarà la stessa indipendentemente da quale ordine sia arrivato prima. Nel caso di un rimborso, c'è un processo chiaro; se modifichiamo l'ordine, sorgeranno delle eccezioni, il rimborso non verrà creato o elaborato - ci ritroveremo in uno stato diverso.
  • Consistenza. Abbiamo un negozio e ora creiamo eventi invece di un'API. Abbiamo bisogno di un modo per trasmettere ai nostri servizi informazioni in modo rapido ed economico su nuovi eventi e modifiche a quelli esistenti. Ciò si ottiene attraverso una specifica comune in un repository git separato e generatori di codice. Pertanto, client e server in diversi servizi sono coordinati.

Kafka in Lamoda

Abbiamo tre installazioni Kafka:

  1. Registri;
  2. Ricerca e sviluppo;
  3. Autobus-eventi.

Oggi parliamo solo dell’ultimo punto. Su events-bus non abbiamo installazioni molto grandi: 3 broker (server) e solo 27 argomenti. Di norma, un argomento è un processo. Ma questo è un punto delicato e lo toccheremo ora.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Sopra è riportato il grafico rps. Il processo di rimborso è contrassegnato da una linea turchese (sì, quella sull'asse X) e la linea rosa è il processo di aggiornamento del contenuto.

Il catalogo Lamoda contiene milioni di prodotti e i dati sono continuamente aggiornati. Alcune collezioni passano di moda, ne vengono lanciate di nuove per sostituirle e nel catalogo compaiono costantemente nuovi modelli. Cerchiamo di prevedere cosa sarà interessante per i nostri clienti domani, quindi acquistiamo costantemente cose nuove, le fotografiamo e aggiorniamo la vetrina.

I picchi rosa sono aggiornamenti di prodotto, ovvero modifiche ai prodotti. Si può vedere che i ragazzi hanno scattato foto, scattato foto e poi ancora! - caricato un pacchetto di eventi.

Casi d'uso di Lamoda Events

Utilizziamo l'architettura costruita per le seguenti operazioni:

  • Monitoraggio dello stato del reso: call-to-action e tracciamento dello stato da tutti i sistemi coinvolti. Pagamento, stati, fiscalizzazione, notifiche. Qui abbiamo testato l'approccio, creato strumenti, raccolto tutti i bug, scritto la documentazione e spiegato ai nostri colleghi come usarla.
  • Aggiornamento delle schede prodotto: configurazione, metadati, caratteristiche. Un sistema legge (che visualizza) e diversi scrivono.
  • E-mail, push e sms: l'ordine è stato ritirato, l'ordine è arrivato, il reso è stato accettato, ecc., ce ne sono moltissimi.
  • Stock, rinnovamento del magazzino — aggiornamento quantitativo degli articoli, solo numeri: arrivo al magazzino, reso. È necessario che tutti i sistemi associati alla prenotazione delle merci funzionino con i dati più aggiornati. Attualmente il sistema di aggiornamento delle scorte è piuttosto complesso; Kafka lo semplificherà.
  • Analisi dei dati (dipartimento R&D), strumenti ML, analisi, statistiche. Vogliamo che l'informazione sia trasparente: Kafka è adatto a questo.

Ora arriva la parte più interessante, riguardante i grandi scossoni e le interessanti scoperte avvenute negli ultimi sei mesi.

Problemi di progettazione

Diciamo che vogliamo fare una cosa nuova, ad esempio trasferire l'intero processo di consegna a Kafka. Ora parte del processo è implementato nell'elaborazione degli ordini in BOB. Esiste un modello di stato dietro il trasferimento di un ordine al servizio di consegna, lo spostamento in un magazzino intermedio e così via. C'è un intero monolite, anche due, oltre a un mucchio di API dedicate alla consegna. Sanno molto di più sulla consegna.

Sembrano aree simili, ma l'elaborazione degli ordini in BOB e il sistema di spedizione hanno stati diversi. Ad esempio, alcuni servizi di corriere non inviano stati intermedi, ma solo quelli finali: “consegnato” o “smarrito”. Altri, al contrario, riferiscono dettagliatamente sulla circolazione delle merci. Ognuno ha le proprie regole di convalida: per alcuni l'e-mail è valida, il che significa che verrà elaborata; per altri non è valido, ma l'ordine verrà comunque evaso perché c'è un numero di telefono per essere contattati, e qualcuno dirà che tale ordine non verrà affatto evaso.

Flusso di dati

Nel caso di Kafka si pone la questione dell'organizzazione del flusso di dati. Questo compito prevede la scelta di una strategia basata su diversi punti; esaminiamoli tutti.

In un argomento o in diversi?

Abbiamo una specifica dell'evento. In BOB scriviamo che questo o quell'ordine deve essere consegnato e indichiamo: il numero dell'ordine, la sua composizione, alcuni SKU e codici a barre, ecc. Quando la merce arriverà al magazzino, la consegna potrà ricevere stati, timestamp e tutto ciò che è necessario. Ma poi vogliamo ricevere aggiornamenti su questi dati in BOB. Abbiamo un processo inverso di ricezione dei dati dalla consegna. È lo stesso evento? Oppure si tratta di uno scambio separato che merita un argomento a parte?

Molto probabilmente, saranno molto simili e la tentazione di creare un argomento non è infondata, perché un argomento separato significa consumatori separati, configurazioni separate, una generazione separata di tutto questo. Ma non è un dato di fatto.

Nuovo campo o nuovo evento?

Ma se usi gli stessi eventi, sorge un altro problema. Ad esempio, non tutti i sistemi di consegna possono generare il tipo di DTO che può generare BOB. Inviamo loro l'ID, ma non lo salvano perché non ne hanno bisogno e dal punto di vista dell'avvio del processo del bus di eventi questo campo è obbligatorio.

Se introduciamo una regola per il bus di eventi secondo cui questo campo è obbligatorio, allora siamo costretti a impostare regole di convalida aggiuntive nel BOB o nel gestore dell'evento di avvio. La convalida inizia a diffondersi in tutto il servizio: questo non è molto conveniente.

Un altro problema è la tentazione dello sviluppo incrementale. Ci viene detto che bisogna aggiungere qualcosa all'evento e forse, se ci pensiamo bene, avrebbe dovuto essere un evento a parte. Ma nel nostro schema, un evento separato è un argomento separato. Un argomento a parte è l'intero processo che ho descritto sopra. Lo sviluppatore è tentato di aggiungere semplicemente un altro campo allo schema JSON e rigenerarlo.

Nel caso dei rimborsi siamo arrivati ​​alla realtà degli eventi in sei mesi. Abbiamo avuto un meta-evento chiamato aggiornamento rimborsi, che aveva un campo tipo che descriveva in cosa consisteva effettivamente questo aggiornamento. Per questo motivo, abbiamo avuto "meravigliosi" scambi con validatori che ci hanno spiegato come convalidare questo evento con questo tipo.

Versionamento degli eventi

Per convalidare i messaggi in Kafka puoi usare Avro, ma è stato necessario adagiarci subito ed utilizzare Confluent. Nel nostro caso, dobbiamo stare attenti con il controllo delle versioni. Non sarà sempre possibile rileggere i messaggi dal log di replica perché il modello è “abbandonato”. Fondamentalmente, si tratta di creare versioni in modo che il modello sia retrocompatibile: ad esempio, rendere un campo temporaneamente facoltativo. Se le differenze sono troppo forti, iniziamo a scrivere in un nuovo argomento e trasferiamo i clienti quando finiscono di leggere quello vecchio.

Ordine di lettura garantito delle partizioni

Gli argomenti all'interno di Kafka sono divisi in partizioni. Questo non è molto importante mentre progettiamo entità e scambi, ma è importante quando decidiamo come consumarli e ridimensionarli.

Nel solito caso, scrivi un argomento in Kafka. Per impostazione predefinita, viene utilizzata una partizione e tutti i messaggi in questo argomento vengono indirizzati ad essa. E il consumatore di conseguenza legge questi messaggi in sequenza. Diciamo che ora dobbiamo espandere il sistema in modo che i messaggi vengano letti da due consumatori diversi. Se, ad esempio, stai inviando SMS, puoi dire a Kafka di creare una partizione aggiuntiva e Kafka inizierà a dividere i messaggi in due parti: metà qui e metà qui.

Come li divide Kafka? Ogni messaggio ha un corpo (in cui memorizziamo JSON) e una chiave. È possibile allegare una funzione hash a questa chiave, che determinerà in quale partizione verrà inserito il messaggio.

Nel nostro caso con i rimborsi, questo è importante, se prendiamo due partizioni, allora c'è la possibilità che il consumatore parallelo elabori il secondo evento prima del primo e ci saranno problemi. La funzione hash garantisce che i messaggi con la stessa chiave finiscano nella stessa partizione.

Eventi vs comandi

Questo è un altro problema che abbiamo riscontrato. Evento è un determinato evento: diciamo che è successo qualcosa da qualche parte (qualcosa_è successo), ad esempio, un articolo è stato annullato o è avvenuto un rimborso. Se qualcuno ascolta questi eventi, in base a "articolo annullato", verrà creata l'entità di rimborso e "rimborso avvenuto" verrà scritto da qualche parte nelle impostazioni.

Ma di solito, quando progetti eventi, non vuoi scriverli invano: fai affidamento sul fatto che qualcuno li leggerà. C'è una forte tentazione di scrivere non qualcosa_che_è_successo (oggetto_cancellato, rimborso_rimborsato), ma qualcosa_dovrebbe_essere_fatto. Ad esempio, l'articolo è pronto per essere restituito.

Da un lato suggerisce come verrà utilizzato l’evento. D'altra parte, suona molto meno come il normale nome di un evento. Inoltre, non è lontano da qui al comando fai_qualcosa. Ma non hai alcuna garanzia che qualcuno legga questo evento; e se lo leggi, lo leggi con successo; e se lo leggi con successo, allora hai fatto qualcosa, e quel qualcosa ha avuto successo. Nel momento in cui un evento diventa do_something, il feedback diventa necessario, e questo è un problema.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Nello scambio asincrono in RabbitMQ, quando leggi il messaggio, vai su http, hai una risposta, almeno che il messaggio è stato ricevuto. Quando scrivi a Kafka, c'è un messaggio che hai scritto a Kafka, ma non sai nulla di come è stato elaborato.

Pertanto, nel nostro caso, abbiamo dovuto introdurre un evento di risposta e impostare il monitoraggio in modo che se fossero stati inviati così tanti eventi, dopo tale tempo dovrebbe arrivare lo stesso numero di eventi di risposta. Se ciò non accade, allora sembra che qualcosa sia andato storto. Ad esempio, se inviamo l'evento "item_ready_to_refund", prevediamo che verrà creato un rimborso, il denaro verrà restituito al cliente e ci verrà inviato l'evento "money_refunded". Ma questo non è certo, quindi è necessario un monitoraggio.

sfumature

C'è un problema abbastanza ovvio: se leggi un argomento in sequenza e ricevi qualche messaggio errato, il consumatore cadrà e non andrai oltre. Hai bisogno fermare tutti i consumatori, conferma ulteriormente l'offset per continuare a leggere.

Lo sapevamo, ci contavamo, eppure è successo. E questo è accaduto perché l'evento era valido dal punto di vista del bus degli eventi, l'evento era valido dal punto di vista del validatore dell'applicazione, ma non era valido dal punto di vista di PostgreSQL, perché nel nostro sistema MySQL con UNSIGNED INT il sistema aveva PostgreSQL solo con INT. La sua taglia è un po' più piccola e l'ID non si adattava. Symfony è morto con un'eccezione. Ovviamente abbiamo rilevato l'eccezione perché facevamo affidamento su di essa e volevamo impegnare questo offset, ma prima volevamo incrementare il contatore dei problemi, poiché il messaggio è stato elaborato senza successo. Anche i contatori in questo progetto sono nel database e Symfony ha già chiuso la comunicazione con il database e la seconda eccezione ha interrotto l'intero processo senza la possibilità di eseguire il commit dell'offset.

Il servizio si è fermato per un po' - fortunatamente con Kafka non è poi così male, perché i messaggi rimangono. Una volta ripristinato il lavoro, puoi finire di leggerli. È comodo.

Kafka ha la capacità di impostare un offset arbitrario tramite gli strumenti. Ma per fare ciò, è necessario fermare tutti i consumatori: nel nostro caso, preparare un rilascio separato in cui non ci saranno consumatori o ridistribuzioni. Quindi in Kafka puoi spostare l'offset tramite gli strumenti e il messaggio verrà trasmesso.

Un'altra sfumatura - registro di replica vs rdkafka.so - è legato alle specificità del nostro progetto. Usiamo PHP e in PHP, di regola, tutte le librerie comunicano con Kafka attraverso il repository rdkafka.so, e quindi c'è una sorta di wrapper. Forse queste sono le nostre difficoltà personali, ma si è scoperto che rileggere semplicemente un pezzo di ciò che avevamo già letto non è così facile. In generale, c'erano problemi software.

Tornando alle specifiche del lavoro con le partizioni, è scritto direttamente nella documentazione consumatori >= partizioni degli argomenti. Ma l'ho scoperto molto più tardi di quanto avrei voluto. Se desideri scalare e avere due consumatori, sono necessarie almeno due partizioni. Cioè, se avessi una partizione in cui si sono accumulati 20mila messaggi e ne hai creata una nuova, il numero di messaggi non verrà presto equalizzato. Pertanto, per avere due consumatori paralleli, è necessario gestire le partizioni.

Monitoraggio

Penso che il modo in cui monitoreremo sarà ancora più chiaro quali problemi ci siano nell’approccio esistente.

Ad esempio, calcoliamo quanti prodotti nel database hanno recentemente cambiato il loro stato e, di conseguenza, gli eventi dovrebbero essersi verificati in base a questi cambiamenti e inviamo questo numero al nostro sistema di monitoraggio. Quindi da Kafka otteniamo il secondo numero, quanti eventi sono stati effettivamente registrati. Ovviamente, la differenza tra questi due numeri dovrebbe essere sempre zero.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Inoltre, è necessario monitorare come sta andando il produttore, se il bus degli eventi ha ricevuto messaggi e come sta andando il consumatore. Ad esempio, nei grafici seguenti, Refund Tool sta andando bene, ma BOB ha chiaramente alcuni problemi (picchi blu).

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Ho già menzionato il ritardo del gruppo di consumatori. In parole povere, questo è il numero di messaggi non letti. In generale, i nostri consumatori lavorano velocemente, quindi il ritardo è solitamente pari a 0, ma a volte può esserci un picco a breve termine. Kafka può farlo immediatamente, ma è necessario impostare un certo intervallo.

C'è un progetto Tanache ti darà maggiori informazioni su Kafka. Utilizza semplicemente l'API del gruppo di consumatori per fornire lo stato di come sta andando questo gruppo. Oltre a OK e Fallito, c'è un avviso e puoi scoprire che i tuoi consumatori non riescono a far fronte al ritmo di produzione: non hanno tempo per correggere ciò che è scritto. Il sistema è abbastanza intelligente e facile da usare.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Ecco come appare la risposta dell'API. Ecco il gruppo bob-live-fifa, partizione rimborso.update.v1, stato OK, ritardo 0 - l'ultimo offset finale così e così.

Esperienza nello sviluppo del servizio Refund Tool con API asincrona su Kafka

Monitoraggio aggiornato_at SLA (bloccato) Ho già menzionato. Ad esempio, il prodotto è passato allo stato pronto per la restituzione. Installiamo Cron, che dice che se in 5 minuti questo oggetto non è andato in rimborso (restituiamo il denaro tramite i sistemi di pagamento molto rapidamente), allora qualcosa è sicuramente andato storto, e questo è sicuramente un caso di supporto. Pertanto, prendiamo semplicemente Cron, che legge queste cose e, se sono maggiori di 0, invia un avviso.

Per riassumere, usare gli eventi è conveniente quando:

  • le informazioni sono necessarie a diversi sistemi;
  • il risultato della lavorazione non è importante;
  • ci sono pochi eventi o piccoli eventi.

Sembrerebbe che l'articolo abbia un argomento molto specifico: l'API asincrona su Kafka, ma in relazione ad esso vorrei consigliare molte cose contemporaneamente.
Prima, dopo Gran Carico ++ bisognerà aspettare novembre, ad aprile ci sarà la versione a San Pietroburgo, a giugno si parlerà di carichi elevati a Novosibirsk.
In secondo luogo, l’autore del rapporto, Sergei Zaika, è membro del comitato di programma della nostra nuova conferenza sulla gestione della conoscenza ConoscenzaConf. La conferenza durerà un giorno, si svolgerà il 26 aprile, ma il suo programma è molto intenso.
E sarà a maggio PHPRussia и RIT++ (con DevOpsConf incluso) - puoi anche suggerire il tuo argomento lì, parlare della tua esperienza e lamentarti dei tuoi coni ripieni.

Fonte: habr.com

Aggiungi un commento