![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
Izgradnja pouzdanog, visoko dostupnog, skalabilnog distribuiranog sustava zahtijeva pridržavanje specifičnih tehnika, načela i obrazaca. Dizajn takvih sustava uključuje rješavanje bezbrojnih izazova. Među najrasprostranjenijim i najosnovnijim problemima je problem dvostrukog pisanja .
"Problem dvostrukog pisanja" izazov je koji se javlja u distribuiranim sustavima, uglavnom kada se radi o višestrukim izvorima podataka ili bazama podataka koje treba održavati sinkronizirane. Odnosi se na poteškoće u osiguravanju da se promjene podataka dosljedno zapisuju u različite pohrane podataka, kao što su baze podataka ili predmemorije, bez uvođenja problema poput nedosljednosti podataka, sukoba ili uskih grla u izvedbi.
Arhitektura mikroservisa i baza podataka uzoraka po usluzi donose vam mnoge prednosti, poput neovisne implementacije i skaliranja, izoliranih kvarova i potencijalnog povećanja brzine razvoja. Međutim, operacije zahtijevaju promjene među više mikroservisa, što vas tjera da razmislite o pouzdanom rješenju za rješavanje ovog problema.
Razmotrimo scenarij u kojem naša domena uključuje prihvaćanje zahtjeva za kredit, njihovu procjenu i zatim slanje upozorenja korisnicima.
U duhu načela jedinstvene odgovornosti, Conwayeva zakona i pristupu dizajna vođenom domenom, nakon nekoliko sesija prepunih događaja, cijela je domena podijeljena u tri poddomene s definiranim ograničenim kontekstima koji imaju jasne granice, modele domene i sveprisutni jezik.
Prvi ima zadatak uključivanja i prikupljanja novih zahtjeva za kredit. Drugi sustav ocjenjuje te prijave i donosi odluke na temelju dostavljenih podataka. Ovaj postupak procjene, uključujući KYC/KYB, provjere protiv prijevara i kreditnog rizika, može biti dugotrajan, zahtijevajući sposobnost istovremenog rukovanja tisućama aplikacija. Posljedično, ova je funkcionalnost delegirana namjenskom mikroservisu s vlastitom bazom podataka, što omogućuje neovisno skaliranje.
Nadalje, ovim podsustavima upravljaju dva različita tima, svaki sa svojim vlastitim ciklusima izdanja, ugovorima o razini usluge (SLA) i zahtjevima za skalabilnost.
Na kraju , postoji specijalizirana usluga obavijesti za slanje upozorenja korisnicima.
Evo pročišćenog opisa primarne upotrebe sustava:
To je na prvi pogled prilično jednostavan i primitivan sustav, ali zaronimo u to kako servis zahtjeva za kredit obrađuje naredbu podnošenja zahtjeva za kredit.
Možemo razmotriti dva pristupa za interakciju usluga:
First-Local-Commit-Then-Publish: U ovom pristupu usluga ažurira svoju lokalnu bazu podataka (commits) i zatim objavljuje događaj ili poruku drugim uslugama.
First-Publish-Then-Local-Commit: Suprotno tome, ova metoda uključuje objavljivanje događaja ili poruke prije predaje promjena u lokalnu bazu podataka.
Obje metode imaju svoje nedostatke i samo su djelomično sigurne za komunikaciju u distribuiranim sustavima.
Ovo je dijagram slijeda primjene prvog pristupa.
U ovom scenariju, Loan Application Service koristi pristup First-Local-Commit-Ten-Publish , gdje prvo obvezuje transakciju, a zatim pokušava poslati obavijest drugom sustavu. Međutim, ovaj je proces osjetljiv na neuspjeh ako, na primjer, postoje problemi s mrežom, usluga procjene nije dostupna ili usluga prijave za kredit naiđe na pogrešku Out of Memory (OOM) i ruši se. U takvim slučajevima, poruka bi se izgubila, ostavljajući Procjenu bez obavijesti o novom zahtjevu za kredit, osim ako se ne provedu dodatne mjere.
I ovaj drugi.
U scenariju First-Publish-Then-Local-Commit , usluga prijave za kredit suočava se sa značajnijim rizicima. Može obavijestiti Assessment Service o novoj aplikaciji, ali ne uspijeva lokalno spremiti ovo ažuriranje zbog problema kao što su problemi s bazom podataka, pogreške u memoriji ili greške koda. Ovaj pristup može dovesti do značajnih nedosljednosti u podacima, što bi moglo uzrokovati ozbiljne probleme, ovisno o tome kako Služba za reviziju kredita postupa s pristiglim zahtjevima.
Stoga moramo pronaći rješenje koje nudi robustan mehanizam za objavljivanje događaja vanjskim korisnicima. No, prije nego što uđemo u potencijalna rješenja, prvo bismo trebali razjasniti vrste jamstava isporuke poruka koje se mogu postići u distribuiranim sustavima.
Postoje četiri vrste jamstava koja možemo postići.
Nema garancija
Nema jamstva da će poruka biti isporučena na odredište. Pristup First-Local-Commit-Then-Publish govori upravo o tome. Potrošači mogu primiti poruke jednom, više puta ili nikada.
Isporuka najviše jednom
Dostava najviše jednom znači da će poruka biti isporučena na odredište najviše 1 put. Pristup First-Local-Commit-Then-Publish može se također implementirati na ovaj način s politikom ponovnog pokušaja pokušaja s vrijednošću jedan.
Najmanje jednom isporukom\Potrošači će primiti i obraditi svaku poruku, ali istu poruku mogu primiti više puta.
Isporuka točno jednom\Isporuka točno jednom znači da će potrošač primiti poruku učinkovito jednom.
Tehnički, to je moguće postići Kafkinim transakcijama i specifičnom idempotentnom implementacijom proizvođača i potrošača.
U većini slučajeva isporuka 'barem jednom' jamči rješavanje mnogih problema osiguravajući da se poruke isporučuju barem jednom, ali potrošači moraju biti idempotentni. Međutim, s obzirom na neizbježne mrežne kvarove, sva potrošačka logika mora biti idempotentna kako bi se izbjegla obrada dvostrukih poruka, bez obzira na jamstva proizvođača. Stoga ovaj zahtjev nije toliko nedostatak koliko odražava stvarnost.
Postoji mnogo rješenja za ovaj problem, koja imaju svoje prednosti i nedostatke.
Prema Wikipediji, Two-Phase Commit (2PC) je protokol distribuiranih transakcija koji se koristi u računalnim znanostima i sustavima upravljanja bazama podataka kako bi se osigurala dosljednost i pouzdanost distribuiranih transakcija. Dizajniran je za situacije u kojima višestruki resursi (npr. baze podataka) trebaju sudjelovati u jednoj transakciji i osigurava da ili svi izvrše transakciju ili je svi prekinu, čime se održava dosljednost podataka. Zvuči točno ono što nam treba, ali Two-Phase Commit ima nekoliko nedostataka:
Najočiglednije rješenje za arhitekturu mikroservisa je primjena uzorka (ili čak ponekad anti-uzorka) — zajedničke baze podataka. Ovaj je pristup vrlo intuitivan ako vam je potrebna dosljednost transakcija u više tablica u različitim bazama podataka, samo koristite jednu zajedničku bazu podataka za ove mikroservise.
Nedostaci ovog pristupa uključuju uvođenje jedne točke kvara, sprječavanje neovisnog skaliranja baze podataka i ograničavanje mogućnosti korištenja različitih rješenja baze podataka koja su najprikladnija za specifične zahtjeve i slučajeve upotrebe. Dodatno, modifikacije kodnih baza mikroservisa bile bi potrebne za podršku takvog oblika distribuirane transakcije.
' Transakcijski izlazni spremnik ' je obrazac dizajna koji se koristi u distribuiranim sustavima kako bi se osiguralo pouzdano širenje poruka, čak i u slučaju nepouzdanih sustava za slanje poruka. Uključuje pohranjivanje događaja u označenu tablicu 'OutboxEvents' unutar iste transakcije kao i sama operacija. Ovaj pristup dobro je usklađen s ACID svojstvima relacijskih baza podataka. Nasuprot tome, mnoge No-SQL baze podataka ne podržavaju u potpunosti ACID svojstva, već se umjesto toga odlučuju za načela CAP teorema i BASE filozofije, koji daju prednost dostupnosti i eventualnoj dosljednosti nad strogom dosljednošću.
Transakcijski izlazni spremnik pruža jamstvo barem jednom i može se implementirati s nekoliko pristupa:
Praćenje dnevnika transakcija
Izdavač anketa
Pristup praćenja dnevnika transakcija podrazumijeva korištenje rješenja specifičnih za bazu podataka kao što je CDC (Change Data Capture). Glavni nedostaci tog pristupa su:
Rješenja specifična za baze podataka
Povećana latencija zbog specifičnosti CDC implementacija
Druga metoda je Polling Publisher , koja olakšava rasterećenje izlazne pošte anketiranjem tablice izlazne pošte. Primarni nedostatak ovog pristupa je mogućnost povećanog opterećenja baze podataka, što može dovesti do većih troškova. Nadalje, ne podržavaju sve No-SQL baze podataka učinkovito postavljanje upita za određene segmente dokumenta. Izdvajanje cijelih dokumenata stoga može rezultirati smanjenjem performansi.
Ovdje je mali dijagram slijeda koji objašnjava kako to radi.
Primarni izazov s uzorkom Transactional Outbox leži u njegovoj ovisnosti o ACID svojstvima baze podataka. To bi moglo biti jednostavno u tipičnim OLTP bazama podataka, ali predstavlja izazov u NoSQL području. Kako bi se to riješilo, potencijalno rješenje je iskoristiti dnevnik dodavanja (na primjer, Kafka) odmah od pokretanja obrade zahtjeva.
Umjesto izravne obrade naredbe 'pošalji zahtjev za kredit', mi je odmah šaljemo internoj Kafkinoj temi i zatim klijentu vraćamo 'prihvaćen' rezultat. Međutim, budući da postoji velika vjerojatnost da naredbu još treba obraditi, ne možemo odmah obavijestiti korisnika o rezultatu. Kako bismo upravljali ovom konačnom dosljednošću, možemo upotrijebiti tehnike kao što su dugo ispitivanje, ispitivanje koje pokreće klijent, optimistična ažuriranja korisničkog sučelja ili korištenje WebSockets ili događaja poslanih s poslužitelja za obavijesti. Međutim, ovo je posve posebna tema, pa se vratimo našoj početnoj temi.
Poslali smo poruku na internu Kafkinu temu. Usluga prijave zajma zatim koristi ovu poruku — istu naredbu koju je primila od klijenta — i započinje obradu. Prvo, izvršava neku poslovnu logiku; tek nakon što je ova logika uspješno izvedena i rezultati su postojani, objavljuje nove poruke o javnoj Kafkinoj temi.
Pogledajmo malo pseudokoda.
public async Task HandleAsync(SubmitLoanApplicationCommand command, ...) { //First, process business logic var loanApplication = await _loanApplicationService.HandleCommandAsync(command, ...); //Then, send new events to public Kafka topic producer.Send(new LoanApplicationSubmittedEvent(loanApplication.Id)); //Then, commit offset consumer.Commit(); }
Što ako obrada poslovne logike ne uspije? Bez brige, budući da pomak još nije učinjen, poruka će se pokušati ponovno poslati.
Što ako slanje novih događaja Kafki ne uspije? Bez brige, budući da je poslovna logika idempotentna, neće stvoriti duplikat zahtjeva za kredit. Umjesto toga, pokušat će ponovno poslati poruku javnoj temi Kafka.
Što ako se poruke šalju Kafki, ali pomak ne uspije? Bez brige, budući da je poslovna logika idempotentna, neće stvoriti duplikat zahtjeva za kredit. Umjesto toga, ponovno će poslati poruke javnoj Kafkinoj temi i nadati se da će popravak ovog puta uspjeti.
Glavni nedostaci ovog pristupa uključuju dodatnu složenost povezanu s novim stilom programiranja, konačnu dosljednost (budući da klijent neće odmah znati rezultat) i zahtjev da sva poslovna logika bude idempotentna.
Što je izvor događaja i kako se ovdje može primijeniti? Izvor događaja je softverski arhitektonski obrazac koji se koristi za modeliranje stanja sustava hvatanjem svih promjena njegovih podataka kao niza nepromjenjivih događaja. Ti događaji predstavljaju činjenice ili promjene stanja i služe kao jedini izvor istine za trenutno stanje sustava. Dakle, tehnički, implementacijom sustava za pronalaženje događaja, već imamo sve događaje u EventStoreu, a potrošači mogu koristiti ovaj EventStore kao jedinstven izvor istine o tome što se dogodilo. Nema potrebe za posebnim rješenjem baze podataka za praćenje svih promjena ili nedoumica oko naručivanja, jedini problem je sjediti na strani čitanja budući da je za dobivanje stvarnog stanja entiteta potrebno ponoviti sve događaje.
U ovom smo članku pregledali nekoliko pristupa za izgradnju pouzdanog slanja poruka u distribuiranim sustavima. Postoji nekoliko preporuka koje bismo mogli uzeti u obzir prilikom izgradnje sustava s ovim karakteristikama
Sljedeći put ćemo pogledati praktičniji primjer implementacije transakcijskog izlaznog spremnika. Vidjeti
vas!