Bost ikasle eta hiru gako-balioen denda banatuta

Edo nola idatzi genuen bezeroaren C++ liburutegia ZooKeeper, etcd eta Consul KVrentzat

Banatutako sistemen munduan, ohiko zeregin batzuk daude: klusterraren osaerari buruzko informazioa gordetzea, nodoen konfigurazioa kudeatzea, akatsak diren nodoak detektatzea, lider bat aukeratzea. eta beste batzuk. Arazo horiek konpontzeko, banatutako sistema bereziak sortu dira -koordinazio zerbitzuak. Orain horietako hiru interesatuko zaizkigu: ZooKeeper, etcd eta Consul. Consul-en funtzionaltasun aberats guztietatik, Consul KV-n zentratuko gara.

Bost ikasle eta hiru gako-balioen denda banatuta

Funtsean, sistema horiek guztiak akatsekiko tolerantzia eta linealizagarriak diren gako-balioen biltegiak dira. Beraien datu-ereduek desberdintasun nabarmenak dituzten arren, geroago eztabaidatuko ditugunak, arazo praktiko berdinak ebazten dituzte. Jakina, koordinazio-zerbitzua erabiltzen duen aplikazio bakoitza horietako bati lotuta dago, eta horrek aplikazio desberdinetarako arazo berdinak konpontzen dituzten datu-zentro batean hainbat sistema onartzea ekar dezake.

Arazo hau konpontzeko ideia Australiako aholkularitza agentzia batean sortu zen, eta guri, ikasle talde txiki bati, inplementatzea tokatu zitzaigun, eta hori da hitz egingo dudana.

ZooKeeper, etcd eta Consul KVrekin lan egiteko interfaze komun bat eskaintzen duen liburutegi bat sortzea lortu dugu. Liburutegia C++-n idatzita dago, baina beste hizkuntza batzuetara eramateko asmoa dago.

Datu-ereduak

Hiru sistema desberdinetarako interfaze komun bat garatzeko, zer duten komunean eta nola desberdintzen diren ulertu behar duzu. Asma dezagun.

ZooKeeper

Bost ikasle eta hiru gako-balioen denda banatuta

Gakoak zuhaitz batean antolatuta daude eta nodo deitzen zaie. Horren arabera, nodo baterako bere seme-alaben zerrenda lor dezakezu. Znode bat sortzeko (sortu) eta balio bat aldatzeko (setData) eragiketak bereizten dira: dauden gakoak bakarrik irakurri eta aldatu daitezke. Erlojuak nodo baten existentzia egiaztatzeko, balio bat irakurtzeko eta seme-alabak lortzeko eragiketei erantsi daitezke. Watch zerbitzarian dagozkien datuen bertsioa aldatzen denean abiatzen den abiarazlea da. Hutsegiteak detektatzeko nodo iragankorrak erabiltzen dira. Sortu dituen bezeroaren saioari lotuta daude. Bezero batek saio bat ixten duenean edo ZooKeeper-i bere existentziaren berri emateari uzten dionean, nodo hauek automatikoki ezabatzen dira. Transakzio sinpleak onartzen dira: denek arrakasta edo huts egiten duten eragiketa multzoa, gutxienez horietako batentzat posible ez bada.

etab

Bost ikasle eta hiru gako-balioen denda banatuta

Sistema honen garatzaileak ZooKeeper-en inspiratu ziren argi eta garbi, eta, beraz, dena ezberdin egin zuten. Ez dago gakoen hierarkiarik, baina lexikografikoki ordenatutako multzo bat osatzen dute. Barruti jakin bateko gako guztiak lor ditzakezu edo ezabatu. Egitura honek arraroa dirudi, baina, egia esan, oso adierazgarria da, eta haren bidez erraz imita daiteke ikuspegi hierarkikoa.

etcd-k ez du konparatzeko eta ezartzeko eragiketa estandarrik, baina badu zerbait hobea: transakzioak. Noski, hiru sistemetan existitzen dira, baina etcd transakzioak bereziki onak dira. Hiru bloke ditu: egiaztapena, arrakasta, porrota. Lehenengo blokeak baldintza multzo bat dauka, bigarrena eta hirugarrena - eragiketak. Transakzioa atomikoki gauzatzen da. Baldintza guztiak egiazkoak badira, arrakasta blokea exekutatzen da, bestela huts blokea exekutatzen da. API 3.3-n, arrakasta eta porrot blokeek habiaraturiko transakzioak izan ditzakete. Hau da, posible da atomikoki habia-maila ia arbitrarioko baldintzazko eraikuntzak exekutatu. Zer egiaztapen eta eragiketari buruz gehiago jakin dezakezu dokumentazioa.

Erlojuak hemen ere badaude, nahiz eta pixka bat konplexuagoak diren eta berrerabilgarriak diren. Hau da, erloju bat gako-barruti batean instalatu ondoren, barruti horretako eguneratze guztiak jasoko dituzu erlojua bertan behera uzten duzun arte, eta ez lehenengoa bakarrik. Etcd-en, ZooKeeper bezeroen saioen analogoa errentamenduak dira.

Kontsul K.V.

Hemen ere ez dago egitura hierarkiko zorrotzik, baina Consulek dagoen itxura sor dezake: zehaztutako aurrizkiarekin gako guztiak eskuratu eta ezaba ditzakezu, hau da, gakoaren "azpizuhaitzarekin" lan egin. Horrelako kontsultak errekurtsibo deritze. Horrez gain, Kontsulak aurrizkiaren ondoren zehaztutako karakterea ez duten gakoak bakarrik hauta ditzake, berehalako "seme-alabak" lortzeari dagokiona. Baina komeni da gogoratzea hori dela egitura hierarkiko baten itxura, hain zuzen: nahiko posible da gako bat sortzea bere gurasoa existitzen ez bada edo seme-alabak dituen gako bat ezabatzea, haurrak sisteman gordeta jarraituko duten bitartean.

Bost ikasle eta hiru gako-balioen denda banatuta
Erlojuen ordez, Consul-ek HTTP eskaerak blokeatzen ditu. Funtsean, datuen irakurketa metodorako dei arruntak dira, eta horretarako, beste parametro batzuekin batera, datuen azken bertsio ezaguna adierazten da. Zerbitzarian dagozkien datuen uneko bertsioa zehaztutakoa baino handiagoa bada, erantzuna berehala itzuliko da, bestela - balioa aldatzen denean. Era berean, edozein unetan giltzari atxiki daitezkeen saioak ere badaude. Aipatzekoa da, etcd eta ZooKeeper ez bezala, non saioak ezabatzeak lotutako gakoak ezabatzea ekartzen duen, saioa horietatik deslotura besterik ez den modu bat dagoela. Eskuragarri transakzioak, adarrik gabe, baina era guztietako txekeekin.

Guztia batera jarriz

ZooKeeper-ek datu-eredurik zorrotzena du. Etcd-n eskuragarri dauden adierazgarri-barrutiaren kontsultak ezin dira modu eraginkorrean emulatu ez ZooKeeper-en edo Consul-en. Zerbitzu guztietatik onena sartu nahian, ZooKeeper interfazearen ia baliokidea den interfaze batekin amaitu genuen salbuespen esanguratsu hauekin:

  • sekuentzia, edukiontzi eta TTL nodoak ez da onartzen
  • ACLak ez dira onartzen
  • set metodoak gako bat sortzen du existitzen ez bada (ZK-n setData-k errore bat itzultzen du kasu honetan)
  • set eta cas metodoak bereizten dira (ZKn funtsean gauza bera dira)
  • ezabatzeko metodoak nodo bat ezabatzen du bere azpizuhaitzarekin batera (ZK-n ezabatzeak errore bat itzultzen du nodoak seme-alabak baditu)
  • Gako bakoitzeko bertsio bakarra dago - balioaren bertsioa (ZK horietako hiru daude)

Nodo sekuentzialak baztertzea, etcd-k eta Consul-ek ez dutelako euskarri integratua, eta erabiltzaileak erraz inplementa ditzake ondoriozko liburutegiko interfazearen gainean.

Erpin bat ezabatzean ZooKeeper-en antzeko portaera ezartzeak gako bakoitzeko seme-alaba-kontagailu bat mantendu beharko luke etcd-en eta Consul-en. Meta informazioa gordetzen saihesten saiatu ginenez, azpizuhaitz osoa ezabatzea erabaki zen.

Ezarpenaren ñabardurak

Ikus ditzagun liburutegiko interfazea sistema ezberdinetan ezartzearen alderdi batzuk.

Hierarkia etab

Etcd-en ikuspegi hierarkikoa mantentzea zeregin interesgarrienetako bat izan zen. Barruti-kontsultei esker, aurrizki zehatza duten gakoen zerrenda bat berreskuratzea erraza da. Adibidez, hasten den guztia behar baduzu "/foo", barruti bat eskatzen ari zara ["/foo", "/fop"). Baina honek gakoaren azpizuhaitz osoa itzuliko luke, eta hori agian ez da onargarria izango azpizuhaitza handia bada. Hasieran itzulpen mekanismo gako bat erabiltzea aurreikusi genuen, zetcd-en ezarrita. Gakoaren hasieran byte bat gehitzea dakar, zuhaitzeko nodoaren sakoneraren berdina. Adibide bat jartzen dizut.

"/foo" -> "u01/foo"
"/foo/bar" -> "u02/foo/bar"

Ondoren, lortu giltzaren berehalako seme-alaba guztiak "/foo" posible da tarte bat eskatuz ["u02/foo/", "u02/foo0"). Bai, ASCII-n "0" berehala gelditzen da "/".

Baina nola inplementatu kasu honetan erpin bat kentzea? Bihurtzen da motako barruti guztiak ezabatu behar dituzula ["uXX/foo/", "uXX/foo0") 01etik FFrako XX. Eta gero topo egin genuen eragiketa kopurua muga transakzio baten barruan.

Ondorioz, gakoen bihurketa sistema sinple bat asmatu zen, gako bat ezabatzea eta haurren zerrenda bat lortzea eraginkortasunez inplementatzea ahalbidetu zuena. Nahikoa da karaktere berezi bat gehitzea azken tokenaren aurretik. Adibidez:

"/very" -> "/u00very"
"/very/long" -> "/very/u00long"
"/very/long/path" -> "/very/long/u00path"

Ondoren, gakoa ezabatu "/very" ezabaketa bihurtzen da "/u00very" eta barrutia ["/very/", "/very0"), eta haur guztiak lortzea - ​​barrutiaren giltzak eskaera batean ["/very/u00", "/very/u01").

ZooKeeper-en gako bat kentzen

Lehen aipatu dudan bezala, ZooKeeper-en ezin duzu nodo bat ezabatu haurrak baditu. Azpizuhaitzarekin batera gakoa ezabatu nahi dugu. Zer egin beharko nuke? Baikorrekin egiten dugu hori. Lehenik eta behin, azpizuhaitza modu errekurtsiboan zeharkatzen dugu, erpin bakoitzaren seme-alabak kontsulta bereizi batekin lortuz. Ondoren, azpizuhaitzaren nodo guztiak ordena egokian ezabatzen saiatzen den transakzio bat eraikitzen dugu. Jakina, aldaketak gerta daitezke azpizuhaitz bat irakurri eta ezabatzearen artean. Kasu honetan, transakzioak huts egingo du. Gainera, azpizuhaitza alda daiteke irakurketa prozesuan zehar. Hurrengo nodoaren seme-alaben eskaerak errore bat itzul dezake, adibidez, nodo hori dagoeneko ezabatu bada. Bi kasuetan, prozesu osoa berriro errepikatuko dugu.

Ikuspegi honek gako bat ezabatzea oso eraginkorra bihurtzen du seme-alabak baditu, eta are gehiago aplikazioak azpizuhaitzarekin lanean jarraitzen badu, gakoak ezabatuz eta sortuz. Hala ere, horri esker, etcd eta Consul-en beste metodo batzuen ezarpena zaildu ahal izan genuen.

ZooKeeper-en kokatuta

ZooKeeper-en zuhaitz-egiturarekin lan egiten duten metodo bereiziak daude (create, delete, getChildren) eta nodoetako datuekin lan egiten dutenak (setData, getData).Gainera, metodo guztiek aurrebaldintza zorrotzak dituzte: create-k errore bat itzuliko du nodoak dagoeneko badu. sortu, ezabatu edo ezarriData - dagoeneko existitzen ez bada. Tekla baten presentzian pentsatu gabe deitu daitekeen multzo-metodo bat behar genuen.

Aukera bat ikuspegi baikorra hartzea da, ezabatzean bezala. Egiaztatu nodorik dagoen. Bada, deitu setData, bestela sortu. Azken metodoak errore bat itzuli badu, errepikatu berriro. Kontuan izan behar den lehenengo gauza existentzia probak ez duela zentzurik. Sortu berehala deitu dezakezu. Arrakastaz osatzeak esan nahi du nodoa ez zela existitzen eta sortu zela. Bestela, sortu-k akats egokia itzuliko du, eta ondoren setData deitu behar duzu. Jakina, deien artean, erpin bat ezabatu liteke lehiakide den batek, eta setData-k ere errore bat itzuliko luke. Kasu honetan, berriro egin dezakezu, baina merezi al du?

Bi metodoek errore bat itzultzen badute, ziur badakigu lehian ezabatzea gertatu dela. Imajina dezagun ezabaketa hau set deitu ondoren gertatu dela. Orduan, ezartzen saiatzen ari garen esanahia dagoeneko ezabatu egiten da. Horrek esan nahi du multzo hori arrakastaz exekutatu dela pentsa dezakegula, nahiz eta egia esan ezer idatzi ez izan.

Xehetasun tekniko gehiago

Atal honetan sistema banatuetatik atsedena hartuko dugu eta kodeketari buruz hitz egingo dugu.
Bezeroaren eskakizun nagusietako bat plataforma anitzekoa zen: zerbitzuetako bat gutxienez Linux, MacOS eta Windows-en onartzen da. Hasieran, Linuxerako bakarrik garatu genuen, eta beste sistema batzuetan probatzen hasi ginen geroago. Horrek arazo asko eragin zituen, denbora batez nola hurbildu zen guztiz argi ez zutenak. Ondorioz, hiru koordinazio-zerbitzuak Linux eta MacOS-en onartzen dira orain, eta Windows-en Consul KV bakarrik onartzen da.

Hasiera-hasieratik saiatu ginen prest egindako liburutegiak erabiltzen zerbitzuetara sartzeko. ZooKeeper-en kasuan, aukeran erori zen ZooKeeper C++, azkenean Windows-en konpilatu ez zena. Hau, ordea, ez da harritzekoa: liburutegia Linux-en soilik kokatzen da. Kontsularentzat aukera bakarra zen ppkontsul. Laguntza gehitu behar zitzaion saioak и transakzioak. Etcd-rako, ez da aurkitu protokoloaren azken bertsioa onartzen duen liburutegi osorik, beraz, besterik gabe sortutako grpc bezeroa.

ZooKeeper C++ liburutegiaren interfaze asinkronoan inspiratuta, interfaze asinkrono bat ere ezartzea erabaki genuen. ZooKeeper C++-k etorkizuneko/promes primitiboak erabiltzen ditu horretarako. STLn, zoritxarrez, oso xume inplementatzen dira. Adibidez, ez gero metodoa, iraganeko funtzioa erabilgarri dagoenean etorkizuneko emaitzari aplikatzen diona. Gure kasuan, horrelako metodo bat beharrezkoa da emaitza gure liburutegiaren formatura bihurtzeko. Arazo honi aurre egiteko, gure hari multzo sinplea inplementatu behar izan genuen, bezeroak eskatuta ezin baikenituen hirugarrenen liburutegi astunak erabili, hala nola Boost.

Gure orduan inplementazioak horrela funtzionatzen du. Deitzen denean, promesa/etorkizuneko bikote osagarri bat sortzen da. Etorkizun berria itzultzen da, eta gainditutakoa dagokion funtzioarekin eta promesa gehigarri batekin batera jartzen da ilaran. Igerilekuko hari batek ilaratik hainbat etorkizun hautatzen ditu eta wait_for erabiliz galdetzen ditu. Emaitza eskuragarri dagoenean, dagokion funtzioari deitzen zaio eta bere itzulera-balioa promesari pasatzen zaio.

Hari multzo bera erabili dugu etcd-i eta Consul-i kontsultak egiteko. Horrek esan nahi du azpiko liburutegietara hainbat hari ezberdinetatik atzitu daitekeela. ppconsul ez da hari segurua, beraz, harako deiak blokeo bidez babestuta daude.
Hainbat haritako grpc-ekin lan egin dezakezu, baina ñabardurak daude. Etcd-n erlojuak grpc korronteen bidez ezartzen dira. Mota jakin bateko mezuentzako noranzko biko kanalak dira. Liburutegiak hari bakarra sortzen du erloju guztientzat eta sarrerako mezuak prozesatzen dituen hari bakarra. Beraz, grpc-k korrontean idazketa paraleloak debekatzen ditu. Horrek esan nahi du erloju bat abiaraztean edo ezabatzean, aurreko eskaera bidaltzen amaitu arte itxaron behar duzula hurrengoa bidali aurretik. Sinkronizaziorako erabiltzen dugu baldintza aldagaiak.

Guztira

Zuk zeuk ikusi: liboffkv.

Gure taldea: Raed Romanov, Ivan Glushenkov, Dmitri Kamaldinov, Victor Krapivensky, Vitaly Ivanin.

Iturria: www.habr.com

Gehitu iruzkin berria