Verovatno najjasniji članak o ZooKeeper konceptima
Verovatno najjasniji članak o ZooKeeper konceptima
Priredio: Chenmo Wang Er, pogledajte link za preštampanje, autor: Yueban Feiyu, pogledajte originalni link.
1. Osnovni uvod
Apache ZooKeeper je nastao iz podprojekta Apache Hadoop i pruža efikasne i pouzdane usluge koordinacije za distribuirane aplikacije.
U rešavanju problema doslednosti distribuiranih podataka, ZK nije direktno usvojio Paxos algoritam, već ZAB (ZooKeeper Atomic Broadcast) protokol. ZK može da pruži funkcije poput objavljivanja/pretplate na podatke, balansiranja opterećenja, imenovanja servisa, distribuirane koordinacije/obaveštenja, upravljanja klasterom, izbora vođe (Master), distribuiranog zaključavanja, distribuiranih redova itd.
Ima sledeće karakteristike:
- Sekvencijalna doslednost: transakcioni zahtevi koje pokrene jedan klijent na kraju se primenjuju u ZooKeeper-u striktno prema redosledu pokretanja;
- Atomarnost: ili se primenjuje na sve ili na nijedan; ne postoji situacija u kojoj deo mašina primeni transakciju, a drugi deo ne;
- Jedinstveni prikaz: svi klijenti vide isti model podataka na serveru, bez obzira na to na koji ZK server je klijent povezan;
- Pouzdanost: kada server jednom uspešno primeni transakciju, promena koju ona izaziva ostaje dok je ne izmeni druga transakcija;
- Aktualnost: kada je transakcija uspešno primenjena, ZooKeeper garantuje da klijent može odmah da pročita najnovije stanje podataka nakon te promene (u određenom vremenskom periodu).
2. Model podataka
Model podataka u ZooKeeper-u je struktura u obliku stabla, veoma slična sistemu datoteka na računaru - postoji korenska fascikla, a ispod nje mnogo podfascikli.
Model ZooKeeper podataka takođe ima fiksni korenski čvor
(/), možemo da kreiramo podčvorove ispod korenskog čvora, a ispod podčvorova nastavljamo da kreiramo čvorove sledećeg nivoa.Svaki nivo u ZooKeeper stablu razdvojen je kosom crtom
(/), a ZooKeeper čvorovi se mogu pretraživati samo putem apsolutne putanje (npr.get /work/task), ne i putanjom relativnom.

Zašto ZooKeeper ne može da koristi relativne putanje za pronalaženje čvorova?
❝
Zato što je većina scenarija primene ZooKeeper-a lociranje čvorova na modelu podataka i izvođenje operacija nad relevantnim čvorovima.
❞
Problemi pronalaženja zapisa koji su jednaki zadatoj vrednosti najbolje se rešavaju heširanjem.
Zato ZooKeeper u osnovnoj implementaciji koristi hashtable, odnosno hashtableConcurrentHashMap<String, DataNode> nodes, gde je ključ kompletna putanja čvora za čuvanje podataka čvora.
To značajno povećava performanse ZooKeeper-a.
Tipovi čvorova
Čvorovi podataka u ZooKeeper-u dele se na tri tipa: trajni čvorovi, privremeni čvorovi i uređeni čvorovi:
❝
- Trajni čvor (persistent)
❞
Kada se čvor jednom kreira kao trajni, taj čvor podataka ostaje uskladišten na ZooKeeper serveru čak i kada se sesija između klijenta koji ga je kreirao i servera zatvori. Da bismo obrisali trajni čvor, moramo eksplicitno pozvati funkciju delete.
❝
- Privremeni čvor (ephemeral)
❞
Ako se čvor kreira kao privremeni, podaci tog čvora se ne čuvaju trajno na ZooKeeper serveru.
Kada sesija klijenta koji je kreirao privremeni čvor istekne ili se zatvori zbog izuzetka, taj čvor se brie sa ZooKeeper servera. Isto tako, možemo aktivno obrisati privremeni čvor na isti način kao i trajni.
U svakodnevnom razvoju možemo iskoristitu tu osobinu privremenih čvorova za statistiku rada mašina unutar klastera servera: klaster se postavi kao čvor /servers, i za svaki server u klasteru kreira se privremeni čvor /servers/host; kada server prestane sa radom taj čvor se automatski brie, a konačno prebrojavanjem privremenih čvorova saznajemo stanje rada u klasteru.
❝
- Uređeni čvor (sequential)
❞
Uređenost čvora znači da prilikom kreiranja uređenog čvora ZooKeeper server automatski koristi monoton rastući broj kao sufiks i dodaje ga na kraj imena čvora koji kreiramo.
Na primer, ako klijent kreira uređeni čvor sa putanjom works/task-, ZooKeeper će generisati redni broj i dodati ga na kraj putanje čvora, tako da je konačna putanja čvora works/task-1.
- Na taj način možemo direktno videti redosled kreiranja čvorova.
Svaki čvor u ZooKeeper-u održava sledeći sadržaj: binarni niz (bajt data[]) za čuvanje podataka čvora, ACL informacije za kontrolu pristupa, podatke o podčvorovima (pošto privremeni čvorovi ne mogu imati podčvorove, njihovo polje podčvorova je null); pored toga, svaki čvor podataka ima i polje stat koje beleži sopstvene informacije o stanju.
Struktura stanja čvora
Izvršavanjem stat /zk_test možemo videti da konzola ispisuje neke informacije - to su informacije o stanju čvora.
Svaki čvor ima svoje atribute stanja koji beleže informacije o samom čvoru:
| Atribut stanja | Opis |
|---|---|
| czxid | ID transakcije pri kreiranju čvora podataka |
| ctime | Vreme kreiranja čvora podataka |
| mzxid | ID transakcije poslednje izmene čvora podataka |
| mtime | Vreme poslednje izmene čvora podataka |
| pzxid | ID transakcije poslednje izmene podčvorova čvora podataka |
| cversion | Verzija podčvorova |
| version | Verzija podataka trenutnog čvora |
| aversion | Verzija ACL-a čvora |
| ephemeralOwner | Ako je čvor privremeni, predstavlja SessionID sesije koja ga je kreirala; ako je trajni čvor, vrednost ovog atributa je 0 |
| dataLength | Dužina sadržaja podataka |
| numChildren | Trenutni broj podčvorova čvora podataka |
Verzija čvora podataka
U ZooKeeper-u je uveden koncept verzije za čvorove podataka; svaki čvor podataka ima 3 tipa verzija, i svaka operacija ažuriranja čvora podataka izaziva promenu verzije.
Verzija u ZooKeeper-u predstavlja broj izmena nad sadržajem podataka čvora, informacijama o podčvorovima ili ACL informacijama.
3. Skladištenje podataka
Sa stanovišta lokacije skladištenja, transakcioni log i snimak podataka se čuvaju na lokalnom disku; sa poslovnog stanovišta, podaci u memoriji su podaci kojima direktno manipulišemo pri kreiranju čvorova podataka, dodavanju praćenja itd.
Transakcioni log se uglavnom koristi za beleženje lokalnih transakcionih operacija sesije i za sinhronizaciju podataka između servera u ZooKeeper klasteru.
Snimak transakcije trajno čuva memorijske podatke na lokalni disk.
❝ Važno je napomenuti da se snimak podataka čuva na lokalni disk u određenim vremenskim intervalima, pa podaci neće uvek biti u skladu sa podacima u memoriji. ❞ Kada jedan ZooKeeper server prestane sa radom zbog izuzetka, može doći do gubitka podataka.
Podaci u memoriji
Model podataka ZooKeeper-a može se posmatrati kao struktura stabla, gde su čvorovi podataka listovi tog stabla.
Sa stanovišta skladištenja podataka, model ZooKeeper podataka čuva se u memoriji.
Možemo model ZooKeeper podataka posmatrati kao bazu podataka u memoriji, koja ne samo da čuva informacije o čvorovima podataka, već i ACL informacije o pravima pristupa i stat informacije o stanju za svaki čvor.
U osnovnoj implementaciji model ZooKeeper podataka definisan je klasom DataTree. Klasa DataTree definiše memorijsku strukturu ZooKeeper podataka.
DataTree interno definiše tip čvora nodes, informacije o korenskom čvoru root i informacije o praćenju WatchManager za podčvorove.
Može se reći da klasa DataTree definiše logičku strukturu memorijskih podataka ZooKeeper-a.
Transakcioni log
Radi doslednosti podataka u celom ZooKeeper klasteru, Leader server šalje informacije o sinhronizaciji podataka ostalim serverskim ulogama u ZooKeeper klasteru, a po prijemu tih informacija Follow i Observer serveri u ZooKeeper klasteru vrše sinhronizaciju podataka.
❝ Informacije koje primaju ta dva tipa servera su transakcioni log Leader servera. ❞ Nakon prijema transakcionog loga, on se izvršava na lokalnom serveru. Taj način sinhronizacije podataka izbegava direktno korišćenje stvarnih poslovnih podataka, smanjuje mrežne troškove prenosa i poboljšava performanse celog ZooKeeper klastera.
4. Watch mehanizam
Klijent ZooKeeper-a može putem Watch mehanizma da se pretplati na obaveštenja koja se dobijaju kada se podaci ili stanje nekog čvora na serveru promene;
Kako se implementira:
Možemo to ostvariti prosleđivanjem Watcher parametra konstruktoru ZooKeeper klijenta:
new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)Gornji kod znači da je definisana instanca objekta ZooKeeper klijenta i da su joj prosleđena tri parametra:
connectString - adresa servera
sessionTimeout - vreme isteka
Watcher - događaj praćenja
Taj Watcher se, kao kontekst cele ZooKeeper sesije, trajno čuva u defaultWatcher ZKWatchManager-a na strani klijenta.
Pored toga, ZooKeeper klijent može putem tri interfejsa getData, exists i getChildren da registruje Watcher na ZooKeeper serveru, čime se Watch događaji lako dodaju u različitim situacijama:
getData(String path, Watcher watcher, Stat stat)Uslovi za okidanje obaveštenja:

Gornja slika navodi tipove događaja koje server podržava na čvorovima pri različitim stanjima sesije klijenta.
- Na primer, kada se klijent poveže na server, može se pratiti kreiranje, brisanje, izmena podataka i ažuriranje podčvorova čvora podataka.
Kada na nekom čvoru na serveru dođe do izmene podataka, da li će svi klijenti koji su postavili događaj praćenja za taj čvor dobiti obaveštenje od servera?
Odgovor je ne. Mehanizam okidanja Watch događaja zavisi od stanja veze sesije i tipa događaja koji je klijent registrovao, tako da će promena stanja klijentske sesije ili čvora podataka okinuti odgovarajući Watch događaj.
Implementacija scenarija objavljivanja/pretplate
❝
Kada se pomenu scenariji primene ZooKeeper-a, verovatno vam prvo pada na pamet najtipičnija funkcija objavljivanja i pretplate.
❞
Funkcija objavljivanja i pretplate može se posmatrati kao relacija jedan-prema-više, odnosno jedan izdavač servisa ili podataka može biti pozivan od više različitih potrošača.
Opšte uzevši, razmena podataka u modelu objavljivanja/pretplate može se podeliti na "povlačni" (pull) model, u kojem potrošač aktivno traži informacije od proizvođača, i "potisni" (push) model, u kojem proizvođač pri promeni podataka aktivno gura podatke potrošaču.
ZooKeeper je usvojio kombinaciju oba modela da bi implementirao funkciju objavljivanja/pretplate.
❝
Hajde da analiziramo konkretan primer:
❞
Tokom razvoja sistema koristi se raznovrsna konfiguracija, kao što su konfiguracione stavke baze podataka, interfejsi trećih strana, adrese servisa itd. Te konfiguracije se lako obavljaju u toku razvoja, ali njihovo konfigurisanje u velikom klasteru postaje komplikovano.
U takvom klasteru funkcija upravljanja konfiguracijom može automatski da održava konfiguracione informacije servera, a funkcija objavljivanja/pretplate ZooKeeper-a može rešiti taj problem.
Možemo uskladištiti informacije poput konfiguracionih stavki baze podataka u ZooKeeper čvor podataka.
Na primer /confs/data_item1.
Klijent serverskog klastera dodaje Watch događaj za taj čvor; kada se servis u klasteru pokrene, čita podatke tog čvora kako bi dobio konfiguracione informacije.
Kada se podaci tog čvora promene, ZooKeeper server šalje Watch događaj svakom klijentu; klijenti u klasteru, nakon što prime to obaveštenje, ponovo čitaju konfiguracione informacije baze podataka sa tog čvora.
Upotrebom Watch mehanizma implementirali smo funkciju upravljanja konfiguracijom u distribuiranom okruženju - dodavanjem događaja izmene podataka na čvor ZooKeeper servera postižemo da, po izmeni konfiguracionih stavki baze podataka, svi klijenti u klasteru prime obaveštenje o toj izmeni i preuzmu najnovije konfiguracione informacije.
❝
Treba obratiti pažnju na to da je Watch jednokratan, pa nakon što dobijemo obaveštenje od servera moramo ponovo dodati Watch događaj.
❞
5. Mehanizam sesije
Način rada ZooKeeper-a uglavnom se ostvaruje tako što klijent šalje zahteve serveru.
Tokom slanja zahteva klijent prvo mora uspostaviti vezu sa serverom, a jedna veza je jedna sesija.
❝
U ZooKeeper-u, sesija se može posmatrati kao struktura podataka Session koja predstavlja vezu između klijenta i servera.
❞
Ta struktura podataka sastoji se iz tri dela: ID sesije (sessionID), vreme isteka sesije (TimeOut) i status zatvaranja sesije (isClosing).
ID sesije: ID sesije služi kao identifikator sesije; kada kreiramo sesiju, ZooKeeper joj automatski dodeljuje jedinstveni ID.
Vreme isteka sesije: generalno, vreme isteka sesije je vreme od pokretanja sesije do njenog zatvaranja od strane servera. Nakon postavljanja vremena isteka, server uzima u obzir to vreme i konačno izračunava sopstveno vreme isteka. To vreme isteka je ono koje se zaista koristi za upravljanje korisničkim sesijama na serveru u ZooKeeper-u.
Status zatvaranja sesije: polje isClosing označava da li je sesija već zatvorena. Ako server utvrdi da je sesija istekla ili na drugi način prestala da važi, ZooKeeper će postaviti vrednost isClosing na "zatvoreno" i nakon toga neće izvoditi operacije nad tom sesijom.
Stanja sesije
Tokom rada ZooKeeper servisa, sesija prolazi kroz različite promene stanja.
Ta stanja uključuju:
❝
Povezivanje (CONNECTING), povezano (CONNECTIED), ponovno povezivanje (RECONNECTING), ponovo povezano (RECONNECTED), zatvaranje sesije (CLOSE) itd.
❞
Kada klijent započne kreiranje sesije sa serverom, njegovo stanje sesije prelazi u CONNECTING, a zatim klijent pokušava da se poveže sa IP adresama servera iz liste. Ako jedna IP adresa dozvoli povezivanje na server, stanje klijentske sesije prelazi u CONNECTIED.
Ako zbog mrežnih razloga dođe do prekida veze već povezane klijentske sesije, klijent ponovo pokušava da se poveže sa serverom, a odgovarajuće stanje klijentske sesije ponovo prelazi u CONNECTING, dok se sesija ponovo ne poveže na server i konačno pređe u CONNECTIED.
❝
Tokom celog rada ZooKeeper servisa, stanje sesije se često prebacuje između CONNECTING i CONNECTIED.
❞
Konačno, kada dođe do isteka vremena ili klijent aktivno izađe iz programa, stanje klijentske sesije prelazi u CLOSE.
Izuzeci sesije
U ZooKeeper-u, izuzeci isteka sesije uključuju klijentski readtimeout i serverski sessionTimeout.
- U svakodnevnom razvoju treba jasno razlikovati ta dva izuzeka: jedan se dešava na klijentu, a drugi na serveru.
Za programere koji nemaju dublje iskustvo sa ZooKeeper-om, česta zamka je u tome što, iako postave vreme isteka, ZooKeeper u stvarnom radu ne upravlja sesijom prema postavljenom vremenu.
- To je zato što vreme isteka koje ZooKeeper zaista primenjuje određuje se dogovorom klijenta i servera.
Kada ZooKeeper klijent uspostavlja vezu sa serverom, prosleđuje klijentski postavljeno vreme isteka sesije, a to vreme se poredi sa maksimalnim i minimalnim vremenom isteka postavljenim na serveru; ako se nalazi u dozvoljenom opsegu, koristi se klijentsko vreme za upravljanje sesijom.
Ako je veće ili manje od vremena isteka postavljenog na serveru, koristi se vrednost postavljena na serveru.
Strategija grupisanja u "kante" (bucket)
Da bi se u ZooKeeper-u održalo stanje sesije, klijent mora periodično slati poruke o održivosti (heartbeat) serveru.
- Poruka o održivosti koju klijent šalje može biti ping zahtev ili običan poslovni zahtev.
Nakon što ZooKeeper server primi zahtev, ažurira vreme isteka sesije kako bi održao sesiju aktivnom.
- Zato je u upravljanju sesijama ZooKeeper-a glavni zadatak upravljanje vremenom isteka sesija.
❝
ZooKeeper koristi poseban način upravljanja vremenom isteka sesija.
❞
U ZooKeeper-u sesije se dele prema različitim vremenskim intervalima, pa se sesije sa sličnim vremenom isteka smeštaju u isti interval. Time se izbegava provera svake sesije pojedinačno i umesto toga sesije upravljaju grupno.
To olakšava upravljanje sesijama jer obrada isteka u malim grupama poboljšava efikasnost obrade sesija.
Koje su prednosti ovakvog upravljanja sesijama u ZooKeeper-u?
Ta segmentna strategija upravljanja sesijama značajno poboljšava efikasnost izračunavanja isteka sesija. U realnom produkcionom okruženju, veliki distribuirani sistemi često imaju veliki obim pristupa.
Kao komponenta takvog sistema, ZooKeeper često opslužuje hiljade klijenata, pa mora upravljati i sa hiljadama sesija.
U takvoj situaciji nije pogodno upravljati i proveravati svaku sesiju pojedinačno, pa grupno upravljanje sesijama iz istog vremenskog perioda znatno povećava efikasnost rada servisa.
Osnovna implementacija
Ključna tačka osnovne implementacije ZooKeeper-a je struktura podataka red za istek (expiration queue). Sve operacije povezane sa istekom sesija vrše se oko tog reda.
- Može se reći da ZooKeeper u osnovi koristi tu strukturu reda za upravljanje istekom sesija.
Red za istek sesija sastoji se od više bucket-a.
Bucket je vremenski interval.
U ZooKeeper-u se vremenski intervali obično dele po jedinici expirationInterval, što je najmanja jedinica za podelu vremenskih intervala u strategiji grupisanja u kante ZooKeeper-a.
U ZooKeeper-u red za istek sastoji se od različitih bucket-a.
Svaki bucket sadrži sesije koje ističu u određenom vremenskom periodu.
Nakon što se sesije rasporede po različitim intervalima isteka u redu za istek, tokom rada ZooKeeper servisa konkretan proces izvršavanja je prikazan na donjoj slici.

Prvo, ZooKeeper servis pokreće nit koja je specijalno zadužena za pretraživanje reda za istek i pronalaženje bucket-a koji treba da istekne; ZooKeeper u svakom trenutku pušta da istekne samo jedan bucket sesija. Kada treba izvršiti operaciju isteka sesije, ZooKeeper budi nit u stanju mirovanja koja vrši operaciju isteka, a zatim se ponovo vrši pretraga reda za istek na gore opisani način; nakon vađenja sesija koje ističu, izvršava se operacija isteka.
6. ACL prava
ACL ZooKeeper-a može da postavi odgovarajuće informacije o pravima za znode-ove.
Jedno podešavanje ACL prava obično se deli u 3 dela: režim prava (Scheme), objekat ovlašćenja (ID) i informacija o pravima (Permission).
Konačno se formira ACL zahtev u formatu npr. scheme🆔permission. Režim prava: Scheme
Načini provere prava u ZooKeeper-u grubo se dele u dve kategorije: provera opsega i provera lozinke.
❝ Provera opsega ❞ Provera opsega znači da ZooKeeper može dodeliti određenu dozvolu jednoj IP adresi ili opsegu IP adresa.
Na primer, možemo dozvoliti mašini sa IP adresom 192.168.0.11 pravo upisa u određeni čvor podataka na serveru.
Ili možemo putem ip:192.168.0.11/22 dodeliti prava opsegu IP adresa.
❝ Provera lozinke ❞ Može se shvatiti kao način sa korisničkim imenom i lozinkom - to nam je najpoznatiji i najčešće korišćeni model u svakodnevnom životu, na primer kada uključujemo računar ili podižemo novac u banci moramo navesti odgovarajuću lozinku.
U ZooKeeper-u taj način provere je Digest autentifikacija. S obzirom da prenos preko mreže nije posebno siguran, nikada se ne šalje lozinka u otvorenom obliku preko mreže - to je jedan od važnih principa u projektovanju programa. Digest način autentifikacije najpre prosleđuje identifikator prava u obliku username:password sa klijentske strane, a zatim ZooKeeper server šifruje deo lozinke koristeći SHA-1 i BASE64 algoritme radi sigurnosti.
❝ Super režim prava ❞ Režim prava Super može se smatrati posebnim oblikom Digest autentifikacije.
Klijent sa Super pravima može izvršiti bilo koju operaciju nad bilo kojim čvorom podataka u ZooKeeper-u.
Donji kod prikazuje način pozivanja klijenta u Digest režimu.
// kreiranje čvora
create /digest_node1
// postavljanje digest provere prava
setAcl /digest_node1 digest:korisničko ime:lozinka u base64 formatu:rwadc
// upit ACL prava čvora
getAcl /digest_node1
// operacija ovlašćenja
addauth digest user:passwd❝ Ako je klijent postavio za čvor na serveru prava koja dozvoljavaju operaciju samo njemu, šta raditi kada taj klijent odstupi ili bude obrisan? ❞ Za izmenu čvora koji je on kreirao možemo putem "super režima", odnosno kao superadministrator, obrisati taj čvor ili promeniti njegov način provere prava.
Upravo zato što "super režim" ima toliko velika prava, pri svakodnevnoj upotrebi moramo biti posebno oprezni.
❝ world režim ❞ Ovaj režim ovlašćenja odgovara svim korisnicima u sistemu i suštinski ne igra nikakvu ulogu.
Kada se postavi world režim prava, operacije svih korisnika u sistemu mogu se izvršavati bez provere prava.
Objekat ovlašćenja (ID)
Objekat ovlašćenja je onaj kome dodeljujemo prava. Za različite režime prava važi: ako izaberemo IP način, objekat ovlašćenja može biti IP adresa ili opseg IP adresa; ako koristimo Digest ili Super način, odgovara korisničkom imenu.
Ako je World režim, ovlašćuje sve korisnike u sistemu.
Informacija o pravima (Permission)
Prava se odnose na vrste operacija koje možemo izvršiti na čvoru podataka. U ZooKeeper-u su definisane sledeće (5 vrsta) prava:
Pravo kreiranja čvora podataka (create): ovlašćeni objekat može da kreira podčvorove ispod čvora podataka;
Pravo ažuriranja čvora podataka (write): ovlašćeni objekat može da ažurira taj čvor podataka;
Pravo čitanja čvora podataka (read): ovlašćeni objekat može da čita sadržaj tog čvora i informacije o podčvorovima;
Pravo brisanja čvora podataka (delete): ovlašćeni objekat može da brie podčvorove tog čvora podataka;
Pravo administratora čvora podataka (admin): ovlašćeni objekat može da postavi ACL prava za taj čvor podataka.
❝ Važno je napomenuti da svaki čvor održava sopstvene ACL podatke o pravima; čak i podčvorovi tog čvora imaju sopstvena ACL prava, a ne nasleđuju direktno prava svog roditeljskog čvora. ❞ Implementacija sopstvene kontrole prava
Iako je sopstveni mehanizam kontrole prava u ZooKeeper-u veoma detaljan, ZooKeeper i dalje nudi mehanizam proširenja koji korisnicima omogućava da implementiraju sopstveni način kontrole prava.
U zvaničnoj dokumentaciji taj mehanizam je definisan kao Pluggable ZooKeeper Authenication - "plug-in" mehanizam za autentifikaciju; već iz imena se vidi njegova fleksibilnost. Kako se taj mehanizam implementira?
❝ Najvažnije za implementaciju sopstvenog mehanizma kontrole prava je implementacija interfejsa kontrolera prava AuthenticationProvider koji pruža ZooKeeper. ❞ Nakon implementacije sopstvenih prava, kako naterati ZooKeeper server da koristi sopstveni način provere prava?
Zatim je potrebno registrovati sopstvenu kontrolu prava na ZooKeeper server, a registracija se obično radi na dva načina.
Prvi je registracija sopstvenog kontrolera prava putem postavljanja sistemskih svojstava:
-Dzookeeper.authProvider.x=CustomAuthenticationProviderDrugi je konfiguracija u konfiguracionoj datoteci zoo.cfg:
authProvider.x=CustomAuthenticationProvider
Princip implementacije
Prvo se kapsulira tip tog zahteva, zatim se informacije o pravima kapsuliraju u request i šalju serveru. Implementacija na serveru je prilično složena: prvo se analizira da li je tip zahteva operacija povezana sa pravima, zatim se u zavisnosti od različitih režima prava (scheme) pozivaju različite klase implementacije za proveru prava i konačno se čuvaju informacije o pravima.
U interfejsu za ovlašćenje vredi napomenuti da se informacije o ovlašćenju sesije čuvaju u memoriji na strani ZooKeeper servera; ako se klijentska sesija zatvori, informacije o ovlašćenju se brišu.
Pri sledećem povezivanju na server potrebno je ponovo pozvati interfejs za ovlašćenje.
7. Način serijalizacije
U ZooKeeper-u nije usvojen isti način serijalizacije kao u Java-i, već je usvojeno Jute rešenje za serijalizaciju kao sopstveni način serijalizacije okvira ZooKeeper.
❝ ZooKeeper od samog početka koristi Jute kao rešenje za serijalizaciju, i to se nije promenilo ni u najnovijoj verziji. ❞ Iako ZooKeeper uvek koristi Jute okvir kao rešenje za serijalizaciju, to ne znači da je Jute boljih performansi od drugih okvira; naprotiv, okviri poput Apache Avro i Thrift su boljih performansi.
Razlog što ZooKeeper i dalje koristi Jute kao rešenje za serijalizaciju uglavnom leži u pitanjima kompatibilnosti starih i novih verzija.
Kako serijalizovati pomoću Jute
Ako želimo da serijalizujemo neku definisanu klasu, ta klasa prvo mora implementirati metode serilize i deserialize interfejsa Record - to su metode za serijalizaciju i deserijalizaciju.
❝ Donji kod prikazuje konkretnu implementaciju serijalizacije kakvu obično radimo u ZooKeeper-u: ❞ Prvo definišemo klasu test_jute; da bismo je mogli serijizovati, klasa test_jute mora implementirati Record interfejs, a u odgovarajućoj metodi serialize za serijalizaciju i deserialize za deserijalizaciju uređujemo konkretnu logiku implementacije.
class test_jute implements Record{
private long ids;
private String name;
...
public void serialize(OutpurArchive a_,String tag){
...
}
public void deserialize(INputArchive a_,String tag){
...
}
}U metodi serialize logika koju treba implementirati je sledeća: prvo se preko znakovnog parametra tag prosleđuje oznaka serijalizacije, a zatim se pomoću metoda poput writeLong i writeString serijalizuju polja svojstava objekta.
public void serialize(OutpurArchive a_,String tag) throws ...{
a_.startRecord(this.tag);
a_.writeLong(ids,"ids");
a_.writeString(type,"name");
a_.endRecord(this,tag);
}Poziv derseralize u procesu deserijalizacije je upravo suprotan procesu serijalizacije koji smo gore opisali.
public void deserialize(INputArchive a_,String tag) throws {
a_.startRecord(tag);
ids = a_.readLong("ids");
name = a_.readString("name");
a_.endRecord(tag);
}Način kodiranja logike serijalizacije i deserijalizacije je relativno fiksiran: prvo se preko startRecord započinje operacija serijalizacije, a zatim se putem writeLong, writeString ili readLong, readString izvršava serijalizacija ili deserijalizacija.
U ovom primeru implementirana je samo serijalizacija i deserijalizacija celobrojnog (long) i znakovnog tipa; pored toga, Jute okvir u ZooKeeper-u podržava i celobrojni tip (Int), bulov tip (Bool), tip dvostruke preciznosti (Double) i Byte/Buffer tip.
8. Klaster
Karakteristike klaster režima ZooKeeper-a
U ZooKeeper klasteru serveri se dele na tri uloge - "Leader, Follow, Observer"; tokom rada klastera ta tri tipa servera imaju različite zadatke:
Leader server upravlja ostalim serverima u klasteru i zadužen je za raspodelu i koordinaciju rada; može klijentu pružiti i uslugu upisa i uslugu čitanja.
Glavni zadatak Follow servera je izbor Leader servera; kada dođe do izbora Leader servera, sistem bira, na osnovu principa većine glasova između Follow servera, jedan Follow server kao novi Leader server; može pružiti samo uslugu čitanja.
Observer server je uglavnom zadužen za obradu zahteva klijenta poput preuzimanja podataka; ne učestvuje u izboru Leader servera, ne može biti izabran za Leader server i može pružiti samo uslugu čitanja.
Kada ZooKeeper klaster primi zahtev za sesijom od klijenta, prvo procenjuje da li je taj zahtev transakcioni.
❝ Za transakcioni zahtev sesije, server ZooKeeper klastera ga objedinjeno prosleđuje Leader serveru na obradu. Transakcioni zahtev je onaj nakon čijeg izvršenja od strane ZooKeeper servera dolazi do promene podataka ili stanja tog servera, što bi moglo dovesti do nedoslednosti podataka sa ostalim serverima u klasteru. ❞ Nakon što Leader server interno obradi taj transakcioni zahtev, sinhronizuje podatke sa ostalim serverskim ulogama, čime se garantuje redosled izvršenja transakcionih zahteva za sesiju, a time i doslednost podataka celog ZooKeeper klastera.
❝ U internoj implementaciji ZooKeeper klastera, kojim se metodom garantuje da će svi transakcioni zahtevi sesije koje primi ZooKeeper klaster biti prosleđeni Leader serveru na obradu? ❞ U ZooKeeper klasteru, nakon što serveri uloga koji nisu Leader prime transakcioni zahtev sesije od klijenta, oni moraju proslediti taj zahtev Leader serveru na obradu.
Follow i Observer serveri u ZooKeeper klasteru proveravaju da li je primljeni zahtev sesije transakcioni; ako jeste, prosleđuju ga Leader serveru kao poruku tipa REQUEST.
Nakon što server u ZooKeeper klasteru primi tu poruku, vrši njenu analizu.
Iz poruke se izdvaja originalni zahtev klijentske sesije koji ona sadrži.
Zatim se ta poruka prosleđuje u sopstveni lanac obrade zahteva Leader servera i započinje obrada transakcionog zahteva sesije.
Ako nije transakcioni zahtev, ZooKeeper klaster ga prepusta na obradu Follow i Observer serverima, npr. upit za informacije o čvoru podataka.
Kada poslovni scenario ima više operacija čitanja, a malo transakcionih operacija poput kreiranja i brisanja, performanse ZooKeeper klastera su veoma dobre.
❝ U ekstremnom slučaju, kada ZooKeeper klaster ima samo transakcione zahteve sesija bez operacija čitanja, Follow i Observer serveri mogu da služe samo kao serveri za prosleđivanje zahteva, a sav pritisak obrade sesija pada na Leader server. ❞ U pogledu performansi, usko grlo celog klastera zavisi od performansi Leader servera.
❝ Uloga ZooKeeper klastera može samo da garantuje da, kada Leader čvor padne, ponovnim izborom Leader servera obezbedi stabilnost sistema. ❞ To je i jedna od mana dizajna ZooKeeper-a.
Izbor vođe (Leader)
Izbor Leader servera uglavnom se dešava u dve situacije.
Prva je pri pokretanju ZooKeeper klaster servisa, a druga kada stari Leader server u ZooKeeper klasteru otkaže, kada ZooKeeper klaster mora da izabere novog Leader servera.
❝ U procesu ponovnog izbora Leader-a u ZooKeeper klasteru učestvuju samo Follow serveri. ❞ ❝ Stanje servera ❞ Server ima četiri stanja: LOOKING, FOLLOWING, LEADING, OBSERVING.
LOOKING: stanje traženja Leader-a. Kada je server u tom stanju, on smatra da u trenutnom klasteru nema Leader-a, pa je potrebno preći u stanje izbora Leader-a.
FOLLOWING: stanje pratioca. Označava da je trenutna uloga servera Follower.
LEADING: stanje vođe. Označava da je trenutna uloga servera Leader.
OBSERVING: stanje posmatrača. Označava da je trenutna uloga servera Observer.
ID transakcije (zxid)
Svaka promena stanja u ZooKeeper-u obeležena je ZooKeeper ID-om transakcije (ZXID).
❝ Upis u ZooKeeper dovodi do promene stanja, i svaki upis izaziva promenu ZXID-a. ❞ ZXID dodeljuje Leader, globalno je jedinstven, dužine 64 bita i rastući.
ZXID prikazuje redosled svih promena stanja u ZooKeeper-u; svaka promena ima jedinstven ZXID, i ako je zxid1 manji od zxid2, to znači da se transakcija zxid1 desila pre transakcije zxid2.
Proces izbora
Tokom ponovnog izbora Leader čvora u ZooKeeper klasteru proces se uglavnom može podeliti na: otkrivanje pada Leader-a, ponovni izbor Leader-a, promenu uloge Follow servera i sinhronizaciju klastera.
❝ Otkrivanje pada Leader-a ❞ U ZooKeeper klasteru, kada Leader server otkaže, ZooKeeper klaster ponovo bira novog Leader servera.
Način detekcije da li je Leader server živ u ZooKeeper klasteru veoma je sličan metodu održavanja aktivnosti klijenta. Prvo, Follow serveri periodično šalju mrežni zahtev Leader serveru; po prijemu zahteva, Leader server vraća paket sa odgovorom Follow serveru; nakon što Follow server primi odgovor Leader servera, ako proceni da Leader server radi normalno, nastavlja sa sinhronizacijom podataka i prosleđivanjem servisa; u suprotnom se prelazi na ponovni izbor Leader servera.
❝ Ponovni izbor Leader-a ❞ Kada Follow server pošalje paket sa zahtevom za status Leader serveru i ne dobije povratnu informaciju od Leader servera, tada - ako individualni Follow serveri u klasteru uoče grešku u odgovoru - to neće dovesti do toga da ZooKeeper klaster odmah ponovo izabere Leader servera; umesto toga Follow server menja svoje stanje u LOOKING i pokreće glasanje na mreži. Kada u ZooKeeper klasteru sve više mašina pokrene glasanje, a konačni rezultat glasanja zadovolji princip većine -
ZooKeeper ponovo bira Leader servera.
❝ Promena uloge Follow servera ❞ U ZooKeeper klasteru, Follow server je kandidat za Leader servera; nakon što bude izabran za Leader servera, njegova uloga Follow u ZooKeeper klasteru se takođe menja - odnosno transformiše se u Leader servera i kao Leader server u ZooKeeper klasteru pruža servis prema spolja.
❝ Sinhronizacija podataka u klasteru ❞ Nakon što ZooKeeper klaster uspešno izabere Leader servera i kandidat Follow server promeni ulogu -
da bi se izbegli problemi nedoslednosti podataka koji bi mogli nastati u tom periodu, ZooKeeper klaster pre početka pružanja servisa vrši sinhronizaciju ostalih serverskih uloga pod upravljanjem Leader servera.
Osnovna implementacija
Prvo, ZooKeeper klaster procenjuje da li je Leader server otkazao - način provere je da Follow server pošalje paket zahteva Leader serveru, a zatim, po prijemu podataka sa odgovorom, Follow server ih raščlanjuje i na osnovu vraćenih podataka procenjuje stanje rada Leader servera; ako je povratna vrednost ključna reč LOOKING, to znači da sa Leader serverom u klasteru nije moguća normalna komunikacija.
Zatim, pri izboru Leader servera u ZooKeeper klasteru to se ostvaruje preko klase FastLeaderElection. Ta klasa implementira TCP komunikacionu vezu i služi za koordinaciju i komunikaciju sa ostalim Follow serverima u ZooKeeper klasteru.
Klasa FastLeaderElection nasleđuje Election interfejs i definisana je kao klasa implementacije za izbor.
U njoj su definisani i neki konfiguracioni parametri povezani sa komunikacijom pri izboru, kao što su finalizeWait - konačno vreme čekanja, maxNotificationInterval - maksimalni interval obaveštenja itd. Tokom izbora se prvo poziva funkcija ToSend koja šalje informacije o glasanju sopstvene mašine ostalim serverskim ulogama u ZooKeeper klasteru; ostali serveri, nakon prijema informacija o glasanju, vrše proveru valjanosti itd., a zatim ZooKeeper klaster prebrojava informacije o glasanju; ako više od polovine mašina ima usaglašene glasove, klaster bira novog Leader servera.
❝ Ovde treba obratiti pažnju na jedan problem: tokom ponovnog izbora Leader servera, ZooKeeper klaster teorijski ne može da obrađuje transakcione zahteve. ❞ Zbog toga transakcione sesije poslate u ZooKeeper klaster ostaju na čekanju i privremeno se ne izvršavaju; izvršavaju se tek nakon izbora novog Leader servera.
Observer
Tokom rada ZooKeeper klaster servisa, Observer server i Follow server imaju istu funkciju - zaduženi su za obradu netransakcionih zahteva sesija od klijenta, poput upita za podatke čvorova.
Za razliku od Follow servera, Observer ne učestvuje u izboru Leader servera i ne može biti izabran za Leader servera. U ranijem radu ZooKeeper klaster servisa postojali su samo Leader server i Follow server.
Međutim, sa širom primenom ZooKeeper-a u distribuiranim okruženjima pojavile su se i mane ranijeg modela, uglavnom:
Sa povećanjem veličine klastera, performanse obrade upisa opadaju.
ZooKeeper klaster ne može da se postavi preko više regiona (cross-datacenter).
Glavni problem je u tome što, kada se veličina ZooKeeper klastera poveća i broj Follow servera u klasteru postepeno raste, performanse ZooKeeper-a pri obradi transakcionih zahteva kao što je kreiranje čvorova podataka postepeno opadaju.
To je zato što ZooKeeper klaster pri obradi transakcionih zahteva mora da pokrene glasanje za taj transakcioni zahtev u klasteru; tek kada više od polovine Follow servera glasa usaglašeno, ta operacija upisa se izvršava.
Upravo zbog toga, sa sve većim brojem Follow servera u klasteru, glasanje za jednu operaciju upisa i slično postaje sve složenije, a mrežna komunikacija između Follow servera postaje sve vremenski zahtevnija. Posledica je da se sa postepenim povećanjem broja Follow servera performanse transakcione obrade sve više smanjuju.
Da bi se rešio taj problem, nakon verzije ZooKeeper 3.6 uvedena je nova serverska uloga u ZooKeeper klasteru - Observer (server posmatrač). Observer može da obrađuje netransakcione zahteve u ZooKeeper klasteru i ne učestvuje u operacijama glasanja poput izbora Leader čvora.
Na taj način se istovremeno obezbeđuje skalabilnost performansi ZooKeeper klastera i izbegava uticaj previše servera koji učestvuju u glasanju na sposobnost ZooKeeper klastera da obrađuje transakcione zahteve sesija.
U realnom postavljanju, pošto Observer ne učestvuje u operacijama poput izbora Leader čvora, on ne komunicira sa Leader serverom toliko često kao Follow server. Zbog toga Observer serveri mogu biti raspoređeni u različitim mrežnim regionima, bez uticaja na performanse celog ZooKeeper klastera - to je takozvano postavljanje preko više regiona (cross-datacenter deployment).
Pri svakodnevnoj upotrebi ZooKeeper klaster servera, da li broj mašina u klasteru treba da bude neparan?
Dva razloga:
❝ Pri istoj sposobnosti tolerancije na greške, neparan broj štedi resurse. ❞ Leader algoritam izbora u ZooKeeper-u koristi Zab protokol.
Osnovna ideja Zab-a je: kada većina Server-a uspešno zapiše, smatra se da je upis uspeo.
Dva primera:
Ako ZooKeeper klaster 1 ima 3 čvora, 3/2=1.5, odnosno da bi ZooKeeper normalno pružao servis (odnosno da bi izbor vođe bio uspešan) potrebno je da najmanje 2 čvora budu normalna. Drugim rečima, ZooKeeper klaster od 3 čvora dozvoljava pad jednog čvora.
Ako ZooKeeper klaster 2 ima 4 čvora, 4/2=2, odnosno da bi ZooKeeper normalno pružao servis potrebno je da najmanje 3 čvora budu normalna. Drugim rečima, ZooKeeper klaster od 4 čvora takođe dozvoljava pad jednog čvora.
I klaster 1 i klaster 2 imaju istu sposobnost tolerancije na greške - dozvoljavaju pad 1 čvora - ali klaster 2 ima jedan čvor više od klastera 1. Pri istoj sposobnosti tolerancije na greške, u skladu sa principom štednje resursa bolje je da broj čvorova u ZooKeeper klasteru bude neparan.
❝ Sprečavanje nedostupnosti klastera izazvane "split-brain" (podeljenim mozgom). ❞ "Split-brain" se obično dešava kada komunikacija između čvorova nije moguća - klaster se deli u više manjih klastera, a svaki mali klaster bira svog master čvor, što dovodi do situacije u kojoj prvobitni klaster ima više master čvorova; to je "split-brain".
Evo primera zašto neparan broj čvorova može sprečiti nedostupnost servisa izazvanu "split-brain"-om:
Ako ZooKeeper klaster ima 5 čvorova i dođe do "split-brain"-a u dva mala klastera A i B:
A: 1 čvor, B: 4 čvora
A: 2 čvora, B: 3 čvora
Vidi se da u oba slučaja jedan od manjih klastera A ili B uvek ispunjava uslov: broj dostupnih čvorova > ukupan broj čvorova / 2.
Zato ZooKeeper klaster i dalje može da izabere leader-a i da pruža servis - samo je deo čvorova pao.
Ako ZooKeeper klaster ima 4 čvora i na isti način dođe do "split-brain"-a u dva mala klastera A i B:
A: 1 čvor, B: 3 čvora
A: 2 čvora, B: 2 čvora
Pošto i A i B imaju po 2 čvora, nijedan ne ispunjava uslov za izbor: broj dostupnih čvorova > ukupan broj čvorova / 2, pa u tom trenutku ZooKeeper uopšte ne može da pruži servis.
9. ZAB protokol
Algoritam ZAB protokola
Najznačajnija uloga ZooKeeper-a je da garantuje doslednost podataka u distribuiranom sistemu; prilikom obrade zahteva sesija od klijenta, kao i pri ponovnom izboru Leader čvora u klasteru, mogu nastati situacije nedoslednosti podataka.
❝
Da bi se rešio taj problem, ZooKeeper je usvojio algoritam ZAB protokola.
❞
Algoritam ZAB protokola (Zookeeper Atomic Broadcast - protokol atomske emisije Zookeeper-a) je algoritam koji je ZooKeeper posebno dizajnirao za rešavanje problema konačne doslednosti u klasteru; njegove dve ključne funkcije su oporavak od pada (crash recovery) i protokol atomske emisije.
- U celokupnoj osnovnoj implementaciji ZAB protokola, ZooKeeper klaster uglavnom usvaja arhitekturu sistema tipa master-slave kako bi garantovao doslednost sistema ZooKeeper klastera.
Kada se primi transakcioni zahtev sesije od klijenta, sistem klastera koristi glavni server za obradu tog zahteva; rezultat obrađen na glavnom serveru zatim se putem mreže šalje ostalim potčinjenim serverskim čvorovima u klasteru radi sinhronizacije podataka.
❝
Na primeru ZooKeeper klastera taj proces se može sumirati ovako:
❞
Kada ZooKeeper klaster primi transakcioni zahtev sesije od klijenta, Follow serveri u klasteru prosleđuju taj zahtev Leader serveru na obradu.
Nakon što Leader čvor obradi taj zahtev sesije, rezultat se putem loga operacija sinhronizuje sa Follow serverima u klasteru.
Zatim Follow serveri na osnovu primljenog loga operacija lokalno izvršavaju obradu podataka, i konačno se završava obrada klijentske sesije od strane celog ZooKeeper klastera.
Oporavak od pada
Kada Leader u klasteru otkaže, ceo klaster zbog nedostatka Leader servera ne može da obrađuje transakcione zahteve sesija od klijenta.
❝
Zato, da bi se rešio taj problem, u ZAB protokolu je predviđen mehanizam oporavka od pada.
❞
Mehanizam oporavka od pada ključan je za visoku dostupnost usluge ZooKeeper klastera. Događaj koji okida ZooKeeper klaster da izvrši oporavak od pada je situacija u kojoj Leader čvor u klasteru otkaže i ne može da radi, pa Follow serveri glasanjem odlučuju da li da izaberu novog Leader čvora.
❝
Proces glasanja je sledeći:
❞
Kada se pokrene mehanizam oporavka od pada, svaki Follow server u ZooKeeper klasteru pokreće glasanje i sinhronizuje ga sa ostalim Follow serverima u klasteru.
Nakon prijema informacija o glasanju od ostalih Follow servera u klasteru, svaki Follow server ih poredi sa svojim sopstvenim informacijama o glasanju; ako proceni da su nove informacije o glasanju prikladnije, usvaja ih kao svoje. Dok informacije o glasanju u klasteru ne dostignu većinu, vrši se novi krug glasanja; konačno, kada više od polovine Follow servera u ZooKeeper klasteru da isti rezultat, bira se novi Leader server.
❝
Struktura glasa:
❞
U implementaciji Fast Leader Election izbora, kao što je prikazano na donjoj slici, celokupni rezultat glasa može se podeliti u šest delova:

logicClock: beleži rundu glasanja servera. logicClock počinje od 1 i svaki put kada taj server prođe kroz rundu glasanja, vrednost logicClock se uvećava za 1.
state: označava trenutno stanje servera. Server u ZooKeeper klasteru ima četiri stanja: LOOKING, FOLLOWING, LEADERING, OBSERVING.
self_id: predstavlja informaciju o ID-u trenutnog servera; to polje se u ZooKeeper klasteru uglavnom koristi kao identifikator identiteta servera.self_zxid: najveći ID transakcije podataka sačuvanih na trenutnom serveru; broji se od 0.vote_id: jedinstveni ID servera koji se preporučuje glasanjem.vote_zxid: najveći ID transakcije podataka sačuvanih na preporučenom serveru; broji se od 0.
Kada ZooKeeper klaster treba da ponovo izabere novog Leader servera, na osnovu gore navedenih informacija o glasanju vrši poređenje kako bi pronašao najprikladniji server.
❝
Selekcija glasova
❞
Nakon što Follow server primi informacije o glasanju drugih Follow servera u mreži, kako ih poredi da bi ažurirao svoje informacije o glasanju?
Proces poređenja glasova Follow servera prikazan je na donjoj slici.

Prvo se poredi logicClock - runda glasanja servera; kada je logicClock isti, to znači da su dva glasa u istoj fazi glasanja, pa se prelazi na sledeću fazu, u suprotnom se preskače.
Zatim se poredi vote_zxid - informacija o ID-u izabranog servera; ako je vote_zxid u primljenom spoljnom glasanju veći, onda se u sopstvenom glasu vote_zxid i vote_myid ažuriraju na vote_zxid i vote_myid iz primljenog glasa i emituju se.
Ako je rezultat poređenja isti, nastavlja se sa poređenjem vote_myid - najveći ID transakcije sačuvan na izabranom serveru; ako je vote_myid spoljnog glasa veći, onda se u sopstvenom glasu vote_myid ažurira na vote_myid iz primljenog glasa.
Nakon tih poređenja i zamena taj Follow server konačno generiše nove informacije o glasanju i šalje ih u ZooKeeper klaster u sledećoj rundi glasanja.
Emitovanje poruka
Nakon što Leader čvor obradi zahtev, potrebno je obavestiti ostale serverske uloge u klasteru radi sinhronizacije podataka. ZooKeeper klaster koristi način emitovanja poruka za slanje obaveštenja.
ZooKeeper klaster koristi protokol atomske emisije za slanje poruka; proces osnovne implementacije tog protokola veoma je sličan procesu dvofaznog izvršenja (two-phase commit), kao što je prikazano na slici.

Kada treba izvršiti sinhronizaciju podataka sa ostalim serverskim ulogama u klasteru, Leader server kapsulira taj proces operacije u Proposal (predlog transakcije) i šalje ga ostalim serverima u klasteru kojima je potrebna sinhronizacija podataka.
Kada ti serveri prime transakciju sinhronizacije podataka od Leader servera, oni Leader serveru vraćaju povratnu informaciju o tome da li se ta transakcija može normalno izvršiti lokalno; Leader server, po prijemu povratnih informacija od ostalih Follow servera, vrši statistiku i procenjuje da li da izvrši tu operaciju transakcije u klasteru.
Obratite pažnju ovde: za razliku od procesa dvofaznog izvršenja (gde svi serveri u klasteru moraju da vrate da mogu izvršiti transakciju, pa glavni server ponovo šalje commit zahtev za izvršenje izmene podataka), algoritam ZAB protokola izostavlja logiku prekida - kada više od polovine Follow servera u ZooKeeper klasteru može normalno da izvrši operaciju transakcije, ceo ZooKeeper klaster može da izvrši commit Proposal transakcije.
10. Čišćenje logova
Tipovi logova
Tokom rada ZooKeeper servisa obično nastaju snimci podataka i datoteke logova; snimci podataka služe za sinhronizaciju podataka u klaster servisu, dok logovi podataka beleže relevantne informacije o stanju rada ZooKeeper servisa.
❝ Logovi podataka su datoteke koje u produkcionom okruženju moramo redovno održavati i upravljati njima. ❞ Rešenje za čišćenje
Kao što je gore opisano, za logove koji nastaju u produkcionom sistemu uobičajene operacije održavanja su backup i čišćenje.
Backup služi za kasnije analize stanja rada sistema i optimizaciju, dok je čišćenje uglavnom potrebno zato što sa porastom sistemskih logova oni postepeno zauzimaju prostor za skladištenje; ako se nikada ne čiste, mogu iscrpiti diskovni prostor sistema i konačno uticati na rad servisa.
Alatke za čišćenje
❝ Corntab ❞ Prvo predstavljamo Linux corntab - to je softver u Linux sistemu koji automatski, prema vremenu koje mi postavimo, periodično izvršava relevantne skripte koje napišemo.
Način rada sa crontab skriptom je relativno fleksibilan: možemo postaviti način održavanja logova prema poslovnim potrebama - na primer, ako želimo da redovno čistimo logove rada ZooKeeper servisa, a ne želimo da brišemo datoteke snimaka podataka, to možemo postići pomoću skripte, čime vršimo čišćenje samo datoteka logova podataka.
❝ PurgeTxnLog ❞ ZooKeeper sam pruža i klasu alatki PurgeTxnLog za čišćenje datoteka snimaka podataka (snapshot) i sistemskih logova.
Način čišćenja PurgeTxnLog veoma je sličan gore opisanom - takođe se izvršava putem tajmer skripte; jedina razlika je u tome što smo gore, prilikom pisanja skripte za čišćenje logova logsCleanWeek, koristili izvornu shell skriptu i ručno pisali logiku čišćenja logova podataka, dok se upotrebom PurgeTxnLog pri pisanju skripte za čišćenje može pozvati klasa alatki koju pruža ZooKeeper i tako obaviti čišćenje logova.
Kao što je prikazano u donjem kodu, prvo u direktorijumu /usr/bin kreiramo skriptu PurgeLogsClean. Napomena: ta skripta je takođe shell datoteka.
U skripti je potrebno napisati samo pozivni program klase PurgeTxnLog i sistem će automatski putem klase alatki PurgeTxnLog za nas obaviti čišćenje odgovarajućih datoteka logova.
#!/bin/sh
java -cp "$CLASSPATH" org.apache.zookeeper.server.PurgeTxnLog
echo "Čišćenje završeno"U poređenju sa crontab, PurgeTxnLog je lakši za upotrebu, stabilniji i sigurniji, ali je crontab fleksibilniji - možemo pisati sopstvenu logiku čišćenja prema različitim poslovnim potrebama.
11. Implementacija distribuiranog zaključavanja
Cilj distribuiranog zaključavanja je da u klasteru distribuirano postavljenih aplikacija, kada više servisa zahteva istu metodu ili istu poslovnu operaciju, odgovarajuća poslovna logika bude izvršena samo na jednoj niti na jednoj mašini, čime se izbegavaju problemi istovremenosti.
❝
Trenutno postoje tri popularna rešenja za implementaciju distribuiranog zaključavanja: na osnovu baze podataka, Redis-a i ZooKeeper-a.
❞
Rešenje 1:
Korišćenjem oblasti za čuvanje podataka u čvoru: veličina podataka koje čvor može da čuva u ZK-u ne sme preći 1M, ali za čuvanje jednog identifikatora je dovoljno. Kada nit želi da dobije zaključavanje, prvo proverava da li je identifikator "slobodan"; ako jeste, menja ga u "zauzet", a po završetku upotrebe vraća ga u "slobodan".
Rešenje 2:
Korišćenjem podčvorova: svaki put kada neka nit zatraži zaključavanje, kreira podčvor ispod čvora zaključavanja; tip podčvora mora održavati redosled, zatim se vrši sortiranje po samorastućem rednom broju podčvorova, i podrazumevano zaključavanje uvek dobija nit koja odgovara podčvoru sa najmanjim rednim brojem; pri otpuštanju zaključavanja dovoljno je obrisati odgovarajući podčvor.

Rizik od mrtvog zaključavanja (deadlock):
Oba rešenja su izvodljiva, ali pri upotrebi zaključavanja obavezno treba izbeći mrtvo zaključavanje.
Rešenje 1 na prvi pogled izgleda u redu - pri upotrebi se postavlja identifikator, a po završetku se brie - ali ako se niti koja drži zaključavanje dogodi neočekivani događaj, kod za otpuštanje zaključavanja se ne može izvršiti; tada se zaključavanje ne može otpustiti, ostale niti će beskonačno čekati, a relevantan kod za sinhronizaciju se ne može izvršiti.
Rešenje 2 ima isti problem, ali rešenje 2 može iskoristiti ZK privremene uređene čvorove (ephemeral sequential) da ga reši: čim se niti dogodi izuzetak koji prekine program, veza sa ZK se gubi; kada ZK detektuje prekid te veze, automatski brie privremeni čvor koji je ta veza kreirala. Na taj način se postiže da, čak i ako se programu niti koja drži zaključavanje dogodi neočekivani događaj, zaključavanje ipak bude normalno otpušteno.
Izbegavanje efekta stada
Zahtevi za zaključavanje ređaju se prema broju sufiksa; onaj sa manjim sufiksom prvi dobija zaključavanje.
Ako svi koji traže zaključavanje "watch"-uju držaoca zaključavanja, kada se znode koji predstavlja onoga ko traži zaključavanje obriše, obavešteni su svi koji traže zaključavanje, ali samo jedan dobija zaključavanje. To je efekat stada.
❝
Da bi se izbegao efekat stada, svako ko traži zaključavanje "watch"-uje onog ispred sebe u redu.
❞
Svaki put kada se zaključavanje otpusti, samo jedan koji traži zaključavanje će biti obavešten.
To dodatno čini dodelu zaključavanja pravednom - dodela zaključavanja prati princip "ko prvi dođe, prvi dobija".

Tok algoritma za implementaciju distribuiranog zaključavanja pomoću ZooKeeper-a, korenski čvor je /lock:
Klijent se povezuje na ZooKeeper i kreira privremeni uređeni podčvor ispod
/lock; podčvor koji odgovara prvom klijentu je/lock/lock01/00000001, a drugom/lock/lock01/00000002;Ostali klijenti preuzimaju listu podčvorova ispod
/lock01i procenjuju da li je podčvor koji su kreirali onaj sa najmanjim rednim brojem u trenutnoj listi;Ako jeste, smatraju da su dobili zaključavanje i izvršavaju poslovni kod; u suprotnom, preko watch događaja prate poruku o izmeni podčvorova
/lock01, a nakon što prime obaveštenje o izmeni ponavljaju ovaj korak dok ne dobiju zaključavanje;Po završetku poslovnog procesa, brišu odgovarajući podčvor i otpuštaju distribuirano zaključavanje;
U realnom razvoju možete iskoristiti Apache Curator za brzu implementaciju distribuiranog zaključavanja - Curator je ZooKeeper klijent koji je kompanija Netflix otvorila kao open-source i koji apstrahuje i obuhvata izvorni API ZooKeeper-a.
12. Implementacija distribuiranog ID-a
Možemo, putem sopstvenog režima rada klijenta i servera ZooKeeper-a, implementirati proces zahtevanja i distribucije ID-a u distribuiranom mrežnom okruženju.
❝ Svaki poslovni server kojem je potreban ID može se posmatrati kao klijent ZooKeeper-a. Generator ID može biti server ZooKeeper-a. ❞ Klijent šalje zahtev ZooKeeper serveru kako bi dobio informaciju o kodu; server po prijemu zahteva šalje ID klijentu.
Princip implementacije:
Može se iskoristiti uređeni čvor (sequential node) iz modela podataka ZooKeeper-a kao ID.
Klijent pozivom funkcije create kreira uređeni čvor. Nakon što server uspešno kreira čvor, odgovara klijentu na zahtev i šalje mu informacije o kreiranom čvoru.
Klijent koristi ime čvora podataka kao ID i nastavlja sa lokalnim poslovnim operacijama.
Iskorišćavanjem osobine uređenih čvorova u ZooKeeper-u lako možemo postići da ID koji kreiramo ima svojstvo uređenosti. Takođe, klijent može proslediti ime čvora i na osnovu različitih poslovnih kodova razlikovati različite poslovne sisteme, čime se povećava sposobnost proširenja koda.
❝ Iako ovakav način implementacije pomoću ZooKeeper-a ima mnogo prednosti, nosi i potencijalne probleme. ❞ Najvažniji je taj što definicija pravila kodiranja i dalje snažno zavisi od sposobnosti samog programera i njegovog dubokog razumevanja posla.
Lako se dešava da, zbog nepotpunog razmišljanja, postavljena pravila nakon određenog vremena rada ne mogu da zadovolje poslovne zahteve ili nisu dovoljno sigurna.
13. Implementacija balansiranja opterećenja
Uobičajeni algoritmi balansiranja opterećenja
❝
Metoda kruženja (round-robin)
❞
Metoda kruženja je najjednostavniji algoritam balansiranja opterećenja: kada se primi zahtev klijenta iz mreže, server za balansiranje opterećenja ga redom dodeljuje pozadinskim servisima.
Na primer, ako u klasteru postoje 3 servera - server1, server2, server3 - metoda kruženja će redom distribuirati zahteve sesija na server1, server2, server3. Kada se završi prvi krug kruženja, ponovo započinje sledeći ciklus.
❝
Metoda slučajnog izbora (random)
❞
Metoda slučajnog izbora znači da server za balansiranje opterećenja, po prijemu zahteva od klijenta, na osnovu određenog algoritma slučajnog izbora bira jedan server u pozadinskom klasteru za obradu tog zahteva sesije.
Međutim, kada u klasteru postane sve više kandidovanih mašina, iz statistike znamo da je verovatnoća izbora svake mašine gotovo jednaka, pa stvarni efekat metode slučajnog izbora sve više teži metodi kruženja.
❝
Metoda heširanja izvorne adrese (source IP hash)
❞
Osnovna ideja metode heširanja izvorne adrese je da se na osnovu IP adrese klijenta izvrši heširanje, rezultat izračunavanja koristi za operaciju modula, a zatim se na osnovu konačnog rezultata bira jedna mašina iz liste adresa servera koja obrađuje taj zahtev sesije.
Nakon usvajanja tog algoritma, kada klijent sa istom IP ponovo pristupi serveru, server za balansiranje opterećenja na kraju izabire isti server koji je prošli put obradio zahtev sesije te mašine, odnosno klijentu se uvek dodeljuje isti server.
❝
Metoda težinskog kruženja (weighted round-robin)
❞
Metoda težinskog kruženja veoma je slična metodi kruženja; jedina razlika je u tome što pri izboru mašine ne bira se isključivo redom, već se vodi računa o konfiguraciji i performansama mašine - mašine sa boljom konfiguracijom i performansama se češće biraju prve.
❝
Metoda težinskog slučajnog izbora (weighted random)
❞
Metoda težinskog slučajnog izbora je, kao i gore pomenuta metoda slučajnog izbora, takva da pri korišćenju algoritma slučajnog izbora servera uzima u obzir performanse sistema kao uslov težine.
❝
Metoda najmanjeg broja veza (least connections)
❞
Metoda najmanjeg broja veza znači da se na osnovu broja veza sesija klijenata koje pozadinski serveri obrađuju izračunava kom serveru treba dodeliti novu sesiju.
Opšte je prihvaćeno da mašine sa manje veza imaju veliku prednost u mrežnom protoku i računarskim performansama, pa se biraju kao prioritetni za dodelu.
Implementacija algoritma balansiranja opterećenja pomoću ZooKeeper-a
❝
Ovde ćemo, koristeći metodu najmanjeg broja veza, odrediti kako ravnomerno distribuirati mrežne zahteve sesija pozadinskim klijentima.
❞
Kao što je prikazano na donjoj slici, u izgrađenom modelu ZooKeeper podataka čvor Severs može služiti kao roditeljski čvor za čuvanje liste servera.
Ispod njega kreiraju se privremeni čvorovi servers_host1, servers_host2, servers_host3 itd. za čuvanje informacija o stanju rada servera u klasteru.

Celokupni proces implementacije prikazan je na donjoj slici.

Prvo, po prijemu zahteva klijenta, metodom getData preuzima se lista servera ispod čvora Severs na serveru; u informacijama svakog čvora čuva se trenutni broj veza tog servera.
Procenom se bira najmanji broj veza kao server za obradu trenutne sesije, a metodom setData se broj veza tog čvora uvećava za 1.
Konačno, kada klijent završi, ponovo se poziva metoda setData i informacija tog čvora se smanjuje za 1.
Definišemo metodu addBlance koja povećava broj veza na ZooKeeper serveru nakon što server primi zahtev sesije.
Metodom readData preuzimamo najnoviji broj veza servera, zatim taj broj uvećavamo za 1, a metodom writeData upisujemo novu informaciju o broju veza u odgovarajući čvor na serveru.
Kada server obradi taj zahtev sesije, potrebno je ažurirati broj veza relevantnog čvora na serveru.
Konkretna operacija je u osnovi ista kao u metodi addBlance, s tim što se dobijena informacija o broju veza smanjuje za jedan.
Napomena:
Balancer opterećenja koji svakodnevno koristimo uglavnom bira pozadinski server za obradu i njemu distribuira zahteve.
❝
Server implementiran pomoću ZooKeeper-a pruža samo posao izbora servera.
❞
U procesu distribucije zahteva i dalje se algoritmom balansiranja izračunava server kojem se pristupa, nakon čega se klijent sam povezuje na taj server i završava operaciju zahteva.
14. Primeri upotrebe u open-source okvirima
Dubbo i ZooKeeper
Dubbo je open-source tehnički okvir koji je razvio Alibaba; to je visokoperfasni, laki open-source Java RPC okvir.
ZooKeeper kao centar za registraciju
Tokom celokupne implementacije Dubbo okvira, centar za registraciju je jedna od najkritičnijih tačaka - on obezbeđuje transparentnost servisa prema spolja tokom celog PRC procesa.
Centar za registraciju Dubbo-a implementira se i putem ZooKeeper-a.
Kao što je prikazano na donjoj slici, tokom celokupnog procesa pokretanja Dubbo servisa, pružalac servisa pri pokretanju upisuje svoju URL adresu u direktorijum /dubbo/com.foo.BarService/providers; ta operacija se može posmatrati kao ZooKeeper klijent koji kreira čvor podataka na modelu podataka ZooKeeper servera.
Potrošač servisa se pri pokretanju pretplaćuje na URL adrese pružaoca ispod direktorijuma /dubbo/com.foo.BarService/providers i upisuje svoju URL adresu u direktorijum /dubbo/com.foo.BarService/consumers.
Ta operacija se vrši tako što ZooKeeper server kreira podčvor podataka ispod putanje čvora /consumers, a zatim se u zahtevu sesije pokreće watch praćenje čvora /providers.

Kafka i ZooKeeper
Uloga ZooKeeper-a
Pošto Broker serveri rade u režimu distribuiranog klastera, tokom rada servisa neizbežno se dešava da neka mašina zatvori zbog izuzetka.
Da bi se garantovala dostupnost celog Kafka klastera, potrebno je pratiti stanje rada svih mašina u sistemu. Kafka može preko čvorova podataka u ZooKeeper-u da čuva statistiku rada mašina u mreži ispod čvora brokers u modelu podataka.
U Kafka-i je za registraciju informacija o Topic-u takođe potrebno koristiti ZooKeeper; u Kafka-i isti kontejner poruka Topic može biti podeljen na više različitih particija, a te particije mogu postojati na jednom Broker serveru ili na različitim Broker serverima.
U Kafka klasteru svaki Broker server je relativno nezavisan.
Da bi se pročitale informacije o particijama uskladištene na distribuiran način, Kafka čuva odgovarajuću vezu između tih informacija o particijama i Broker servera u čvoru topic u modelu ZooKeeper podataka; svaki topic postoji u ZooKeeper čvoru podataka u obliku /brokers/topics/[topic].

Priredio: Chenmo Wang Er, pogledajte link za preštampanje, autor: Yueban Feiyu, pogledajte originalni link.
