Fiif studinten en trije ferdielde winkels mei kaaiwearde

Of hoe't wy skreau in klant C ++ bibleteek foar ZooKeeper, etcd en Consul KV

Yn 'e wrâld fan ferdielde systemen binne d'r in oantal typyske taken: it opslaan fan ynformaasje oer de gearstalling fan it kluster, it behearen fan de konfiguraasje fan knopen, it opspoaren fan defekte knopen, it kiezen fan in lieder en oaren. Om dizze problemen op te lossen, binne spesjale ferdielde systemen makke - koördinaasjetsjinsten. No sille wy ynteressearje yn trije fan har: ZooKeeper, etcd en Consul. Ut alle rike funksjonaliteit fan Consul, sille wy rjochtsje op Consul KV.

Fiif studinten en trije ferdielde winkels mei kaaiwearde

Yn essinsje binne al dizze systemen fouttolerante, linearisearre winkels foar kaaiwearden. Hoewol har gegevensmodellen signifikante ferskillen hawwe, dy't wy letter sille beprate, losse se deselde praktyske problemen op. Fansels is elke applikaasje dy't de koördinaasjetsjinst brûkt is bûn oan ien fan har, wat kin liede ta de needsaak om ferskate systemen te stypjen yn ien datasintrum dy't deselde problemen oplosse foar ferskate applikaasjes.

It idee om dit probleem op te lossen is ûntstien yn in Australysk konsultaasjeburo, en it foel op ús, in lyts team fan studinten, om it út te fieren, wêr't ik oer sil prate.

Wy slagge in meitsje in bibleteek dy't soarget foar in mienskiplike ynterface foar in wurk mei ZooKeeper, etcd en Consul KV. De bibleteek is skreaun yn C++, mar d'r binne plannen om it nei oare talen te portearjen.

Data Models

Om in mienskiplike ynterface te ûntwikkeljen foar trije ferskillende systemen, moatte jo begripe wat se mienskiplik hawwe en hoe't se ferskille. Litte wy it útfine.

ZooKeeper

Fiif studinten en trije ferdielde winkels mei kaaiwearde

De kaaien wurde organisearre yn in beam en wurde knopen neamd. Dêrom, foar in knooppunt kinne jo krije in list fan syn bern. De operaasjes fan it meitsjen fan in znode (meitsje) en it feroarjen fan in wearde (setData) wurde skieden: allinich besteande kaaien kinne lêzen en feroare wurde. Horloazjes kinne wurde hechte oan de operaasjes fan it kontrolearjen fan it bestean fan in knooppunt, it lêzen fan in wearde, en it krijen fan bern. Watch is in ienmalige trigger dy't ûntspringt as de ferzje fan 'e oerienkommende gegevens op' e tsjinner feroaret. Efemere knopen wurde brûkt om mislearrings te detektearjen. Se binne bûn oan 'e sesje fan' e kliïnt dy't se makke. As in kliïnt in sesje slút of ophâldt mei it ynformearjen fan ZooKeeper fan har bestean, wurde dizze knopen automatysk wiske. Ienfâldige transaksjes wurde stipe - in set fan operaasjes dy't allegear slagje of mislearje as dit net mooglik is foar op syn minst ien fan har.

ensfh

Fiif studinten en trije ferdielde winkels mei kaaiwearde

De ûntwikkelders fan dit systeem waarden dúdlik ynspirearre troch ZooKeeper, en diene dêrom alles oars. Der is gjin hiërargy fan kaaien, mar se foarmje in leksikografysk oardere set. Jo kinne alle kaaien krije of wiskje dy't ta in bepaald berik hearre. Dizze struktuer kin lykje frjemd, mar it is eins hiel ekspressyf, en in hiërargyske werjefte kin maklik wurde emulearre troch it.

etcd hat gjin standert fergelykje-en-set operaasje, mar it hat wat better: transaksjes. Fansels, se bestean yn alle trije systemen, mar etcd transaksjes binne benammen goed. Se besteane út trije blokken: check, súkses, failure. It earste blok befettet in set fan betingsten, de twadde en tredde - operaasjes. De transaksje wurdt atomysk útfierd. As alle betingsten wier binne, dan wurdt it suksesblok útfierd, oars wurdt it mislearre blok útfierd. Yn API 3.3 kinne súkses- en mislearringsblokken geneste transaksjes befetsje. Dat is, it is mooglik om atomysk út te fieren betingsten konstruksjes fan hast willekeurich nêst nivo. Jo kinne mear leare oer wat kontrôles en operaasjes besteane út dokumintaasje.

Horloazjes besteane hjir ek, hoewol se in bytsje yngewikkelder binne en opnij te brûken. Dat is, nei it ynstallearjen fan in horloazje op in kaaiberik, krije jo alle updates yn dit berik oant jo it horloazje annulearje, en net allinich de earste. Yn etcd, de analoog fan ZooKeeper klant sesjes binne leases.

Konsul K.V.

D'r is hjir ek gjin strikte hiërargyske struktuer, mar Consul kin it uterlik meitsje dat it bestiet: jo kinne alle kaaien krije en wiskje mei it oantsjutte foarheaksel, dat is, wurkje mei de "subtree" fan 'e kaai. Sokke fragen wurde rekursyf neamd. Derneist kin Consul allinich toetsen selektearje dy't it opjûne karakter net befetsje nei it foarheaksel, wat oerienkomt mei it krijen fan direkte "bern". Mar it is it wurdich te betinken dat dit krekt it uterlik fan in hiërargyske struktuer is: it is hiel mooglik om in kaai te meitsjen as syn âlder net bestiet of in kaai wiskje dy't bern hat, wylst de bern bliuwe yn it systeem opslein.

Fiif studinten en trije ferdielde winkels mei kaaiwearde
Ynstee fan horloazjes hat Consul HTTP-fersiken blokkearje. Yn essinsje binne dit gewoane oproppen nei de gegevenslêsmetoade, wêrfoar, tegearre mei oare parameters, de lêste bekende ferzje fan 'e gegevens wurdt oanjûn. As de aktuele ferzje fan 'e oerienkommende gegevens op' e tsjinner grutter is as de oantsjutte, wurdt it antwurd fuortendaliks weromjûn, oars - as de wearde feroaret. D'r binne ek sesjes dy't op elk momint oan kaaien kinne wurde hechte. It is de muoite wurdich op te merken dat yn tsjinstelling ta etcd en ZooKeeper, wêr't it wiskjen fan sesjes liedt ta it wiskjen fan assosjearre kaaien, d'r in modus is wêryn de sesje gewoan fan har is loskeppele. Beskikber transaksjes, sûnder tûken, mar mei allerhanne kontrôles.

It alles byinoar sette

ZooKeeper hat it meast strange gegevensmodel. De ekspressive berikfragen beskikber yn etcd kinne net effektyf emulearre wurde yn ZooKeeper of Consul. Troch it besykjen om it bêste fan alle tsjinsten op te nimmen, hawwe wy in ynterface dy't hast lykweardich is oan 'e ZooKeeper-ynterface mei de folgjende wichtige útsûnderings:

  • sequence, container en TTL knopen net ûndersteund
  • ACL's wurde net stipe
  • de setmetoade makket in kaai as it net bestiet (yn ZK jout setData in flater yn dit gefal)
  • set- en cas-metoaden wurde skieden (yn ZK binne se yn wêzen itselde ding)
  • de wismetoade wisket in knooppunt tegearre mei syn subtree (yn ZK jout wiskje in flater as de knooppunt bern hat)
  • Foar elke kaai is d'r mar ien ferzje - de weardeferzje (yn ZK der binne der trije fan)

De ôfwizing fan sekwinsjele knopen komt troch it feit dat etcd en Consul gjin ynboude stipe foar har hawwe, en se kinne maklik troch de brûker boppe op 'e resultearjende bibleteek-ynterface wurde ymplementearre.

It útfieren fan gedrach fergelykber mei ZooKeeper by it wiskjen fan in toppunt soe nedich wêze om in aparte bernteller te behâlden foar elke kaai yn etcd en Consul. Sûnt wy besochten it bewarjen fan meta-ynformaasje te foarkommen, waard besletten om de folsleine subtree te wiskjen.

Subtiliteiten fan ymplemintaasje

Litte wy in tichterby besjen op guon aspekten fan it ymplementearjen fan de biblioteekynterface yn ferskate systemen.

Hierargy yn ensfh

It behâld fan in hiërargyske werjefte yn etcd blykte ien fan de meast nijsgjirrige taken te wêzen. Range-fragen meitsje it maklik om in list fan kaaien op te heljen mei in spesifisearre foarheaksel. Bygelyks, as jo nedich hawwe alles dat begjint mei "/foo", jo freegje om in berik ["/foo", "/fop"). Mar dit soe de hiele subtree fan 'e kaai werombringe, wat miskien net akseptabel is as de subtree grut is. Yn it earstoan planden wy in wichtich oersetmeganisme te brûken, ymplementearre yn zetcd. It giet om it tafoegjen fan ien byte oan it begjin fan 'e kaai, gelyk oan de djipte fan' e knooppunt yn 'e beam. Lit my dy in foarbyld jaan.

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

Dan krije alle direkte bern fan de kaai "/foo" mooglik troch it oanfreegjen fan in berik ["u02/foo/", "u02/foo0"). Ja, yn ASCII "0" stiet rjocht na "/".

Mar hoe te fieren it fuortheljen fan in vertex yn dit gefal? It docht bliken dat jo alle berik fan it type wiskje moatte ["uXX/foo/", "uXX/foo0") foar XX fan 01 oant FF. En doe rûnen wy tsjin operaasje number limyt binnen ien transaksje.

As gefolch, in ienfâldige kaai konverzje systeem waard útfûn, dat makke it mooglik om effektyf útfiere sawol wiskjen in kaai en it krijen fan in list fan bern. It is genôch om in spesjaal karakter ta te foegjen foar it lêste token. Bygelyks:

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

Dan wiskje de kaai "/very" feroaret yn wiskjen "/u00very" en berik ["/very/", "/very0"), en it krijen fan alle bern - yn in fersyk foar kaaien út it berik ["/very/u00", "/very/u01").

It fuortsmiten fan in kaai yn ZooKeeper

Lykas ik al neamde, kinne jo yn ZooKeeper gjin knooppunt wiskje as it bern hat. Wy wolle de kaai wiskje tegearre mei de subtree. Wat moat ik dwaan? Wy dogge dit mei optimisme. Earst geane wy ​​rekursyf troch de subtree, en krije de bern fan elke hoekpunt mei in aparte query. Dan bouwe wy in transaksje dy't besiket alle knopen fan 'e subtree yn' e juste folchoarder te wiskjen. Fansels kinne feroarings foarkomme tusken it lêzen fan in subbeam en it wiskjen. Yn dit gefal sil de transaksje mislearje. Boppedat kin de subbeam feroarje tidens it lêsproses. In fersyk foar de bern fan it folgjende knooppunt kin in flater weromjaan as bygelyks dizze knooppunt al wiske is. Yn beide gefallen werhelje wy it hiele proses wer.

Dizze oanpak makket it wiskjen fan in kaai tige net effektyf as it bern hat, en noch mear as de applikaasje trochgiet te wurkjen mei de subtree, toetsen wiskje en oanmeitsje. Dit liet ús lykwols foarkomme om de ymplemintaasje fan oare metoaden yn etcd en Consul te komplisearjen.

set yn ZooKeeper

Yn ZooKeeper binne der aparte metoaden dy't wurkje mei de beamstruktuer (create, delete, getChildren) en dy't wurkje mei gegevens yn knooppunten (setData, getData) Boppedat hawwe alle metoaden strikte betingsten: create jout in flater as de knoop al hat is oanmakke, wiskje of setData - as it net al bestiet. Wy hawwe in setmetoade nedich dy't kin wurde neamd sûnder te tinken oer de oanwêzigens fan in kaai.

Ien opsje is om in optimistyske oanpak te nimmen, lykas by wiskjen. Kontrolearje oft der in knooppunt bestiet. As bestiet, neame setData, oars meitsje. As de lêste metoade in flater joech, werhelje it dan wer. It earste ding om op te merken is dat de besteantest sinleas is. Jo kinne fuortendaliks skilje meitsje. Súksesfolle foltôging sil betsjutte dat it knooppunt net bestie en it waard makke. Oars, meitsje sil weromkomme de passende flater, wêrnei't jo moatte neame setData. Fansels, tusken oproppen, in vertex koe wurde wiske troch in konkurrearjende oprop, en setData soe ek werom in flater. Yn dit gefal kinne jo it allegear opnij dwaan, mar is it it wurdich?

As beide metoaden in flater weromjaan, dan witte wy wis dat in konkurrearjende wiskje plakfûn. Lit ús yntinke dat dit wiskjen barde nei it oproppen set. Dan is de betsjutting dy't wy besykje te fêstigjen al wiske. Dit betsjut dat wy kinne oannimme dat de set mei súkses útfierd is, sels as yn feite neat skreaun is.

Mear technyske details

Yn dizze seksje sille wy in skoft nimme fan ferdielde systemen en prate oer kodearring.
Ien fan 'e wichtichste easken fan' e klant wie cross-platform: op syn minst ien fan 'e tsjinsten moat wurde stipe op Linux, MacOS en Windows. Yn it earstoan ûntwikkelen wy allinich foar Linux, en begûnen letter te testen op oare systemen. Dat soarge foar in soad problemen, dy't in skoft folslein ûndúdlik wiene hoe't se oanpakke moasten. As resultaat wurde alle trije koördinaasjetsjinsten no stipe op Linux en MacOS, wylst allinich Consul KV wurdt stipe op Windows.

Fan it begjin ôf besochten wy klearmakke biblioteken te brûken om tagong te krijen ta tsjinsten. Yn it gefal fan ZooKeeper foel de kar op ZooKeeper C++, dy't úteinlik net slagge om te kompilearjen op Windows. Dit is lykwols net ferrassend: de bibleteek is gepositioneerd as linux-allinich. Foar Consul wie de ienige opsje ppkonsul. Dêr moast stipe oan komme sesjes и transaksjes. Foar etcd is in folsleine bibleteek dy't de lêste ferzje fan it protokol stipet net fûn, dus wy gewoan generearre grpc client.

Ynspirearre troch de asynchrone ynterface fan 'e ZooKeeper C ++-bibleteek, hawwe wy besletten om ek in asynchrone ynterface út te fieren. ZooKeeper C ++ brûkt takomst / belofte primitives foar dit. Yn STL wurde se spitigernôch tige beskieden útfierd. Bygelyks, nee dan metoade, dy't de trochjûne funksje tapast op it resultaat fan 'e takomst as it beskikber wurdt. Yn ús gefal is sa'n metoade nedich om it resultaat te konvertearjen yn it formaat fan ús bibleteek. Om dit probleem om te kommen, moasten wy ús eigen ienfâldige threadpool implementearje, om't wy op fersyk fan 'e klant gjin swiere biblioteken fan tredden lykas Boost koenen brûke.

Us doe útfiering wurket sa. As jo ​​​​oproppen wurde, wurdt in ekstra belofte / takomstpear makke. De nije takomst wurdt weromjûn, en de trochjûne wurdt tegearre mei de oerienkommende funksje en in ekstra belofte yn 'e wachtrige pleatst. In tried út it swimbad selekteart ferskate futures út 'e wachtrige en ûndersiket se mei wait_for. As in resultaat beskikber wurdt, wurdt de oerienkommende funksje oproppen en de weromkommende wearde wurdt trochjûn oan de belofte.

Wy brûkten deselde threadpool om queries út te fieren nei etcd en Consul. Dit betsjut dat de ûnderlizzende bibleteken tagonklik wurde kinne troch meardere ferskillende triedden. ppconsul is net thread feilich, dus oproppen nei it wurde beskerme troch slûzen.
Jo kinne wurkje mei grpc út meardere triedden, mar d'r binne subtiliteiten. Yn etcd wurde horloazjes ymplementearre fia grpc-streamen. Dit binne bidirectionele kanalen foar berjochten fan in bepaald type. De bibleteek makket in inkele thread foar alle horloazjes en in inkele thread dy't ynkommende berjochten ferwurket. Dat grpc ferbiedt parallel skriuwen om te streamen. Dit betsjut dat jo by it inisjalisearjen of wiskjen fan in horloazje moatte wachtsje oant it foarige fersyk it ferstjoeren hat foltôge foardat jo de folgjende ferstjoere. Wy brûke foar syngronisaasje betingst fariabelen.

It resultaat

Sjoch foar josels: liboffkv.

Ús team: Raed Romanov, Ivan Glushenkov, Dmitry Kamaldinov, Victor Krapivensky, Vitaly Ivanin.

Boarne: www.habr.com

Add a comment