Penki studentai ir trys platino pagrindinių vertybių parduotuves

Arba kaip sukūrėme kliento C++ biblioteką ZooKeeper, etcd ir Consul KV

Paskirstytų sistemų pasaulyje yra keletas tipiškų užduočių: saugoti informaciją apie klasterio sudėtį, valdyti mazgų konfigūraciją, aptikti sugedusius mazgus, pasirinkti lyderį. ir kiti. Šioms problemoms spręsti sukurtos specialios paskirstytos sistemos – koordinavimo tarnybos. Dabar mus sudomins trys iš jų: ZooKeeper, etcd ir Consul. Iš visų gausių „Consul“ funkcijų daugiausia dėmesio skirsime „Consul KV“.

Penki studentai ir trys platino pagrindinių vertybių parduotuves

Iš esmės visos šios sistemos yra gedimams atsparios, linijuojamos raktų vertės saugyklos. Nors jų duomenų modeliai turi reikšmingų skirtumų, kuriuos aptarsime vėliau, jie išsprendžia tas pačias praktines problemas. Akivaizdu, kad kiekviena programa, kuri naudoja koordinavimo paslaugą, yra susieta su viena iš jų, todėl gali prireikti palaikyti kelias sistemas viename duomenų centre, kurios išsprendžia tas pačias skirtingų programų problemas.

Idėja išspręsti šią problemą kilo Australijos konsultacinėje agentūroje, o ją įgyvendinti teko mums, nedidelei studentų komandai, apie ką ir kalbėsiu.

Mums pavyko sukurti biblioteką, kuri suteikia bendrą sąsają darbui su ZooKeeper, etcd ir Consul KV. Biblioteka parašyta C++ kalba, tačiau planuojama ją perkelti į kitas kalbas.

Duomenų modeliai

Norėdami sukurti bendrą trijų skirtingų sistemų sąsają, turite suprasti, kas jos turi bendro ir kuo jos skiriasi. Išsiaiškinkime.

Zoologijos sodo prižiūrėtojas

Penki studentai ir trys platino pagrindinių vertybių parduotuves

Raktai yra suskirstyti į medį ir vadinami mazgais. Atitinkamai, mazgui galite gauti jo vaikų sąrašą. Znode kūrimo (create) ir reikšmės keitimo (setData) operacijos yra atskirtos: skaityti ir keisti galima tik esamus raktus. Laikrodžiai gali būti pritvirtinti prie mazgo egzistavimo tikrinimo, vertės nuskaitymo ir vaikų gavimo operacijų. Žiūrėti yra vienkartinis aktyviklis, kuris suaktyvinamas, kai pasikeičia atitinkamų duomenų versija serveryje. Efemeriniai mazgai naudojami gedimams aptikti. Jie susieti su juos sukūrusio kliento seansu. Kai klientas uždaro sesiją arba nustoja pranešti „ZooKeeper“ apie jos egzistavimą, šie mazgai automatiškai ištrinami. Palaikomos paprastos operacijos – operacijų rinkinys, kurios visos sėkmingos arba nepavyksta, jei tai neįmanoma bent vienai iš jų.

ir tt

Penki studentai ir trys platino pagrindinių vertybių parduotuves

Šios sistemos kūrėjai buvo aiškiai įkvėpti ZooKeeper, todėl viską darė kitaip. Raktų hierarchijos nėra, bet jie sudaro leksikografiškai sutvarkytą rinkinį. Galite gauti arba ištrinti visus raktus, priklausančius tam tikram diapazonui. Ši struktūra gali atrodyti keista, bet iš tikrųjų ji yra labai išraiškinga ir per ją galima lengvai pamėgdžioti hierarchinį vaizdą.

etcd neturi standartinės palyginimo ir nustatymo operacijos, tačiau ji turi kai ką geresnio: sandorius. Žinoma, jie egzistuoja visose trijose sistemose, tačiau etcd operacijos yra ypač geros. Jie susideda iš trijų blokų: patikrinimas, sėkmė, nesėkmė. Pirmame bloke yra sąlygų rinkinys, antrame ir trečiame – operacijos. Sandoris vykdomas atomiškai. Jei visos sąlygos yra teisingos, tada vykdomas sėkmės blokas, priešingu atveju vykdomas nesėkmės blokas. API 3.3 sėkmės ir nesėkmės blokuose gali būti įdėtos operacijos. Tai yra, galima atomiškai vykdyti beveik savavališko lizdo lygio sąlygines konstrukcijas. Galite sužinoti daugiau apie tai, kas yra patikrinimai ir operacijos dokumentacija.

Čia taip pat yra laikrodžių, nors jie yra šiek tiek sudėtingesni ir yra daugkartinio naudojimo. Tai yra, įdiegę laikrodį raktų diapazone, gausite visus šio diapazono atnaujinimus, kol neatšauksite laikrodžio, o ne tik pirmąjį. Programoje etcd ZooKeeper klientų seansų analogas yra nuoma.

Konsulas K.V.

Čia taip pat nėra griežtos hierarchinės struktūros, tačiau konsulas gali sukurti išvaizdą, kad ji egzistuoja: galite gauti ir ištrinti visus raktus su nurodytu priešdėliu, tai yra, dirbti su rakto „pomedžiu“. Tokios užklausos vadinamos rekursinėmis. Be to, konsulas gali pasirinkti tik tuos klavišus, kuriuose po priešdėlio nėra nurodyto simbolio, o tai atitinka tiesioginį „vaikų“ gavimą. Tačiau verta atsiminti, kad tai yra hierarchinės struktūros išvaizda: visiškai įmanoma sukurti raktą, jei jo pirminio nėra, arba ištrinti raktą, turintį vaikų, o vaikai ir toliau bus saugomi sistemoje.

Penki studentai ir trys platino pagrindinių vertybių parduotuves
Vietoj laikrodžių konsulas blokuoja HTTP užklausas. Iš esmės tai yra įprasti duomenų skaitymo metodo iškvietimai, kuriems kartu su kitais parametrais nurodoma paskutinė žinoma duomenų versija. Jei esama atitinkamų duomenų versija serveryje yra didesnė nei nurodyta, atsakymas grąžinamas iš karto, kitu atveju – pasikeitus reikšmei. Taip pat yra seansų, kuriuos bet kada galima prijungti prie raktų. Verta paminėti, kad skirtingai nuo etcd ir ZooKeeper, kur ištrynus seansus ištrinami susiję raktai, yra režimas, kai seansas tiesiog atsiejamas nuo jų. Galima sandorius, be šakų, bet su visokiais čekiais.

Viską sudėjus

„ZooKeeper“ turi griežčiausią duomenų modelį. Išraiškingų diapazono užklausų, prieinamų etcd, negalima efektyviai emuliuoti nei ZooKeeper, nei Consul. Bandydami įtraukti geriausią iš visų paslaugų, gavome sąsają, beveik lygiavertę ZooKeeper sąsajai, su šiomis reikšmingomis išimtimis:

  • seka, konteineris ir TTL mazgai nepalaikomas
  • ACL nepalaikomi
  • nustatymo metodas sukuria raktą, jei jo nėra (šiuo atveju ZK setData grąžina klaidą)
  • set ir cas metodai yra atskirti (ZK jie iš esmės yra tas pats)
  • trynimo metodas ištrina mazgą kartu su jo pomedžiu (ZK trynimas grąžina klaidą, jei mazgas turi vaikų)
  • Kiekvienam raktui yra tik viena versija - vertės versija (ZK jų yra trys)

Nuosekliųjų mazgų atmetimas yra susijęs su tuo, kad etcd ir Consul neturi jiems įmontuoto palaikymo, o vartotojas gali juos lengvai įdiegti ant susidariusios bibliotekos sąsajos.

Norint įgyvendinti elgseną, panašią į ZooKeeper, ištrinant viršūnę, reiktų išlaikyti atskirą antrinį skaitiklį kiekvienam raktui etcd ir Consul. Kadangi stengėmės vengti meta informacijos saugojimo, buvo nuspręsta ištrinti visą pomedį.

Įgyvendinimo subtilybės

Pažvelkime atidžiau į kai kuriuos bibliotekos sąsajos diegimo įvairiose sistemose aspektus.

Hierarchija ir kt

Hierarchinio požiūrio palaikymas programoje etcd pasirodė esąs viena įdomiausių užduočių. Diapazono užklausos leidžia lengvai gauti raktų sąrašą su nurodytu priešdėliu. Pavyzdžiui, jei jums reikia visko, kas prasideda "/foo", jūs prašote diapazono ["/foo", "/fop"). Tačiau tai grąžintų visą rakto pomedį, o tai gali būti nepriimtina, jei pomedis yra didelis. Iš pradžių planavome naudoti pagrindinį vertimo mechanizmą, įdiegta zetcd. Tai apima vieno baito pridėjimą rakto pradžioje, lygų medyje esančio mazgo gyliui. Pateiksiu pavyzdį.

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

Tada gaukite visus tiesioginius rakto vaikus "/foo" galima paprašius diapazono ["u02/foo/", "u02/foo0"). Taip, ASCII "0" stovi iškart po to "/".

Bet kaip šiuo atveju įgyvendinti viršūnės pašalinimą? Pasirodo, kad reikia ištrinti visus tipo diapazonus ["uXX/foo/", "uXX/foo0") XX nuo 01 iki FF. Ir tada mes susidūrėme operacijų skaičiaus limitas per vieną sandorį.

Dėl to buvo išrasta paprasta raktų konvertavimo sistema, kuri leido efektyviai įgyvendinti ir rakto ištrynimą, ir vaikų sąrašo gavimą. Prieš paskutinį žetoną pakanka pridėti specialų simbolį. Pavyzdžiui:

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

Tada ištrinkite raktą "/very" virsta ištrynimu "/u00very" ir diapazonas ["/very/", "/very0"), o gauti visus vaikus – prašyme raktų iš asortimento ["/very/u00", "/very/u01").

Rakto pašalinimas iš ZooKeeper

Kaip jau minėjau, „ZooKeeper“ negalite ištrinti mazgo, jei jis turi vaikų. Norime ištrinti raktą kartu su pomedžiu. Ką turėčiau daryti? Tai darome su optimizmu. Pirma, mes rekursyviai pereiname pomedį, gaudami kiekvienos viršūnės antrinius požymius su atskira užklausa. Tada sukuriame operaciją, kuri bando ištrinti visus pomedžio mazgus teisinga tvarka. Žinoma, tarp medžio nuskaitymo ir ištrynimo gali atsirasti pakeitimų. Tokiu atveju sandoris nepavyks. Be to, pomedis gali pasikeisti skaitymo proceso metu. Užklausa dėl kito mazgo vaikų gali grąžinti klaidą, jei, pavyzdžiui, šis mazgas jau buvo ištrintas. Abiem atvejais visą procesą kartojame dar kartą.

Dėl šio metodo rakto ištrynimas yra labai neveiksmingas, jei jis turi vaikų, o juo labiau, jei programa ir toliau dirba su pomedžiu, ištrindama ir kurdama raktus. Tačiau tai leido mums neapsunkinti kitų metodų įgyvendinimo etcd ir Consul.

nustatytas ZooKeeper

„ZooKeeper“ yra atskiri metodai, veikiantys su medžio struktūra (kurti, ištrinti, gautiChildren) ir dirbantys su duomenimis mazguose (setData, getData). Be to, visi metodai turi griežtas prielaidas: Create grąžins klaidą, jei mazgas jau padarė buvo sukurta, ištrinkite arba nustatykiteData – jei jos dar nėra. Mums reikėjo nustatyto metodo, kurį būtų galima iškviesti negalvojant apie rakto buvimą.

Vienas iš variantų yra optimistinis požiūris, kaip ir ištrynimo atveju. Patikrinkite, ar mazgas yra. Jei yra, iškvieskite setData, kitu atveju sukurkite. Jei paskutinis metodas grąžino klaidą, pakartokite viską iš naujo. Pirmas dalykas, į kurį reikia atkreipti dėmesį, yra tai, kad egzistavimo testas yra beprasmis. Galite iš karto skambinti sukurti. Sėkmingas užbaigimas reikš, kad mazgo nebuvo ir jis buvo sukurtas. Priešingu atveju, Create grąžins atitinkamą klaidą, po kurios turėsite iškviesti setData. Žinoma, tarp skambučių viršūnę gali panaikinti konkuruojantis skambutis, o setData taip pat grąžintų klaidą. Tokiu atveju galite viską kartoti iš naujo, bet ar verta?

Jei abu metodai grąžina klaidą, mes tikrai žinome, kad įvyko konkuruojantis ištrynimas. Įsivaizduokime, kad šis ištrynimas įvyko po skambinimo rinkinio. Tada bet kokia prasmė, kurią bandome nustatyti, jau ištrinta. Tai reiškia, kad galime manyti, kad rinkinys buvo sėkmingai įvykdytas, net jei iš tikrųjų nieko nebuvo parašyta.

Daugiau techninių detalių

Šiame skyriuje pailsėsime nuo paskirstytų sistemų ir pakalbėsime apie kodavimą.
Vienas iš pagrindinių kliento reikalavimų buvo kelių platformų: bent viena iš paslaugų turi būti palaikoma Linux, MacOS ir Windows. Iš pradžių kūrėme tik Linux, o vėliau pradėjome testuoti kitose sistemose. Tai sukėlė daug problemų, kurias kurį laiką buvo visiškai neaišku, kaip kreiptis. Todėl visos trys koordinavimo paslaugos dabar palaikomos „Linux“ ir „MacOS“, o tik „Consul KV“ palaikoma „Windows“.

Nuo pat pradžių paslaugoms pasiekti stengėmės naudoti paruoštas bibliotekas. ZooKeeper atveju pasirinkimas krito ZooKeeper C++, kurios galiausiai nepavyko sukompiliuoti sistemoje „Windows“. Tačiau tai nenuostabu: biblioteka yra skirta tik Linux. Konsului buvo vienintelė išeitis ppkonsulas. Prie jo reikėjo pridėti paramą sesijos и sandorius. Dėl etcd visavertė biblioteka, palaikanti naujausią protokolo versiją, nerasta, todėl mes tiesiog sugeneruotas grpc klientas.

Įkvėpti asinchroninės ZooKeeper C++ bibliotekos sąsajos, nusprendėme įdiegti ir asinchroninę sąsają. „ZooKeeper C++“ tam naudoja ateities / pažadų primityvus. STL, deja, jie įgyvendinami labai kukliai. Pavyzdžiui, ne tada metodas, kuri perduotą funkciją taiko ateities rezultatui, kai jis tampa pasiekiamas. Mūsų atveju toks metodas yra būtinas norint konvertuoti rezultatą į mūsų bibliotekos formatą. Norėdami išspręsti šią problemą, turėjome įdiegti savo paprastą gijų telkinį, nes kliento prašymu negalėjome naudoti sunkių trečiųjų šalių bibliotekų, tokių kaip „Boost“.

Mūsų tuometinis įgyvendinimas veikia taip. Kai iškviečiama, sukuriama papildoma pažado/ateities pora. Nauja ateitis grąžinama, o perduotasis kartu su atitinkama funkcija ir papildomu pažadu dedamas į eilę. Grupės gija parenka kelis ateities sandorius iš eilės ir apklausia juos naudojant laukti_for. Kai pasiekiamas rezultatas, iškviečiama atitinkama funkcija ir jos grąžinama reikšmė perduodama pažadui.

Naudojome tą patį gijų telkinį, kad vykdytume užklausas etcd ir Consul. Tai reiškia, kad pagrindines bibliotekas galima pasiekti keliomis skirtingomis gijomis. ppconsul nėra saugus siūlams, todėl skambučiai į jį yra apsaugoti užraktais.
Galite dirbti su grpc iš kelių gijų, tačiau yra subtilybių. Į etcd laikrodžiai įgyvendinami per grpc srautus. Tai yra dvikrypčiai tam tikro tipo pranešimų kanalai. Biblioteka sukuria vieną giją visiems laikrodžiams ir vieną giją, kuri apdoroja gaunamus pranešimus. Taigi grpc draudžia lygiagretų rašymą sraute. Tai reiškia, kad inicijuodami arba ištrindami laikrodį, prieš siųsdami kitą, turite palaukti, kol ankstesnė užklausa bus išsiųsta. Mes naudojame sinchronizavimui sąlyginius kintamuosius.

Visas

Žr už save: liboffkv.

Mūsų komanda: Raedas Romanovas, Ivanas Glušenkovas, Dmitrijus Kamaldinovas, Viktoras Krapivenskis, Vitalijus Ivaninas.

Šaltinis: www.habr.com

Добавить комментарий