Jag är säker på att de flesta av oss, på ett eller annat sätt, har stött på problem med datainkonsekvenser i ett mjukvarusystem. När man till exempel använder en bankomat för att ta ut pengar händer det ofta att transaktioner misslyckas av olika skäl. Bankomaten kanske inte lämnar ut kontanterna, trots att den visar att beloppet har dragits från kontot. När kunder rapporterar felet i systemet svarar banken vanligtvis att det kommer att ta mellan 60 minuter och 24 timmar att rätta till den felaktiga transaktionsposten. Detta är ett typiskt exempel på datainkonsekvens där bankomaten och bankens server under en kort stund inte är synkroniserade.
Låt oss ta ett annat exempel på betalning av räkningar via mobilappar. Användaren initierar betalningen, appen ansluter säkert till bankens gränssnitt, betalningen behandlas och överlämnas till handlarens gränssnitt för bekräftelse. Låt oss säga att handlarens webbplats är otillgänglig just nu. Nu förblir transaktionen ofullständig under en kort stund tills appens servrar synkroniseras med handlarens gränssnitt.
Observera att i båda ovanstående fall beror datainkonsekvenserna på att vissa delar av systemet är otillgängliga, att tjänster inte svarar, nätverksrelaterade fel etc., och dessa faktorer är mycket vanliga i stora distribuerade mjukvarusystem. Även om systemet så småningom återupprättar konsistensen är en smidig och problemfri upplevelse för slutanvändaren inte garanterad.
Att bygga mjukvaruarkitektur i mikrotjänststil innebär ett distribuerat tillvägagångssätt där varje tjänst/funktion i en mjukvara är i hög grad självständig, har en specifik affärsgräns eller ett definierat sammanhang, kan vara medveten eller inte om andra tjänsters existens, kan hantera sina egna data, kan använda olika lagringsmekanismer och eventuellt är skriven i olika programmeringsspråk, och underhålls av separata team.
Mikrotjänstarkitekturen använder ett decentraliserat tillvägagångssätt och är i hög grad inspirerad av naturen. Mikrotjänster har blivit ett populärt val för att bygga stora företagssystem. Med framväxten av molnet, elastisk databehandling och lagring samt containerteknik och orkestreringsmekanismer har det skett en enorm ökning av utvecklingen av applikationer som använder mikrotjänstarkitektur.
Vid det här laget har du kanske redan insett att mikrotjänstarkitekturens starka förespråkande av ett decentraliserat datatillvägagångssätt leder till eventual datakonsistens i systemen. Det vore dock inte klokt att tro att den monolitiska programvaruarkitekturen hanterar dessa problem problemfritt. Den har sina egna risker.
Det är därför absolut nödvändigt att utvecklare av mikrotjänster tar hänsyn till dessa frågor och är medvetna om de utmaningar som kan uppstå när det gäller den slutliga datakonsistensen. De tekniker som listas nedan hjälper till att hantera den slutliga konsistensen i mikrotjänster.
Saga-mönstret
Transaktioner som sträcker sig över flera tjänster betraktas som en saga/kedja av enkla atomära lokala transaktioner på varje tjänstenivå. En tjänst slutför och bekräftar alltså sin transaktion, meddelar nästa tjänst i kedjan med en händelse/ett meddelande för att utlösa nästa lokala transaktion, och så vidare. Om en transaktion i denna kedja misslyckas, av någon särskild anledning, utlöser det i princip en ångra-operation som går bakåt i kedjan. Det är därför absolut nödvändigt att ta itu med mönsterfel redan vid utformningen av arkitekturen.

Saga-mönstret hanterar visserligen problemen med eventualkonsistens, men är mer lämpligt för mindre distribuerade arkitekturer än för storskaliga. Utvecklarna måste också ta hänsyn till att kompensationsanrop/transaktioner också kan misslyckas. Detta gör det lämpligt för enklare och mindre distribuerade arkitekturer.
Change Data Capture (CDC)
CDC, som visserligen är väl etablerat och används i stor utsträckning inom datalagring, kan mycket väl anpassas effektivt till mikrotjänstdesign för att säkerställa att transaktionerna sträcker sig över tjänsterna med konsistens.
Grundprincipen förblir i grunden densamma. När en tjänst, efter att dess lokala transaktion har bekräftats i den lokala databasen, utlöser den en separat process som skapar en ändringsregistrering och vidarebefordrar denna till nästa tjänst. Denna tjänst tar emot ändringsregistreringsposten från den föregående tjänsten, bearbetar den, bekräftar den i sin lokala databas och skapar i en separat process inom sig en egen ny ändringsregistreringspost som vidarebefordras till nästa uppsättning tjänster.
På så sätt kan denna ändringsregistrering vidarebefordras genom de distribuerade tjänsterna tills alla tjänster är synkroniserade utan någon större belastning på orkestreringen.
Sätt att implementera CDC i mikrotjänster:
Användning av databastransaktionsloggar: Många databaser erbjuder driftsloggar och transaktionsloggar. Genom att skanna innehållet i dessa loggar och tolka ändringarna kan man identifiera de ändringar som gjorts i databasen. Detta kan utgöra den ändringsregistrering som ska spridas vidare till nästa tjänst.

Användning av Kafka Connect och Apache Kafka: De databasändringar som registrerats i en tjänst kan kopplas till en Kafka-anslutning, vilket effektivt vidarebefordrar ändringarna till olika Kafka-ämnen så att prenumeranterna kan ta del av dem och agera.

CDC lämpar sig generellt sett för stora distribuerade arkitekturer, eftersom det inte är prestandakrävande och enkelt kan aktiveras utan att några extra ändringar i schemat behövs. CDC har i sig lägre latens och gör det möjligt för nedströmsdatabaser att snabbt följa ändringarna. Det finns också möjlighet att köra strömbehandling på det ändringsflöde som tjänsterna tar emot.
Den största nackdelen som utvecklare upplever med CDC är dock bristen på flexibilitet när det gäller att ändra tjänsternas scheman. Detta hämmar i viss utsträckning utvecklingen av tjänsternas databasscheman. En schemaändring i en tjänst utlöser förändringar i alla nedströmsliggande tjänster.
Förutom dessa två metoder finns det flera andra sätt att uppnå datasynkronisering via CDC som har implementerats av leverantörer av molninfrastruktur.
Nu när vi har sett två populära metoder för att uppnå datakonsistens och integration samt deras nackdelar, låt oss nu betrakta frågan om datakonsistens ur ett helt nytt perspektiv och paradigm... ”Att acceptera inkonsekvensen” – Det är inte alla mjukvarusystem eller delar av systemet som behöver att data är synkroniserade och konsekventa hela tiden. Låt oss gå tillbaka till vårt exempel med uttag av pengar via en bankomat. Vi har alla accepterat datainkonsekvensen här och väntar på att systemet ska bli konsekvent efter en viss tid.
Många affärssystem är mer toleranta mot datainkonsekvenser än vad man vanligtvis tror. Detta beror på att företag lägger större vikt vid och tjänar mer på tjänstens tillgänglighet, vilket leder oss till den långvariga debatten om att värdera BASE högre än ACID i många system på företagsnivå.
BASE är en förkortning för Basically Available, Softstate och EventualConsistency
ACID är en förkortning för Atomicity, Consistent, Isolatedoch Durable
Detta stämmer överens med det välkända CAP-teoremet, som säger att det är omöjligt för ett distribuerat datalager att samtidigt uppfylla mer än två av följande tre garantier: konsistens, tillgänglighetoch partitionstolerans.
Därför prioriterar mjukvarusystem alltid antingen BASE framför ACID eller tvärtom, beroende på behov, och ser till att delar av systemet förblir konsistenta.
Lämna gärna kommentarer, synpunkter och frågor om den här artikeln – jag ska försöka besvara dem så snart jag har möjlighet.
Relevant Blogs
Bortom automatisering: Hur AI omvandlar testtekniken inom flyg- och rymdindustrin till teknisk intelligens
Bortom automatisering: Hur AI omvandlar testtekniken inom flyg- och rymdindustrin till teknisk intelligens