F: En serviceserver, en databas, operation: fråga användarens nuvarande saldo, dra av 3 % av det aktuella saldot som hanteringsavgift
Synkroniserad lås DB-lås
F: Två tjänsteservrar, en databas, operation: fråga användarens aktuella saldo, dra av 3 % av det aktuella saldot som hanteringsavgift Distribuerade slussar
Vilken typ av distribuerat lås behöver vi? Det kan säkerställa att samma metod i ett distribuerat applikationskluster endast kan köras av en tråd på en maskin samtidigt.
Om detta lås är ett återinförande lås (undvik deadlocks)
Detta lås är bäst att vara ett blockerande lås (fundera på om du vill ha det efter ditt företags behov)
Detta lås är bäst att vara ett rättvist lås (fundera på om du vill ha det eller inte beroende på företagets behov)
Det finns mycket tillgängliga funktioner för förvärv och frigörelselås
Prestandan hos uppsamlings- och frigörelselåsen är bättre
1. Distribuerade lås baserade på databaser
Distribuerade lås baserade på tabellbaserade implementationer
När vi vill låsa en metod, kör följande SQL: infoga i methodLock(method_name,desc) värden ('method_name','desc')
Eftersom vi har gjort en unikhetsbegränsning på method_name, om flera förfrågningar skickas till databasen samtidigt, kommer databasen att säkerställa att endast en operation kan lyckas, då kan vi anta att tråden som lyckades låsa metoden och kan köra metodens kroppsinnehåll.
När metoden körs, om du vill släppa låset, måste du köra följande SQL: delete från methodLock där method_name ='method_name'
Denna enkla implementation ovan har följande problem:
- Detta lås beror på databasens tillgänglighet, som är en enda punkt och gör att affärssystemet blir otillgängligt när databasen hängs upp.
- Detta lås har ingen utgångstid, och när upplåsningsoperationen misslyckas kommer låsposten att finnas kvar i databasen och andra trådar kan inte längre få tag på låset.
- Detta lås kan bara vara icke-blockerande, eftersom insättningsoperationen av data direkt rapporterar ett fel när insättningen misslyckas. Trådar som inte får lås kommer inte att gå in i kön och måste trigga låsförvärvsoperationen igen för att få låset igen.
- Låset är icke-re-inträdande, och samma gänga kan inte få låset igen förrän det släpps. För att datan i datan redan existerar.
- Detta lås är ett orättvist lås, och alla trådar som väntar på låset tävlar om låset av en slump.
Självklart kan vi också lösa ovanstående problem på andra sätt.
- Är databasen en enda punkt? Bygg två databaser, och datan synkroniseras i båda riktningarna. När du lagt på, byt snabbt till backup-biblioteket.
- Ingen utgångstid? Gör bara en schemalagd uppgift för att rensa timeout-data i databasen med jämna mellanrum.
- Icke-blockerande? Gör en while-loop tills insatsen lyckas och sedan återvänder till framgång.
- Icke-återkompans? Lägg till ett fält i databastabellen för att registrera värdinformationen och trådinformationen för den maskin som för närvarande får låset, och nästa gång du får låset, fråga databasen först, om värdinformationen och trådinformationen för den aktuella maskinen finns i databasen kan du direkt tilldela låset till honom.
- Orättvist? Skapa en annan mellanliggande tabell för att registrera alla trådar som väntar på låset, och sortera dem efter skapandetid, och endast den först skapade tråden får hämta låset
Distribuerade lås baserade på exklusiva lås
Förutom att lägga till och ta bort poster i datatabellen kan distribuerade lås också implementeras med hjälp av de lås som följer med datan.
Vi använder också databastabellen vi just skapat. Distribuerade lås kan implementeras genom exklusiva lås på databaser. Den MySQL-baserade InnoDB-motorn kan använda följande metoder för att implementera låsningsoperationer:
Om du lägger till för uppdatering efter frågesatsen kommer databasen att lägga till ett exklusivt lås i databastabellen under frågeprocessen. När ett exklusivt lås läggs till på en post kan andra trådar inte längre lägga till ett exklusivt lås på posten på den linjen.
Vi kan tänka oss att tråden som erhåller det exklusiva låset kan få det distribuerade låset, och när låset är erhållet kan metodens affärslogik exekveras och sedan låsas upp via följande metoder:
public void unlock(){ connection.commit(); }
via connection.commit(); operation för att öppna låset.
Denna metod kan effektivt lösa de ovan nämnda problemen om oförmågan att släppa låset och blockera det.
Blockera lås? Satsen for update återvänder omedelbart efter lyckad körning och förblir blockerad tills den lyckas.
Tjänsten är nere efter låsning, kan inte släppas? På detta sätt släpper databasen låset av sig själv efter att tjänsten har gått ner.
Det löser dock fortfarande inte direkt problemet med databasens single point, reentrancy och fair locking.
För att sammanfatta hur man använder databasen för att implementera distribuerade lås, som båda bygger på en tabell i databasen, är det ena att avgöra om det finns ett lås genom förekomsten av poster i tabellen, och det andra är att implementera distribuerade lås genom databasens exklusiva lås.
Fördelar med distribuerad låsning i databaser
Direkt med hjälp av databasen är det lätt att förstå.
Nackdelar med att implementera distribuerade lås i databaser
Det kommer att finnas olika problem, och hela lösningen kommer att bli mer och mer komplex i processen att lösa problemet.
Att driva databasen kräver viss overhead, och prestandaproblem måste beaktas.
2. Distribuerade lås baserade på cache
Jämfört med den databasbaserade distribuerade låsningslösningen kommer cache-baserad implementation att prestera bättre prestandamässigt.
Det finns många mogna cacheprodukter för närvarande, inklusive Redis, memcached, etc. Här tar vi Redis som exempel för att analysera metoden att använda cache för att implementera distribuerade lås.
Det finns många relaterade artiklar på internet om att implementera distribuerade lås baserade på Redis, och huvudmetoden är att använda Jedis.setNX-metoden.
Det finns också flera problem med ovanstående implementation:
1. Enpunktsproblemet.
2. Detta lås har ingen utgångstid, när upplåsningsoperationen misslyckas kommer det att göra att låsposten alltid är i redis, och andra trådar kan inte längre få tag på låset.
3. Detta lås kan bara vara icke-blockerande, och det kommer att återvända direkt oavsett framgång eller misslyckande.
4. Detta lås är icke-reentrant, efter att en tråd fått låset kan den inte få låset igen innan låset släpps, eftersom nyckeln som används redan finns i redis. setNX-operationer kan inte längre utföras.
5. Detta lås är orättvist, alla väntande trådar startar setNX-operationer samtidigt, och lyckosamma trådar kan få låset.
Självklart finns det också sätt att lösa det.
- Numera stödjer vanliga cachetjänster klusterdistribution för att lösa single point-problem genom klustring.
- Ingen utgångstid? SetExpire-metoden för redis stöder inkommande utgångstid, och datan raderas automatiskt efter att tiden har nåtts.
- Icke-blockerande? medan de upprepade gånger avrättades.
- Är det inte möjligt att återinträda? Efter att en tråd har fått låset, spara den aktuella värdinformationen och trådinformationen, och kontrollera om du är ägare till det aktuella låset innan du hämtar det nästa gång.
- Orättvist? Lägg alla väntande trådar i en kö innan en tråd får ett lås, och ta sedan låset på en först in, först ut-princip.
Synkroniseringspolicyn för redis-klustret tar tid, och det är möjligt att tråd A får ett lås efter att NX har satt sig framgångsrikt, men detta värde har inte uppdaterats till servern där tråd B kör setNX, vilket orsakar samtidighetsproblem.
Salvatore Sanfilippo, författaren till redis, föreslog Redlock-algoritmen, som implementerar distribuerad låshantering (DLM) som är säkrare och mer pålitlig än en enskild nod.
Redlock-algoritmen antar att det finns N redis-noder som är oberoende av varandra, vanligtvis inställda på N=5, och dessa N noder körs på olika maskiner för att behålla fysisk oberoende.
Algoritmens steg är följande:
1. Klienten får den aktuella tiden i millisekunder. 2. Klienten försöker få låset av N noder (varje nod får låset på samma sätt som cachelåset som nämndes tidigare), och N noder får låset med samma nyckel och värde. Klienten måste ställa in gränssnittets åtkomsttimeout, och gränssnittets timeout måste vara mycket kortare än låstimeouten, till exempel är låsets automatiskt frigjorda tid 10 sekunder, sedan är gränssnittets timeout inställd på cirka 5–50 ms. Detta gör att du kan gå ut så tidigt som möjligt när du kommer åt en redis-nod efter att den gått ner, och minskar den normala användningen av låset. 3. Klienten beräknar hur lång tid det tar att få låset, genom att subtrahera tiden som erhållits i steg 1 med den aktuella tiden, men först när klienten har fler än 3 noder av låset och tiden att få låset är kortare än låsets timeout-tid, får klienten det distribuerade låset. 4. Klientens tid att få låset är den inställda låstidsavslutningen minus tiden som krävs för att få låset beräknat i steg 3. 5. Om klienten misslyckas med att få tag på låset kommer klienten att radera alla lås i tur och ordning. Med Redlock-algoritmen kan man garantera att den distribuerade låstjänsten fortfarande kan fungera när man hänger upp till 2 noder, vilket avsevärt förbättrar tillgängligheten jämfört med det tidigare databaslåset och cachelåset.
Dock skrev en expert på distribuerad kod en artikel "How to do distributed locking" där han ifrågasatte rättfärdigheten i Redlock.
Experten nämnde att det finns två aspekter att beakta när man överväger distribuerade lås: prestanda och korrekthet.
Om du använder ett högpresterande distribuerat lås och korrekt information inte krävs, räcker det att använda ett cachelås.
Om du använder ett mycket pålitligt distribuerat lås måste du ta hänsyn till strikta tillförlitlighetsfrågor. Redlock, å andra sidan, uppfyller inte korrektheten. Varför inte? Experter listar flera aspekter.
Numera använder många programmeringsspråk virtuella maskiner med GC-funktioner, i Full GC slutar programmet att bearbeta GC, ibland tar Full GC lång tid, och till och med programmet har några minuters fördröjning, artikeln listar exemplet HBase, HBase ibland GC i några minuter, vilket gör att leasingen går ut i timeout. Till exempel, i figuren nedan får klient 1 ett lås och är på väg att bearbeta en delad resurs, och när den är på väg att bearbeta en delad resurs sker Full GC tills låset löper ut. På så sätt får klient 2 låset igen och börjar arbeta med den delade resursen. När klient 2 behandlar slutförer klient 1 full GC och börjar bearbeta delade resurser, så att båda klienterna bearbetar delade resurser.
Experter gav en lösning, som visas i figuren nedan, det ser ut som MVCC, för en token till låset, token är konceptet version, varje gång operationslås läggs token till 1, ta token när delade resurser bearbetas, endast den specificerade versionen av token kan hantera den delade resursen.
Sedan sa experten också att algoritmen förlitar sig på lokal tid, och att Redis förlitar sig på getTimeOfDay-metoden för att få tiden istället för den monotona klockan vid hantering av nyckelutgång, vilket också leder till tidsmässiga felaktigheter. Till exempel, i ett scenario har två klient 1 och klient 2 5 redis-noder (A, B, C, D och E).
1. Client 1 får framgångsrikt låset från A, B och C, och får låsnätverkets timeout från D och E. 2. Klockan för nod C är felaktig, vilket orsakar låstidsavslutningen. 3. klient 2 lyckas få låset från C, D och E, och får låsnätverkets timeout från A och B. 4. På detta sätt får både klient 1 och klient 2 en låsning. För att sammanfatta de två punkter experter säger om Redlocks otillgänglighet:
1. GC och andra scenarier kan inträffa när som helst, vilket gör att klienten får ett lås, och bearbetningstidsavbrottet gör att en annan klient får låset. Experter gav också en lösning för att använda självinkrementerande tokens. 2. Algoritmen förlitar sig på lokal tid, och klockan kommer att vara felaktig, vilket resulterar i att två klienter får låsningar samtidigt. Därför är experternas slutsats att Redlock endast kan fungera normalt inom det begränsade nätverksfördröjningsområdet, avbrottet i ett begränsat program och det begränsade klockfelintervallet, men gränserna för dessa tre scenarier kan inte bekräftas, så experter rekommenderar inte att använda Redlock. För scenarier med höga korrekthetskrav rekommenderar experter Zookeeper, som senare kommer att diskuteras med Zookeeper som distribuerat lås.
Svar från författaren till Redis
Redis författare svarade genom att skriva en blogg efter att ha sett expertens artikel. Författaren tackade artigt experten och uttryckte sedan sin oenighet med expertens synsätt.
REDIS-författarens diskussion om att använda tokens för att lösa locktimeout-problemet kan sammanfattas i följande fem punkter:
Punkt 1, användningen av distribuerade lås innebär generellt att du inte har något annat sätt att kontrollera delade resurser, experter använder tokens för att säkerställa hanteringen av delade resurser, då finns det inget behov av distribuerade lås. Punkt 2: För tokengenerering, för att säkerställa tillförlitligheten hos tokens som erhållits av olika klienter, behöver tjänsten som genererar tokens fortfarande distribuerade lås för att säkerställa tjänstens tillförlitlighet. Punkt 3, för hur experter säger att självinkrementerande tokens anser redis-författaren att det är helt onödigt, varje klient kan generera en unik uuid som token och sätta den delade resursen till ett tillstånd som endast klienten med uuiden kan hantera, så att andra klienter inte kan bearbeta den delade resursen förrän klienten som får låset släpper låset. Som visas i figuren ovan, om klienten för token 34 skickar GC under skrivprocessen och gör att låset går ut i timeout, kan en annan klient få låset för token 35 och börja skriva igen, vilket resulterar i en låskonflikt. Därför kan ordningen på tokens inte kombineras med delade resurser. Punkt 5, redisförfattaren anser att distribuerade lås i de flesta scenarier används för att hantera uppdateringsproblem i icke-transaktionella scenarier. Författaren bör mena att det finns vissa scenarier där det är svårt att kombinera tokens för att hantera delade resurser, så man måste förlita sig på lås för att låsa resurser och bearbeta dem. Ett annat klockproblem som experter diskuterar, Redis författare ger också en förklaring. Om tiden det tar att få låset är för lång och överskrider den vanliga timeout-tiden för låset, kan klienten inte få låset vid denna tidpunkt, och experter kommer inte att föreslå några exempel.
Personliga känslor
Det första problemet jag sammanfattar är att efter att en klient fått ett distribuerat lås kan låset släppas efter en timeout under klientens bearbetning. Tidigare, när man talade om timeout som sattes av databaslåset på 2 minuter, om en uppgift upptar ett orderlås i mer än 2 minuter, kan det andra handelscentret få tag på detta orderlås, så att de två handelscentren kan behandla samma order samtidigt. Under normala omständigheter behandlas uppgiften på sekunder, men ibland är timeouten som sätts av att ansluta till en RPC-förfrågan för lång, och det finns flera sådana timeout-förfrågningar i en uppgift, vilket gör att det är troligt att den automatiska upplåsningstiden överskrids. Om vi skriver i Java kan det finnas Full GC i mitten, så efter att låset låses upp efter låsets timeout kan klienten inte uppfatta det, vilket är en mycket allvarlig sak. Jag tror inte att detta är ett problem med själva låset, så länge vilket distribuerat lås som helst ovan har egenskaperna för timeout-frigörelse kommer ett sådant problem att uppstå. Om du använder låstidsavbrottsfunktionen måste klienten ställa in låstidsavbrottet och agera därefter, istället för att fortsätta bearbeta den delade resursen. Redlocks algoritm returnerar den låstid som klienten kan uppta efter att klienten fått låset, och klienten måste bearbeta denna tid för att stoppa uppgiften efter den tiden.
Det andra problemet är att distribuerade experter inte förstår Redlock. En viktig funktion i Redlock är att tiden att få tag på låset är den totala tiden låset automatiskt går till timeout minus tiden det tar att få låset, så att tiden det tar för klienten att bearbeta är relativ, oavsett lokal tid.
Ur detta perspektiv kan Redlocks korrekthet vara väl garanterad. Noggrann analys av Redlock, jämfört med redis av en nod, är den främsta egenskapen som Redlock ger högre tillförlitlighet, vilket är en viktig egenskap i vissa scenarier. Men jag tycker att Redlock har spenderat för mycket pengar för att uppnå pålitlighet.
- Först måste 5 noder distribueras för att göra Redlock mer pålitligt.
- Sedan behöver du begära 5 noder för att få låset, och genom Future-metoden kan du först begära till 5 noder samtidigt och sedan få ihop svarsresultatet, vilket kan förkorta svarstiden, men det tar ändå längre tid än ett en-nodes redis-lås.
- Eftersom mer än 3 av de 5 noderna måste erhållas, kan det uppstå en låskonflikt, det vill säga att alla har fått 1–2 lås, och som ett resultat kan ingen få låset, detta problem, lånar författaren av Redis kärnan i flottalgoritmen, genom kollisionen vid slumpmässig tidpunkt kan konflikttiden minskas avsevärt, men detta problem kan inte undvikas särskilt väl, särskilt när låset förvärvas för första gången, så tidskostnaden för att skaffa låset ökar.
- Om 2 av 5 noder är nere kommer tillgängligheten för låset att minskas kraftigt, först måste du vänta tills resultaten från dessa två nedslagna noder går ut innan du återvänder, och det finns bara 3 noder, och klienten måste få låsen för alla 3 noder för att ha låset, vilket också är svårare.
- Om det finns en nätverkspartition kan det finnas en situation där klienten aldrig kommer att kunna få tag på låset.
Efter att ha analyserat så många orsaker tror jag att den mest kritiska punkten i Redlocks problem är att Redlock kräver att klienter säkerställer konsistens i skrivningarna, och de fem backend-noderna är helt oberoende, och alla klienter måste driva dessa fem noder. Om det finns en ledare bland 5 noder kan klienten synkronisera ledarens data så länge klienten får låset från ledaren, så att det inte uppstår problem som partitionering, timeouts och konflikter. Därför tror jag att användning av en distribuerad samordningstjänst med stark konsekvens kan lösa problemet bättre för att säkerställa korrektheten hos distribuerade lås.
Frågan uppstår igen, hur lång tid bör jag ställa in utgångstiden? Hur man ställer in ogiltigförklaringstiden är för kort, och låset släpps automatiskt innan metoden körs, då uppstår samtidighetsproblem. Om det tar för lång tid kan andra trådar som får låset behöva vänta länge.
Detta problem finns också med användning av databaser för att implementera distribuerade lås.
Den nuvarande mainstream-metoden för detta problem är att sätta en kort timeout-tid för varje lås som erhålls och starta en tråd för att uppdatera låsets timeout-tid varje gång timeouten är på väg att nå timeouten. Avsluta den här tråden samtidigt som du släpper låset. Till exempel använder redisson, den officiella distribuerade låskomponenten i redis, denna lösning.
Fördelar med att använda caching för att implementera distribuerade lås Bra prestation.
Nackdelar med att använda caching för att implementera distribuerade lås Implementeringen är för ansvarsfull, det finns för många faktorer att ta hänsyn till.
Distribuerade lås baserade på Zookeeper-implementering
Distribuerade lås baserade på Zookeeper tillfälligt ordnade noder.
Den allmänna idén är att när varje klient låser en metod, genereras en unik omedelbar ordnad nod i katalogen för den specificerade nod som motsvarar metoden på zookeeper. Sättet att avgöra om man ska få ett lås är enkelt, du behöver bara bestämma det med det lägsta serienumret i den ordnade noden. När låset släpps, radera helt enkelt den omedelbara noden. Samtidigt kan det undvika problemet med deadlocks orsakade av driftstopp som inte kan lösas.
Låt oss se om Zookeeper kan lösa de problem som nämnts tidigare.
- Låset går inte att släppa? Att använda Zookeeper kan effektivt lösa problemet med att lås inte släpps, eftersom när man skapar ett lås skapar klienten en tillfällig nod i ZK, och när klienten får låset och plötsligt hänger det (sessionsanslutningen bryts), raderas den tillfälliga noden automatiskt. Andra kunder kan få låset igen.
- Lås som inte blockerar? När noden ändras meddelar Zookeeper klienten, och klienten kan kontrollera om noden den skapade är det minsta ordinaltalet bland alla noder.
- Kan du inte komma in igen? När klienten skapar en nod skriver den direkt värdinformationen och trådinformationen för den aktuella klienten till noden, och nästa gång du vill få låset kan du jämföra det med data i den nuvarande minsta noden. Om informationen är densamma som din egen kan du direkt hämta låset, och om det är annorlunda skapa en tillfällig sekventiell nod för att delta i kön.
Frågan uppstår igen, vi vet att Zookeeper behöver distribueras i kluster, kommer det att uppstå problem med datasynkronisering som i Redis-kluster?
Zookeeper är en distribuerad komponent som garanterar svag konsistens, dvs. slutlig konsistens.
Zookeeper använder ett datasynkroniseringsprotokoll kallat Quorum Based Protocol. Om det finns N Zookeeper-servrar i Zookeeper-klustret (N är vanligtvis udda, 3 kan uppfylla datatillförlitlighet och har hög läs- och skrivprestanda, och 5 har bäst balans mellan datatillförlitlighet och läs- och skrivprestanda), så synkroniseras en skrivoperation av användaren först till N/2 + 1-servrar och återlämnas sedan till användaren, vilket uppmanar användaren att skriva framgångsrikt. Datasynkroniseringsprotokollet baserat på Quorum Based Protocol avgör den konsistens i styrkan som Zookeeper kan stödja.
I en distribuerad miljö är datalagring som uppfyller stark konsistens i princip obefintlig, och det kräver att alla noder uppdateras synkront när data från en nod uppdateras. Denna synkroniseringsstrategi finns i master-slave synkron replikeringsdatabas. Denna synkroniseringsstrategi har dock för stor påverkan på skrivprestandan och förekommer sällan i praktiken. Eftersom Zookeeper skriver N/2+1-noder synkront, och N/2 noder inte uppdateras synkront, är Zookeeper inte starkt konsekvent.
Användarens datauppdateringsoperation garanterar inte att efterföljande läsningar läser det uppdaterade värdet, men kommer så småningom att visa konsistens. Att offra konsistens betyder inte att helt ignorera datans konsistens, annars är datan kaotisk, så oavsett hur hög systemtillgängligheten är, oavsett hur bra fördelningen är, är den värdelös. Att offra konsistens är bara att stark konsistens i relationsdatabaser inte längre krävs, utan så länge systemet kan uppnå slutlig konsistens.
En fråga med en enda punkt? Att använda Zookeeper kan effektivt lösa ett enskilt punktsproblem, ZK distribueras i kluster, så länge mer än hälften av maskinerna i klustret överlever kan tjänsten tillhandahållas till omvärlden.
Rättviseproblem? Att använda Zookeeper kan lösa problemet med rättvisa lås, de tillfälliga noder som klienten skapar i ZK är ordnade, och varje gång låset släpps kan ZK meddela den minsta noden att få låset, vilket säkerställer rättvisa.
Frågan uppstår igen, vi vet att Zookeeper behöver distribueras i kluster, kommer det att uppstå problem med datasynkronisering som i Redis-kluster?
Zookeeper är en distribuerad komponent som garanterar svag konsistens, dvs. slutlig konsistens.
Zookeeper använder ett datasynkroniseringsprotokoll kallat Quorum Based Protocol. Om det finns N Zookeeper-servrar i Zookeeper-klustret (N är vanligtvis udda, 3 kan uppfylla datatillförlitlighet och har hög läs- och skrivprestanda, och 5 har bäst balans mellan datatillförlitlighet och läs- och skrivprestanda), så synkroniseras en skrivoperation av användaren först till N/2 + 1-servrar och återlämnas sedan till användaren, vilket uppmanar användaren att skriva framgångsrikt. Datasynkroniseringsprotokollet baserat på Quorum Based Protocol avgör den konsistens i styrkan som Zookeeper kan stödja.
I en distribuerad miljö är datalagring som uppfyller stark konsistens i princip obefintlig, och det kräver att alla noder uppdateras synkront när data från en nod uppdateras. Denna synkroniseringsstrategi finns i master-slave synkron replikeringsdatabas. Denna synkroniseringsstrategi har dock för stor påverkan på skrivprestandan och förekommer sällan i praktiken. Eftersom Zookeeper skriver N/2+1-noder synkront, och N/2 noder inte uppdateras synkront, är Zookeeper inte starkt konsekvent.
Användarens datauppdateringsoperation garanterar inte att efterföljande läsningar läser det uppdaterade värdet, men kommer så småningom att visa konsistens. Att offra konsistens betyder inte att helt ignorera datans konsistens, annars är datan kaotisk, så oavsett hur hög systemtillgängligheten är, oavsett hur bra fördelningen är, är den värdelös. Att offra konsistens är bara att stark konsistens i relationsdatabaser inte längre krävs, utan så länge systemet kan uppnå slutlig konsistens.
Om Zookeeper uppfyller kausal konsistens beror på hur klienten är programmerad.
Praktiker som inte uppfyller kausal konsistens
- Process A skriver en databit till Zookeepers /z och returnerar framgångsrikt
- Process A informerar process B om att A har modifierat datan i /z
- B läser data från Zookeepers /z
- Eftersom servern för Zookeeper som är ansluten till B kanske inte har uppdaterats med A:s skrivna data, kommer B inte att kunna läsa A:s skrivna data
Praktiker som uppfyller kausal konsistens
- Process B lyssnar efter dataförändringar i /z på Zookeeper
- Process A skriver en databit till Zookeepers /z, och innan den återvänder framgångsrikt måste Zookeeper anropa lyssnaren som är registrerad på /z, och ledaren meddelar B om dataändringen
- Efter att händelsesvarsmetoden för process B har svarats tar den de ändrade datana, så B kommer definitivt att kunna få det ändrade värdet
- Kausal konsistens här syftar på den kausala konsistensen mellan ledaren och B, det vill säga ledaren meddelar data om en förändring
Den andra händelselyssningsmekanismen är också metoden som bör användas för att korrekt programmera Zookeeper, så Zookeeper bör uppfylla den kausala konsistensen
Därför, när vi implementerar distribuerade lås baserade på Zookeeper, bör vi använda praxis att uppfylla kausal konsistens, det vill säga att trådarna som väntar på låset lyssnar på förändringarna i låset i Zookeeper, och när låset släpps kommer Zookeeper att meddela den väntande tråd som uppfyller villkoren för rättvist lås.
Du kan använda Zookeepers tredjepartsbiblioteksklient direkt, som kapslar in en reentrant lock-tjänst.
Distribuerade lås implementerade med ZK verkar passa exakt vad vi förväntade oss av ett distribuerat lås i början av denna artikel. Men det är det inte, och det distribuerade låset som implementeras av Zookeeper har faktiskt en nackdel, det vill säga att prestandan kanske inte är lika hög som för caching-tjänsten. För varje gång i processen att skapa och släppa ett lås måste omedelbara noder dynamiskt skapas och förstöras för att genomföra låsfunktionen. Att skapa och ta bort noder i ZK kan endast utföras via ledarservern, och sedan delas data med alla följedatorer.
Fördelar med att använda Zookeeper för att implementera distribuerade lås Effektivt lösa problem med enskilda punkter, icke-återinträdesproblem, icke-blockeringsproblem och låsfel att släppa. Det är relativt enkelt att implementera.
Nackdelar med att använda Zookeeper för att implementera distribuerade lås Prestandan är inte lika bra som att använda cache för att implementera distribuerade lås. En förståelse för principerna för ZK krävs.
Jämförelse av de tre alternativen
Ur ett lätt förståeligt perspektiv (från låg till hög) Database > Cache > Zookeeper
Ur ett perspektiv av implementeringskomplexitet (från låg till hög) Zookeeper > cache > databaser
Ur ett prestationsperspektiv (från högt till lågt) Caching > Zookeeper >= databas
Ur ett tillförlitlighetsperspektiv (från hög till låg) Zookeeper > cache > databaser
|