Qual è il modo corretto di sincronizzare i dati tra i microservizi?


19

Sono relativamente nuovo nell'architettura dei microservizi. Abbiamo un'applicazione Web di dimensioni moderate e sto valutando i pro ei contro di scomporla in microservizi anziché in un sistema monolitico che ora stiamo andando avanti.

Per quanto ho capito, considera i microservizi Ae Bognuno dei quali si basa su un sottoinsieme di dati che l'altro ha. Se un messaggio viene pubblicato Adicendo che qualcosa è cambiato, Bpuò consumare quel messaggio e replicare una copia locale delle Ainformazioni e usarla per fare tutto ciò che Bdeve fare.

Tuttavia, cosa Bsuccede se scende / fallisce e dopo un po ', torna di nuovo su. Durante quel periodo di inattività, Aha pubblicato altri due messaggi. Come fa Bad aggiornare la sua copia locale delle Ainformazioni?

Certo, se Bè l'unico consumatore della Acoda, allora può iniziare a leggerlo una volta tornato online ma cosa succede se ci sono altri consumatori di quella coda e quei messaggi vengono consumati?

Come esempio più concreto, se un Usersservizio ha il suo indirizzo e-mail aggiornato mentre un Billingmicroservizio è inattivo, se il Billingmicroservizio torna di nuovo, come fa a sapere che l'e-mail è stata aggiornata?

Quando i microservizi ritornano, fa una trasmissione che dice "Hey sono tornato, dammi tutte le tue informazioni attuali?"

In generale, quali sarebbero le migliori pratiche del settore per la sincronizzazione dei dati?


1
Per evitarlo quando possibile.
Telastyn,

1
Perché ha Ordersbisogno di sapere qualcosa Users?
kdgregory,

È solo un esempio. Sostituisci i due con quello che vuoi che abbia senso.
noblerare,

un fan out routing risolverà il tuo problema "il messaggio è consumato da qualcun altro". ma non è davvero chiaro cosa stai cercando di ottenere.
Ewan,

@Ewan Ho aggiornato il mio post originale per spiegare meglio cosa sto cercando di chiedere.
noblerare,

Risposte:


5

Sfiderei tutta la tua idea di "trasferire i dati a tutti gli altri microservizi".

Di solito, se un servizio di fatturazione necessita di un indirizzo e-mail, richiede semplicemente al servizio di indirizzo l'indirizzo e-mail del cliente specifico. Non è necessario conservare una copia di tutti i dati dell'indirizzo né verrà informato se qualcosa cambia. Chiede e ottiene la risposta dai dati più recenti.


Penso che questa risposta sia esattamente giusta. Elimina molti problemi legati alla sincronizzazione. In effetti, sto esaminando il codice in questo momento che presenta tali problemi perché diversi servizi conservano copie delle informazioni e hanno problemi di sincronizzazione.
DaveG,

2
Grazie per la tua risposta. Allora perché allora c'è bisogno di un pub / modello secondario e code di messaggi? Se stiamo cercando di "estrarre" invece di "spingere" i dati, siamo preoccupati per la latenza del servizio.
noblerare,

AFAIK, il tuo servizio non ha bisogno di reagire immediatamente se qualcosa cambia (come in un pub / sub), ma a volte necessita di dati. Quindi lo tirerei semplicemente. Se ti preoccupi della latenza, puoi memorizzare nella cache i dati, ma ciò comporta nuovamente il costo di non sapere se i dati sono aggiornati. Se i tuoi file sono di grandi dimensioni, puoi anche chiedere se qualcosa cambia prima di estrarre di nuovo qualcosa.
J. Fabian Meier,

Tieni presente che questa soluzione ha un costo di accoppiamento stretto del servizio dipendente, il che significa che l'indirizzo e-mail non sarà disponibile quando il servizio utente non è disponibile. Una delle idee iniziali di rottura dei servizi per cominciare in modo che siano distribuibili indipendentemente, scalabili, ecc. Se tutti i servizi comunicano direttamente tra loro senza una cache o una garanzia di alta disponibilità, allora quando un sistema è inattivo, tutti scendere.
dukethrash,

@dukethrash Quindi renderli altamente disponibili.
J. Fabian Meier,

5

Dopo aver fatto un po 'più di ricerca, mi sono imbattuto in questo articolo da cui ho tirato fuori alcune citazioni che penso siano utili per ciò che voglio realizzare (e per eventuali lettori futuri). Ciò offre un modo per adottare un modello di programmazione reattiva rispetto a un modello di programmazione imperativo.

Event-sourcing

L'idea qui è quella di rappresentare la transizione di stato di ogni applicazione in una forma di evento immutabile. Gli eventi vengono quindi archiviati in un registro o in un modulo journal quando si verificano (noto anche come "archivio eventi"). Possono anche essere interrogati e archiviati indefinitamente, con l'obiettivo di rappresentare come lo stato dell'applicazione, nel suo insieme, si è evoluto nel tempo.

Ciò che aiuta a realizzare è che se un microservizio scende, ma altri eventi pertinenti ad esso vengono pubblicati e gli eventi vengono consumati, ad esempio, da altre istanze di quel microservizio, quando il microservizio viene ripristinato, può fare riferimento a questo event storeper recuperare tutto il eventi che ha perso durante il periodo in cui è andato giù.

Apache Kafka come Event Broker

Considera l'uso di Apache Kafka che può archiviare e inviare migliaia di eventi al secondo e ha meccanismi di replica e tolleranza d'errore integrati. Ha un archivio persistente di eventi che possono essere memorizzati su disco indefinitamente e consumati in qualsiasi momento (ma non rimossi) dall'argomento (la coda elaborata di Kafka) a cui sono stati consegnati.

Agli eventi vengono quindi assegnati offset che li identificano in modo univoco all'interno dell'argomento: Kafka può gestire gli offset stessi, fornendo facilmente semantiche di consegna "al massimo una volta" o "almeno una volta", ma possono anche essere negoziate quando un consumatore di eventi si unisce a un argomento , consentendo ai microservizi di iniziare a consumare eventi da qualsiasi luogo arbitrario nel tempo, di solito da dove il consumatore aveva interrotto. Se l'ultimo offset dell'evento consumato è persistentemente transazionale nella memoria locale dei servizi quando le istruzioni vengono "completate correttamente", tale offset può essere facilmente utilizzato per ottenere una semantica di consegna dell'evento "esattamente una volta".

Infatti, quando i consumatori si identificano in Kafka, Kafka registra quali messaggi sono stati recapitati a quale consumatore in modo che non vengano più pubblicati.

Sagas

Per casi d'uso più complessi in cui la comunicazione tra diversi servizi è effettivamente necessaria, la responsabilità di completare il caso d'uso deve essere ben riconosciuta - il caso d'uso è decentralizzato e termina solo quando tutti i servizi coinvolti riconoscono il loro compito come completato con successo, altrimenti l'intero caso d'uso deve fallire e misure correttive devono essere attivate per ripristinare qualsiasi stato locale non valido.

Questo è quando la saga entra in gioco. Una saga è una sequenza di transazioni locali. Ogni transazione locale aggiorna il database e pubblica un messaggio o un evento per attivare la successiva transazione locale nella saga. Se una transazione locale fallisce perché viola una regola aziendale, la saga esegue una serie di transazioni compensative che annullano le modifiche apportate dalle precedenti transazioni locali. Leggi questo per maggiori informazioni.


Ancora non capisco perché vuoi costruire una struttura così complicata. Di solito è molto più semplice se ogni servizio contiene solo i propri dati e li fornisce ad altri servizi su richiesta.
J. Fabian Meier,

^ Ma ridurrà la disponibilità del sistema. La struttura complicata potrebbe essere giustificata se è richiesta un'elevata resilienza.
avmohan,

1

Anche se sono in ritardo, metterei i miei 2 centesimi sull'argomento perché penso che sia un punto importante quando si desidera valutare e progettare un'architettura di microservizi basata sugli eventi. Ogni microservizio sa esattamente quali sono gli eventi che incidono sul suo stato ed è in grado di aspettarli. Quando il microservizio non è disponibile, dovrebbe esserci un componente che conserva i messaggi necessari dal microservizio fallito fino a quando non è in grado di "consumarli". Si tratta in realtà di un modello "produttore / consumatore" e non di "pubblicazione / sottoscrizione". I broker di messaggi (come Kafka, RabbitMQ, ActiveMQ, ecc.) Sono in genere il modo migliore per raggiungere questo comportamento (a meno che non si stia implementando qualcosa di diverso come l'approvvigionamento di eventi) fornendo code persistenti e meccanismo ack / nack.

Ora il microservizio sa che alla fine un messaggio viene recapitato ma non è abbastanza: qual è il modo in cui prevede la consegna di un singolo messaggio? può gestire la consegna di più copie della stessa notifica di evento? Questa è una questione di consegna semantica (almeno una volta, esattamente una volta)

Pensieri finali):

  1. Quando aggiungi un microservizio alla tua architettura che deve consumare eventi da altri, devi eseguire la prima sincronizzazione

  2. Anche il broker può fallire, in questo caso i messaggi vanno persi

per entrambi gli scenari, sarebbe utile disporre di semplici meccanismi per reidratare lo stato del microservizio. Potrebbe essere un'API REST o uno script che invia messaggi, ma la cosa più importante è disporre di mezzi per eseguire alcune attività di manutenzione


0

È possibile sostituire una normale coda di eventi con un modello editore / abbonato, in cui il Aservizio pubblica un nuovo messaggio dell'argomento T e il Btipo di microservizi si abbonerebbe allo stesso argomento.

Idealmente Bsarebbe un servizio senza stato e utilizzerebbe un servizio di persistenza distaccato, in modo tale che Bun'istanza di servizio non riuscita venga sostituita generando una o più Bistanze di servizio per continuare il suo lavoro, leggendo dallo stesso servizio di persistenza condiviso.


0

Se un messaggio viene pubblicato da A dicendo che qualcosa è cambiato, B può consumare quel messaggio e replicare una copia locale delle informazioni di A e usarla per fare tutto ciò che B deve fare.

Se desideri che B sia in grado di accedere ai dati interni di A, sarebbe meglio dargli accesso ai database interni di A.

Tuttavia, non dovresti farlo, il punto centrale di un'architettura orientata al servizio è che il servizio B non può vedere lo stato interno del servizio A ed è limitato a fare richieste tramite le API REST (e viceversa).

Nel tuo caso potresti avere un servizio dati utente, che ha la responsabilità di archiviare tutti i dati utente. Altri servizi che desiderano utilizzare tali dati li richiedono solo quando ne hanno bisogno e non conservano una copia locale (che tra l'altro è davvero utile se si pensa alla conformità al GDPR). Il servizio Dati utente può supportare semplici operazioni CRUD come "Crea nuovo utente" o "Cambia nome per user_id 23" oppure può avere operazioni più complesse "Trova tutti gli utenti standard con un compleanno in arrivo nelle prossime 2 settimane e fornisci loro stato di prova premium ". Ora, quando il tuo servizio di fatturazione deve inviare una e-mail all'utente 42, chiederà al servizio dati utente "Qual è l'indirizzo e-mail di user_id 42", usa i suoi dati interni con tutte le informazioni di fatturazione per creare l'e-mail e quindi può passare il indirizzo e-mail e corpo di un server di posta.

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.