Transazioni tra i microservizi REST?


195

Diciamo che abbiamo un utente, microservizi REST di Wallet e un gateway API che incolla le cose insieme. Quando Bob si registra sul nostro sito Web, il nostro gateway API deve creare un utente tramite il microservizio Utente e un portafoglio tramite il microservizio Portafoglio.

Ora ecco alcuni scenari in cui le cose potrebbero andare storte:

  • La creazione dell'utente Bob non riesce: va bene, restituiamo un messaggio di errore a Bob. Stiamo usando le transazioni SQL, quindi nessuno ha mai visto Bob nel sistema. Va tutto bene :)

  • Viene creato l'utente Bob ma prima che il nostro portafoglio possa essere creato, il nostro gateway API si arresta in modo anomalo. Ora abbiamo un utente senza portafoglio (dati incoerenti).

  • Viene creato l'utente Bob e mentre stiamo creando il portafoglio, la connessione HTTP si interrompe. La creazione del portafoglio potrebbe essere riuscita o potrebbe non esserlo.

Quali soluzioni sono disponibili per prevenire questo tipo di incoerenza dei dati? Esistono modelli che consentono alle transazioni di estendere più richieste REST? Ho letto la pagina di Wikipedia sul commit in due fasi che sembra toccare questo problema, ma non sono sicuro di come applicarlo in pratica. Questo Atomic Distributed Transactions: un documento di design RESTful sembra interessante anche se non l'ho ancora letto.

In alternativa, so che REST potrebbe non essere adatto a questo caso d'uso. Forse sarebbe il modo corretto di gestire questa situazione per eliminare completamente REST e utilizzare un protocollo di comunicazione diverso come un sistema di coda messaggi? O dovrei applicare coerenza nel mio codice dell'applicazione (ad esempio, avendo un lavoro in background che rileva incoerenze e le corregge o avendo un attributo "stato" sul mio modello utente con valori "creazione", "creato", ecc.)?



3
Se un utente non ha senso senza un portafoglio, perché creare un microservizio separato per questo? Potrebbe essere qualcosa che non va bene con l'architettura in primo luogo? Perché hai bisogno di un gateway API generico, tra l'altro? C'è qualche motivo specifico per questo?
Vladislav Rastrusny,

4
@VladislavRastrusny è stato un esempio immaginario, ma potresti pensare al servizio di portafoglio come gestito da Stripe per esempio.
Olivier Lalonde,

È possibile utilizzare un gestore processi per tenere traccia della transazione (modello del gestore processi) o fare in modo che ciascun microservizio sappia come attivare un rollback (modello gestore saga) o eseguire una sorta di commit in due fasi ( blog.aspiresys.com/software-product-engineering / producteering / ... )
andrew pate,

@VladislavRastrusny "Se un utente non ha senso senza un portafoglio, perché creare un microservizio separato per esso" - ad esempio, a parte il fatto che un utente non può esistere senza un portafoglio, non ha alcun codice in comune. Pertanto, due team svilupperanno e distribuiranno i microservizi utente e portafoglio in modo indipendente. Non è il punto principale di fare microservizi in primo luogo?
Nik

Risposte:


148

Cosa non ha senso:

  • transazioni distribuite con servizi REST . I servizi REST per definizione sono apolidi, quindi non dovrebbero essere partecipanti a un confine transazionale che si estende su più di un servizio. Lo scenario del caso d'uso della registrazione utente ha senso, ma la progettazione con microservizi REST per creare dati utente e portafoglio non è buona.

Cosa ti darà mal di testa:

  • EJB con transazioni distribuite . È una di quelle cose che funzionano in teoria ma non in pratica. In questo momento sto cercando di far funzionare una transazione distribuita per EJB remoti attraverso istanze di JBoss EAP 6.3. Abbiamo parlato con il supporto RedHat per settimane e non ha ancora funzionato.
  • Soluzioni di commit a due fasi in generale . Penso che il protocollo 2PC sia un ottimo algoritmo (molti anni fa l'ho implementato in C con RPC). Richiede meccanismi completi di recupero da errori, con tentativi, repository di stato, ecc. Tutta la complessità è nascosta all'interno del framework delle transazioni (es .: JBoss Arjuna). Tuttavia, 2PC non è a prova di errore. Ci sono situazioni che la transazione semplicemente non può completare. Quindi è necessario identificare e correggere manualmente le incoerenze del database. Se sei fortunato, può succedere una volta su un milione di transazioni, ma può accadere una volta ogni 100 transazioni a seconda della piattaforma e dello scenario.
  • Saghe (Transazioni compensative) . C'è il sovraccarico dell'implementazione della creazione delle operazioni di compensazione e il meccanismo di coordinamento per attivare la compensazione alla fine. Ma la compensazione non è neanche una prova. Potresti comunque finire con incoerenze (= qualche mal di testa).

Qual è probabilmente la migliore alternativa:

  • Consistenza finale . Né le transazioni distribuite simili all'ACID né le transazioni compensative sono a prova di fallimento ed entrambe possono portare a incongruenze. La consistenza finale è spesso migliore di "incoerenza occasionale". Esistono diverse soluzioni di design, come ad esempio:
    • È possibile creare una soluzione più solida utilizzando la comunicazione asincrona. Nel tuo scenario, quando Bob si registra, il gateway API potrebbe inviare un messaggio a una coda NewUser e rispondere immediatamente all'utente dicendo "Riceverai un'email per confermare la creazione dell'account". Un servizio clienti in coda può elaborare il messaggio, eseguire le modifiche al database in un'unica transazione e inviare l'e-mail a Bob per notificare la creazione dell'account.
    • Il microservizio utente crea il record utente e un record di portafoglio nello stesso database . In questo caso, l'archivio portafoglio nel microservizio utente è una replica dell'archivio portafoglio principale visibile solo al microservizio portafoglio. Esiste un meccanismo di sincronizzazione dei dati basato su trigger o che si avvia periodicamente per inviare modifiche ai dati (ad es. Nuovi portafogli) dalla replica al master e viceversa.

E se aveste bisogno di risposte sincrone?

  • Rimodellare i microservizi . Se la soluzione con la coda non funziona perché il consumatore del servizio ha bisogno di una risposta immediata, preferirei rimodellare la funzionalità Utente e Portafoglio da collocare nello stesso servizio (o almeno nella stessa VM per evitare transazioni distribuite ). Sì, è un passo più avanti rispetto ai microservizi e più vicino a un monolite, ma ti salverà da qualche mal di testa.

4
Eventuale coerenza ha funzionato per me. In questo caso, la coda "NewUser" dovrebbe essere disponibile e resiliente.
Ram Bavireddi,

@RamBavireddi Kafka o RabbitMQ supportano le code resilienti?
v

@ v.oddou Sì, lo fanno.
Ram Bavireddi,

2
@PauloMerson Non sono sicuro di come differenzi le transazioni compensative con l'eventuale coerenza. E se, nella tua eventuale coerenza, la creazione del portafoglio fallisse?
balsick,

2
@balsick Una delle sfide di eventuali impostazioni di coerenza è la maggiore complessità del design. Sono spesso necessari controlli di coerenza ed eventi di correzione. Il design della soluzione varia. Nella risposta, suggerisco la situazione in cui il record Wallet viene creato nel database durante l'elaborazione di un messaggio inviato tramite un broker di messaggi. In questo caso, potremmo impostare un Dead Letter Channel, ovvero, se l'elaborazione di quel messaggio genera un errore, possiamo inviare il messaggio a una coda di messaggi non inviati e informare il team responsabile di "Wallet".
Paulo Merson,

66

Questa è una domanda classica che mi è stata posta recentemente durante un'intervista. Come chiamare più servizi Web e conservare comunque una sorta di gestione degli errori nel mezzo dell'attività. Oggi, nel calcolo ad alte prestazioni, evitiamo impegni in due fasi. Ho letto un documento molti anni fa su quello che era chiamato il "modello Starbuck" per le transazioni: pensa al processo di ordinazione, pagamento, preparazione e ricezione del caffè che ordini a Starbuck ... Semplifico troppo le cose ma un modello di commit in due fasi avrebbe suggerire che l'intero processo sarebbe una singola transazione di avvolgimento per tutte le fasi coinvolte fino a quando non si riceve il caffè. Tuttavia, con questo modello, tutti i dipendenti aspetterebbero e smetterebbero di lavorare fino a quando non si ottiene il caffè. Vedi la foto?

Invece, il "modello Starbuck" è più produttivo seguendo il modello "best effort" e compensando gli errori nel processo. Innanzitutto, si assicurano che tu paghi! Quindi, ci sono code di messaggi con il tuo ordine allegato alla tazza. Se qualcosa va storto nel processo, come se non avessi preso il tuo caffè, non è quello che hai ordinato, ecc., Entriamo nel processo di compensazione e ci assicuriamo che tu ottenga ciò che desideri o ti rimborsi, questo è il modello più efficiente per una maggiore produttività.

A volte, Starbuck sta sprecando un caffè, ma l'intero processo è efficiente. Ci sono altri trucchi da pensare quando costruisci i tuoi servizi web come progettarli in modo che possano essere chiamati un numero qualsiasi di volte e fornire comunque lo stesso risultato finale. Quindi, la mia raccomandazione è:

  • Non essere troppo bravo quando definisci i tuoi servizi web (non sono convinto dell'hype di micro-servizi che si sta verificando in questi giorni: troppi rischi di andare troppo lontano);

  • Async aumenta le prestazioni, quindi preferisci essere asincrono, invia notifiche via e-mail ogni volta che è possibile.

  • Costruire servizi più intelligenti per renderli "richiamabili" un numero qualsiasi di volte, elaborando con un uid o taskid che seguirà l'ordine dal basso verso l'alto fino alla fine, convalidando le regole aziendali in ogni passaggio;

  • Utilizza le code dei messaggi (JMS o altri) e passa ai processori di gestione degli errori che applicheranno le operazioni al "rollback" applicando operazioni opposte, inoltre, lavorare con un ordine asincrono richiederà una sorta di coda per convalidare lo stato corrente del processo, quindi considera quello;

  • In ultima istanza, (poiché potrebbe non accadere spesso), inserirlo in una coda per l'elaborazione manuale degli errori.

Torniamo con il problema iniziale che è stato pubblicato. Crea un account e crea un portafoglio e assicurati che tutto sia stato fatto.

Supponiamo che venga chiamato un servizio Web per orchestrare l'intera operazione.

Lo pseudo codice del servizio web sarebbe simile al seguente:

  1. Chiama il microservizio di creazione dell'account, passa alcune informazioni e un ID di attività univoco 1.1 Il microservizio di creazione dell'account controllerà prima se quell'account è già stato creato. Un ID attività è associato al record dell'account. Il microservizio rileva che l'account non esiste, quindi lo crea e memorizza l'id dell'attività. NOTA: questo servizio può essere chiamato 2000 volte, eseguirà sempre lo stesso risultato. Il servizio risponde con una "ricevuta che contiene informazioni minime per eseguire un'operazione di annullamento, se necessario".

  2. Chiama la creazione di Wallet, assegnandogli l'ID account e l'id attività. Diciamo che una condizione non è valida e la creazione del portafoglio non può essere eseguita. La chiamata ritorna con un errore ma non è stato creato nulla.

  3. L'orchestrator viene informato dell'errore. Sa che deve interrompere la creazione dell'account ma non lo farà da solo. Chiederà al servizio portafoglio di farlo superando la "ricevuta di annullamento minima" ricevuta alla fine del passaggio 1.

  4. Il servizio Account legge la ricevuta di annullamento e sa come annullare l'operazione; la ricevuta di annullamento potrebbe anche includere informazioni su un altro microservizio che avrebbe potuto chiamare per svolgere parte del lavoro. In questa situazione, la ricevuta di annullamento potrebbe contenere l'ID conto e, eventualmente, alcune informazioni aggiuntive richieste per eseguire l'operazione opposta. Nel nostro caso, per semplificare le cose, supponiamo che sia sufficiente eliminare l'account usando il suo ID account.

  5. Supponiamo ora che il servizio Web non abbia mai ricevuto l'esito positivo o negativo (in questo caso) dell'annullamento della creazione dell'account. Chiamerà semplicemente di nuovo il servizio di annullamento dell'account. E questo servizio non dovrebbe mai fallire perché il suo obiettivo è che l'account non esista più. Quindi controlla se esiste e non vede nulla da fare per annullarlo. Quindi restituisce che l'operazione è un successo.

  6. Il servizio Web restituisce all'utente che non è stato possibile creare l'account.

Questo è un esempio sincrono. Avremmo potuto gestirlo in modo diverso e inserire il caso in una coda di messaggi indirizzata all'help desk se non volessimo che il sistema ripristinasse completamente l'errore ". Ho visto che questo viene eseguito in un'azienda in cui non è sufficiente gli hook possono essere forniti al sistema back-end per correggere le situazioni.L'help desk ha ricevuto messaggi contenenti ciò che è stato eseguito con successo e aveva abbastanza informazioni per risolvere cose come la nostra ricevuta di annullamento potrebbe essere utilizzata in modo completamente automatizzato.

Ho eseguito una ricerca e il sito Web Microsoft ha una descrizione del modello per questo approccio. Si chiama il modello di transazione compensativa:

Modello di transazione compensativa


2
Pensi di poter ampliare questa risposta per fornire consigli più specifici all'OP. Allo stato attuale, questa risposta è piuttosto vaga e difficile da capire. Sebbene capisca come viene servito il caffè presso Starbucks, non mi è chiaro quali aspetti di questo sistema debbano essere emulati nei servizi REST.
jwg

Ho aggiunto un esempio relativo al caso inizialmente fornito nel post originale.
user8098437

2
Ho appena aggiunto un link al modello di transazione compensativa come descritto da Microsoft.
user8098437,

3
Per me questa è la risposta migliore. Così semplice
Oscar Nevarez,

1
Si noti che le transazioni di compensazione potrebbero essere assolutamente impossibili in alcuni scenari complessi (come brillantemente evidenziato nei documenti di Microsoft). In questo esempio, immagina che prima che la creazione del portafoglio non abbia esito positivo, qualcuno potrebbe leggere i dettagli sull'account associato effettuando una chiamata GET sul servizio Account, che idealmente non dovrebbe esistere in primo luogo poiché la creazione dell'account non è riuscita. Ciò può comportare un'incoerenza dei dati. Questo problema di isolamento è ben noto nel modello SAGAS.
Anmol Singh Jaggi,

32

Tutti i sistemi distribuiti hanno problemi con la coerenza transazionale. Il modo migliore per farlo è come hai detto, avere un commit in due fasi. Fai in modo che il portafoglio e l'utente siano creati in uno stato in sospeso. Dopo che è stato creato, effettuare una chiamata separata per attivare l'utente.

Quest'ultima chiamata dovrebbe essere ripetibile in modo sicuro (nel caso in cui la connessione venga interrotta).

Ciò richiederà che l'ultima chiamata sia a conoscenza di entrambe le tabelle (in modo che possa essere eseguita in una singola transazione JDBC).

In alternativa, potresti voler pensare al motivo per cui sei così preoccupato per un utente senza un portafoglio. Credi che questo causerà un problema? Se è così, forse avere quelle come chiamate di riposo separate è una cattiva idea. Se un utente non dovrebbe esistere senza un portafoglio, probabilmente dovresti aggiungere il portafoglio all'utente (nella chiamata POST originale per creare l'utente).


Grazie per il suggerimento I servizi Utente / Portafoglio erano fittizi, solo per illustrare il punto. Ma sono d'accordo che dovrei progettare il sistema in modo da evitare il più possibile la necessità di transazioni.
Olivier Lalonde,

7
Sono d'accordo con il secondo punto di vista. Sembra che anche il tuo microservizio, che crea l'utente, dovrebbe creare un portafoglio, perché questa operazione rappresenta un'unità di lavoro atomica. Inoltre, puoi leggere questo eaipatterns.com/docs/IEEE_Software_Design_2PC.pdf
Sattar Imamov

2
Questa è in realtà un'ottima idea. Undos è un mal di testa. Ma creare qualcosa in uno stato in sospeso è molto meno invasivo. Sono stati eseguiti dei controlli, ma non è stato ancora creato nulla di definitivo. Ora dobbiamo solo attivare i componenti creati. Probabilmente possiamo anche farlo in modo non transazionale.
Timo,

10

IMHO uno degli aspetti chiave dell'architettura dei microservizi è che la transazione è limitata al singolo microservizio (principio della responsabilità singola).

Nell'esempio corrente, la creazione dell'utente sarebbe una transazione propria. La creazione dell'utente spingerebbe un evento USER_CREATED in una coda di eventi. Il servizio Wallet si iscriverebbe all'evento USER_CREATED e farebbe la creazione di Wallet.


1
Supponendo di voler evitare qualsiasi 2PC e supporre che il servizio Utente scriva in un database, quindi non possiamo spingere il messaggio in una coda di eventi da parte dell'utente per essere transazionale, il che significa che potrebbe non farlo mai il servizio di portafoglio.
Roman Kharkovski,

@RomanKharkovski Un punto davvero importante. Un modo per affrontarlo potrebbe essere quello di avviare una transazione, salvare l'utente, pubblicare l'evento (non parte della transazione) e quindi eseguire il commit della transazione. (Caso peggiore, altamente improbabile, il commit fallisce e chi risponde all'evento non sarà in grado di trovare l'utente.)
Timo

1
Quindi archiviare l'evento nel database e nell'entità. Avere un lavoro pianificato per elaborare gli eventi memorizzati e inviarli al broker dei messaggi. stackoverflow.com/a/52216427/4587961
Yan Khonski

7

Se il mio portafoglio fosse solo un altro gruppo di record nello stesso database sql dell'utente, probabilmente avrei inserito l'utente e il codice di creazione del portafoglio nello stesso servizio e gestito quello utilizzando le normali funzionalità di transazione del database.

Mi sembra che ti stia chiedendo cosa succede quando il codice di creazione del portafoglio richiede di toccare un altro o più sistemi? Dico che tutto dipende da quanto sia complesso e o rischioso il processo di creazione.

Se si tratta solo di toccare un altro archivio di dati affidabile (diciamo uno che non può partecipare alle tue transazioni sql), quindi, a seconda dei parametri generali del sistema, potrei essere disposto a rischiare la possibilità che la seconda scrittura sia evanescente. Potrei non fare nulla, ma sollevare un'eccezione e gestire i dati incoerenti tramite una transazione di compensazione o anche un metodo ad hoc. Come dico sempre ai miei sviluppatori: "se questo genere di cose sta accadendo nell'app, non passerà inosservata".

Man mano che la complessità e il rischio della creazione del portafoglio aumentano, è necessario prendere provvedimenti per migliorare i rischi. Supponiamo che alcuni dei passaggi richiedano la chiamata di più API partner.

A questo punto potresti introdurre una coda di messaggi insieme alla nozione di utenti e / o portafogli parzialmente costruiti.

Una strategia semplice ed efficace per assicurarsi che le entità alla fine vengano costruite correttamente è quella di riprovare i lavori fino a quando non riescono, ma molto dipende dai casi d'uso per l'applicazione.

Vorrei anche riflettere a lungo sul motivo per cui ho avuto un passo incline al fallimento nel mio processo di provisioning.


4

Una soluzione semplice è quella di creare un utente utilizzando il Servizio utenti e utilizzare un bus di messaggistica in cui il servizio utente emette i suoi eventi e il servizio Wallet registra sul bus di messaggistica, ascolta l'evento creato dall'utente e crea Wallet per l'utente. Nel frattempo, se l'utente passa all'interfaccia utente di Wallet per vedere il proprio portafoglio, controlla se l'utente è stato appena creato e mostra che la creazione del tuo portafoglio è in corso, controlla tra qualche istante


3

Quali soluzioni sono disponibili per prevenire questo tipo di incoerenza dei dati?

Tradizionalmente, vengono utilizzati i gestori di transazioni distribuite. Alcuni anni fa nel mondo Java EE potresti aver creato questi servizi come EJB distribuiti su nodi diversi e il tuo gateway API avrebbe effettuato chiamate remote a tali EJB. Il server delle applicazioni (se configurato correttamente) garantisce automaticamente, utilizzando il commit in due fasi, che la transazione venga sottoposta a commit o rollback su ciascun nodo, in modo da garantire la coerenza. Ma ciò richiede che tutti i servizi siano distribuiti sullo stesso tipo di application server (in modo che siano compatibili) e che in realtà funzionino solo con servizi distribuiti da una singola azienda.

Esistono modelli che consentono alle transazioni di estendere più richieste REST?

Per SOAP (ok, non REST), esiste la specifica WS-AT ma nessun servizio che io abbia mai dovuto integrare ha il supporto. Per REST, JBoss ha qualcosa in cantiere . Altrimenti, il "modello" consiste nel trovare un prodotto che è possibile collegare all'architettura o creare la propria soluzione (non consigliato).

Ho pubblicato un prodotto simile per Java EE: https://github.com/maxant/genericconnector

Secondo il documento a cui si fa riferimento, esiste anche il modello Try-Cancel / Confirm e il prodotto associato di Atomikos.

I motori BPEL gestiscono la coerenza tra i servizi distribuiti in remoto utilizzando la compensazione.

In alternativa, so che REST potrebbe non essere adatto a questo caso d'uso. Forse sarebbe il modo corretto di gestire questa situazione per eliminare completamente REST e utilizzare un protocollo di comunicazione diverso come un sistema di coda messaggi?

Esistono molti modi per "associare" risorse non transazionali in una transazione:

  • Come suggerisci, potresti utilizzare una coda di messaggi transazionale, ma sarà asincrona, quindi se dipendi dalla risposta diventa disordinata.
  • È possibile scrivere il fatto che è necessario chiamare i servizi di back-end nel database e quindi chiamare i servizi di back-end utilizzando un batch. Ancora una volta, asincrono, quindi può diventare disordinato.
  • È possibile utilizzare un motore di processo aziendale come gateway API per orchestrare i microservizi di back-end.
  • È possibile utilizzare EJB remoto, come indicato all'inizio, poiché supporta immediatamente le transazioni distribuite.

O dovrei applicare coerenza nel mio codice dell'applicazione (ad esempio, avendo un lavoro in background che rileva incoerenze e le corregge o avendo un attributo "stato" sul mio modello utente con i valori "creazione", "creato", ecc.)?

Giocando con i diavoli sostenitori: perché costruire qualcosa del genere, quando ci sono prodotti che lo fanno per te (vedi sopra), e probabilmente lo fanno meglio di te, perché sono provati e testati?


2

Personalmente mi piace l'idea di Micro Services, moduli definiti dai casi d'uso, ma come menziona la tua domanda, hanno problemi di adattamento per le aziende classiche come banche, assicurazioni, telecomunicazioni, ecc ...

Le transazioni distribuite, come molti hanno già detto, non sono una buona scelta, ora le persone vanno di più per sistemi eventualmente coerenti, ma non sono sicuro che funzionerà per banche, assicurazioni, ecc.

Ho scritto un blog sulla mia soluzione proposta, potrebbe essere questo può aiutarti ...

https://mehmetsalgar.wordpress.com/2016/11/05/micro-services-fan-out-transaction-problems-and-solutions-with-spring-bootjboss-and-netflix-eureka/


0

La consistenza finale è la chiave qui.

  • Uno dei servizi è scelto per diventare gestore principale dell'evento.
  • Questo servizio gestirà l'evento originale con un unico commit.
  • Il gestore principale si assumerà la responsabilità di comunicare in modo asincrono gli effetti secondari ad altri servizi.
  • Il gestore principale eseguirà l'orchestrazione di altre chiamate di servizi.

Il comandante è responsabile della transazione distribuita e prende il controllo. Conosce le istruzioni da eseguire e coordinerà l'esecuzione. Nella maggior parte degli scenari ci saranno solo due istruzioni, ma può gestire più istruzioni.

Il comandante si assume la responsabilità di garantire l'esecuzione di tutte le istruzioni e ciò significa ritirarsi. Quando il comandante tenta di effettuare l'aggiornamento remoto e non ottiene una risposta, non ha più tentativi. In questo modo il sistema può essere configurato per essere meno soggetto a guasti e guarire se stesso.

Poiché abbiamo dei tentativi, abbiamo idempotenza. L'idempotenza è la proprietà di poter fare qualcosa due volte in modo tale che il risultato finale sia lo stesso di se fosse stato fatto una sola volta. Abbiamo bisogno dell'idempotenza presso il servizio remoto o l'origine dati in modo che, nel caso in cui riceva l'istruzione più di una volta, la elabori una sola volta.

Consistenza finale Ciò risolve la maggior parte delle sfide relative alle transazioni distribuite, tuttavia è necessario prendere in considerazione un paio di punti qui. Ogni transazione fallita sarà seguita da un nuovo tentativo, la quantità di tentativi ripetuti dipende dal contesto.

La coerenza è eventuale, ad esempio, mentre il sistema è fuori dallo stato coerente durante un nuovo tentativo, ad esempio se un cliente ha ordinato un libro, ha effettuato un pagamento e quindi aggiorna la quantità di magazzino. Se le operazioni di aggiornamento dello stock falliscono e presupponendo che fosse l'ultimo stock disponibile, il libro sarà comunque disponibile fino a quando l'operazione di nuovo tentativo per l'aggiornamento dello stock non avrà avuto esito positivo. Dopo che il tentativo ha esito positivo, il sistema sarà coerente.


-2

Perché non utilizzare la piattaforma di gestione API (APIM) che supporta script / programmazione? Quindi, sarai in grado di creare un servizio composito nell'APIM senza disturbare i micro-servizi. Ho progettato usando APIGEE per questo scopo.

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.