Viis õpilast ja kolm levitasid võtmeväärtuste poode

Või kuidas me kirjutasime ZooKeeperi, etcd ja Consul KV jaoks kliendi C++ teegi

Hajussüsteemide maailmas on mitmeid tüüpilisi ülesandeid: teabe salvestamine klastri koostise kohta, sõlmede konfiguratsiooni haldamine, vigaste sõlmede tuvastamine, juhi valimine. muu. Nende probleemide lahendamiseks on loodud spetsiaalsed hajutatud süsteemid - koordineerimisteenused. Nüüd oleme huvitatud neist kolmest: ZooKeeper, etcd ja Consul. Kõigist Consuli rikkalikest funktsioonidest keskendume Consul KV-le.

Viis õpilast ja kolm levitasid võtmeväärtuste poode

Sisuliselt on kõik need süsteemid veakindlad, lineariseeritavad võtmeväärtuste salvestajad. Kuigi nende andmemudelitel on olulisi erinevusi, millest me hiljem räägime, lahendavad nad samu praktilisi probleeme. Ilmselgelt on iga koordineerimisteenust kasutav rakendus seotud ühega neist, mistõttu võib tekkida vajadus toetada ühes andmekeskuses mitut süsteemi, mis lahendavad erinevate rakenduste jaoks samu probleeme.

Idee selle probleemi lahendamiseks sai alguse ühest Austraalia konsultatsiooniagentuurist ja selle elluviimine jäi meie, väikese tudengite meeskonna õlule, millest ma ka räägin.

Meil õnnestus luua raamatukogu, mis pakub ühist liidest ZooKeeperi, etcd ja Consul KV-ga töötamiseks. Teek on kirjutatud C++ keeles, kuid plaanis on see ka teistesse keeltesse portida.

Andmemudelid

Kolme erineva süsteemi jaoks ühise liidese väljatöötamiseks peate mõistma, mis on neil ühist ja kuidas need erinevad. Selgitame välja.

Loomaaiatalitaja

Viis õpilast ja kolm levitasid võtmeväärtuste poode

Võtmed on organiseeritud puusse ja neid nimetatakse sõlmedeks. Sellest lähtuvalt saate sõlme jaoks hankida selle laste loendi. Znode loomise (create) ja väärtuse muutmise (setData) toimingud on eraldatud: lugeda ja muuta saab ainult olemasolevaid võtmeid. Kellad saab kinnitada sõlme olemasolu kontrollimise, väärtuse lugemise ja laste hankimise operatsioonide juurde. Watch on ühekordne päästik, mis käivitub, kui vastavate andmete versioon serveris muutub. Efemeerseid sõlme kasutatakse rikete tuvastamiseks. Need on seotud need loonud kliendi seansiga. Kui klient sulgeb seansi või lõpetab ZooKeeperi olemasolust teavitamise, kustutatakse need sõlmed automaatselt. Toetatakse lihtsaid tehinguid – toimingute komplekt, mis kõik õnnestuvad või ebaõnnestuvad, kui see pole vähemalt ühe puhul võimalik.

jne

Viis õpilast ja kolm levitasid võtmeväärtuste poode

Selle süsteemi arendajad olid selgelt ZooKeeperist inspireeritud ja tegid seetõttu kõike teisiti. Võtmete hierarhiat ei ole, vaid need moodustavad leksikograafiliselt järjestatud komplekti. Saate hankida või kustutada kõik teatud vahemikku kuuluvad võtmed. See struktuur võib tunduda kummaline, kuid tegelikult on see väga väljendusrikas ja selle kaudu saab hõlpsasti jäljendada hierarhilist vaadet.

etcd-l pole standardset võrdlemise ja määramise operatsiooni, kuid sellel on midagi paremat: tehingud. Muidugi on need olemas kõigis kolmes süsteemis, aga etcd tehingud on eriti head. Need koosnevad kolmest plokist: kontroll, edu, ebaõnnestumine. Esimene plokk sisaldab tingimuste kogumit, teine ​​ja kolmas - toiminguid. Tehing teostatakse aatomiliselt. Kui kõik tingimused on tõesed, siis käivitatakse eduplokk, vastasel juhul käivitatakse ebaõnnestumise plokk. API 3.3 puhul võivad õnnestumise ja ebaõnnestumise plokid sisaldada pesastatud tehinguid. See tähendab, et peaaegu suvalise pesastustasemega tingimuslikke konstruktsioone on võimalik atomaarselt täita. Saate lisateavet selle kohta, millest tšekid ja toimingud eksisteerivad dokumentatsioon.

Ka siin on kellad olemas, kuigi need on veidi keerulisemad ja korduvkasutatavad. See tähendab, et pärast kella installimist võtmevahemikule saate kõik selle vahemiku värskendused kuni kella tühistamiseni, mitte ainult esimese. Programmis etcd on ZooKeeperi kliendiseansside analoogiks liisingud.

Konsul K.V.

Siin pole ka ranget hierarhilist struktuuri, kuid Consul saab luua välimuse, nagu see on olemas: saate kõik määratud eesliitega võtmed hankida ja kustutada, st töötada võtme "alampuuga". Selliseid päringuid nimetatakse rekursiivseteks. Lisaks saab konsul valida ainult need võtmed, mis ei sisalda eesliite järel määratud märki, mis vastab koheste “laste” hankimisele. Kuid tasub meeles pidada, et just see on hierarhilise struktuuri välimus: võtit on täiesti võimalik luua, kui selle vanemat pole, või kustutada võti, millel on lapsed, samal ajal kui lapsi säilitatakse süsteemis.

Viis õpilast ja kolm levitasid võtmeväärtuste poode
Kellade asemel blokeerib Consul HTTP-päringuid. Sisuliselt on tegemist tavaliste andmete lugemismeetodi väljakutsetega, mille jaoks koos muude parameetritega näidatakse andmete viimane teadaolev versioon. Kui vastavate andmete praegune versioon serveris on suurem kui määratud, tagastatakse vastus kohe, vastasel juhul - väärtuse muutumisel. Samuti on seansse, mida saab igal ajal võtmetele kinnitada. Väärib märkimist, et erinevalt etcd-st ja ZooKeeperist, kus seansside kustutamine viib seotud võtmete kustutamiseni, on olemas režiim, kus seanss lihtsalt ühendatakse nendega lahti. Saadaval tehingud, ilma oksteta, aga igasuguste tšekkidega.

Kõike kokku panema

ZooKeeperil on kõige rangem andmemudel. Etdd-s saadaolevaid väljendusrikkaid vahemikupäringuid ei saa ZooKeeperis ega Consulis tõhusalt emuleerida. Püüdes kõigist teenustest parimat võtta, saime ZooKeeperi liidesega peaaegu samaväärse liidese järgmiste oluliste eranditega:

  • jada, konteiner ja TTL sõlmed ei toetata
  • ACL-e ei toetata
  • set-meetod loob võtme, kui seda pole olemas (ZK-s tagastab setData sel juhul vea)
  • set ja ca meetodid on eraldatud (ZK-s on need sisuliselt samad)
  • kustutamismeetod kustutab sõlme koos selle alampuuga (ZK-s tagastab kustutamine vea, kui sõlmel on lapsed)
  • Iga võtme jaoks on ainult üks versioon - väärtuse versioon (ZK neid on kolm)

Järjestikuste sõlmede tagasilükkamine on tingitud asjaolust, et etcd-l ja Consulil pole nende jaoks sisseehitatud tuge ning kasutaja saab neid hõlpsasti rakendada tekkiva teegi liidese peale.

ZooKeeperiga sarnase käitumise rakendamine tipu kustutamisel eeldaks iga võtme jaoks eraldi alamloendurit säilitamist kataloogis etcd ja Consul. Kuna püüdsime vältida metainfo salvestamist, otsustati kogu alampuu kustutada.

Rakenduse peensused

Vaatame lähemalt raamatukogu liidese rakendamise mõningaid aspekte erinevates süsteemides.

Hierarhia jne

Hierarhilise vaate säilitamine etcd-s osutus üheks huvitavamaks ülesandeks. Vahemikupäringud muudavad määratud eesliitega võtmete loendi toomise lihtsaks. Näiteks kui vajate kõike, mis algab "/foo", küsite vahemikku ["/foo", "/fop"). Kuid see tagastaks kogu võtme alampuu, mis ei pruugi olla vastuvõetav, kui alampuu on suur. Alguses plaanisime kasutada võtmetõlkemehhanismi, rakendatud zetcd-s. See hõlmab ühe baidi lisamist võtme algusesse, mis on võrdne puu sõlme sügavusega. Lubage mul tuua teile näide.

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

Seejärel hankige kõik võtmelapsed "/foo" võimalik, küsides vahemikku ["u02/foo/", "u02/foo0"). Jah, ASCII-s "0" seisab kohe pärast "/".

Kuidas aga teostada sellisel juhul tipu eemaldamist? Selgub, et peate kustutama kõik tüübivahemikud ["uXX/foo/", "uXX/foo0") XX jaoks alates 01 kuni FF. Ja siis sattusime kokku operatsioonide arvu piirang ühe tehingu jooksul.

Selle tulemusena leiutati lihtne võtmete teisendamise süsteem, mis võimaldas tõhusalt rakendada nii võtme kustutamist kui ka laste nimekirja hankimist. Piisab, kui lisada viimase märgi ette erimärk. Näiteks:

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

Seejärel kustutage võti "/very" muutub kustutamiseks "/u00very" ja ulatus ["/very/", "/very0"), ja kõigi laste hankimine - tootevaliku võtmete päringus ["/very/u00", "/very/u01").

Võtme eemaldamine ZooKeeperis

Nagu ma juba mainisin, ei saa ZooKeeperis sõlme kustutada, kui sellel on lapsed. Soovime kustutada võtme koos alampuuga. Mida ma peaksin tegema? Teeme seda optimismiga. Esiteks läbime rekursiivselt alampuu, saades iga tipu lapsed eraldi päringuga. Seejärel koostame tehingu, mis püüab kustutada kõik alampuu sõlmed õiges järjekorras. Muidugi võib alampuu lugemise ja kustutamise vahel tekkida muudatusi. Sel juhul tehing ebaõnnestub. Lisaks võib alampuu lugemisprotsessi ajal muutuda. Järgmise sõlme laste päring võib tagastada veateate, kui näiteks see sõlm on juba kustutatud. Mõlemal juhul kordame kogu protsessi uuesti.

See lähenemisviis muudab võtme kustutamise väga ebaefektiivseks, kui sellel on lapsed, ja veelgi enam, kui rakendus jätkab alampuuga töötamist, kustutades ja luues võtmeid. See võimaldas meil aga vältida muude meetodite rakendamise keerulisemaks muutmist dokumendis etcd ja Consul.

seatud ZooKeeperisse

ZooKeeperis on eraldi meetodid, mis töötavad puustruktuuriga (loo, kustuta, getChildren) ja mis töötavad sõlmedes olevate andmetega (setData, getData). Lisaks on kõigil meetoditel ranged eeltingimused: loo tagastab vea, kui sõlm on juba teinud. loodud, kustutada või seadaData – kui seda veel pole. Vajasime seatud meetodit, mida saab välja kutsuda ilma võtme olemasolule mõtlemata.

Üks võimalus on läheneda optimistlikult, nagu ka kustutamise puhul. Kontrollige, kas sõlm on olemas. Kui see on olemas, helistage setDatale, vastasel juhul looge. Kui viimane meetod andis vea, korrake seda uuesti. Esimene asi, mida tuleb märkida, on see, et olemasolu test on mõttetu. Saate kohe helistada loomisele. Edukas lõpetamine tähendab, et sõlme ei eksisteerinud ja see loodi. Vastasel juhul tagastab Create vastava vea, mille järel peate helistama setDatale. Muidugi võib kõnede vahel tippu konkureeriv kõne kustutada ja setData tagastaks samuti vea. Sel juhul saate seda kõike uuesti teha, kuid kas see on seda väärt?

Kui mõlemad meetodid tagastavad vea, siis teame kindlalt, et toimus konkureeriv kustutamine. Kujutagem ette, et see kustutamine toimus pärast komplekti helistamist. Siis on kõik tähendused, mida me püüame luua, juba kustutatud. See tähendab, et võime eeldada, et komplekt täideti edukalt, isegi kui tegelikult midagi ei kirjutatud.

Rohkem tehnilisi üksikasju

Selles jaotises teeme hajutatud süsteemidest pausi ja räägime kodeerimisest.
Üks kliendi peamisi nõudeid oli platvormideülene: vähemalt üks teenustest peab olema toetatud nii Linuxis, MacOS-is kui ka Windowsis. Algselt arendasime välja ainult Linuxi jaoks ja hiljem hakkasime teistes süsteemides testima. See tekitas palju probleeme, millele oli mõnda aega täiesti ebaselge, kuidas läheneda. Selle tulemusel toetatakse nüüd Linuxis ja MacOS-is kõiki kolme koordineerimisteenust, Windowsis aga ainult Consul KV.

Üritasime algusest peale kasutada teenustele juurdepääsuks valmis teeke. ZooKeeperi puhul langes valik peale ZooKeeper C++, mille kompileerimine Windowsis lõpuks ebaõnnestus. See pole aga üllatav: raamatukogu on paigutatud ainult Linuxi jaoks. Konsuli jaoks oli ainus võimalus ppkonsul. Sellele tuli lisada toetus seansse и tehingud. etcd jaoks ei leitud täisväärtuslikku teeki, mis toetaks protokolli uusimat versiooni, nii et me lihtsalt loodud grpc klient.

Inspireerituna ZooKeeper C++ teegi asünkroonsest liidesest, otsustasime rakendada ka asünkroonse liidese. ZooKeeper C++ kasutab selleks tuleviku/lubaduse primitiive. STL-is rakendatakse neid kahjuks väga tagasihoidlikult. Näiteks ei siis meetod, mis rakendab läbitud funktsiooni tuleviku tulemusele, kui see kättesaadavaks saab. Meie puhul on selline meetod vajalik tulemuse teisendamiseks meie teegi vormingusse. Selle probleemi lahendamiseks pidime rakendama oma lihtsa lõimekogumi, kuna kliendi soovil ei saanud me kasutada raskeid kolmanda osapoole teeke, nagu Boost.

Meie toonane teostus toimib nii. Kutsumisel luuakse täiendav lubadus/tulevikupaar. Uus tulevik tagastatakse ning läbitu pannakse koos vastava funktsiooni ja lisalubadusega järjekorda. Puuli lõime valib järjekorrast mitu futuuri ja küsitleb need, kasutades ooteaega. Tulemuse kättesaadavaks saamisel kutsutakse välja vastav funktsioon ja selle tagastusväärtus edastatakse lubadusele.

Kasutasime sama lõimekogumit, et täita päringuid etcd-le ja Consulile. See tähendab, et aluseks olevatele teekidele pääseb juurde mitme erineva lõime kaudu. ppconsul ei ole lõimekindel, seega on kõned sellele kaitstud lukkudega.
Grpc-ga saate töötada mitmest lõimest, kuid seal on nüansse. Etdd-s rakendatakse kellasid grpc-voogude kaudu. Need on teatud tüüpi sõnumite kahesuunalised kanalid. Teek loob kõigi kellade jaoks ühe lõime ja ühe lõime, mis töötleb sissetulevaid sõnumeid. Seega keelab grpc paralleelse kirjutamise voogu. See tähendab, et kella lähtestamisel või kustutamisel tuleb enne järgmise saatmist oodata, kuni eelmine päring on saatmise lõpetanud. Kasutame sünkroonimiseks tingimuslikud muutujad.

Summaarne

Vaadake ise: liboffkv.

Meie meeskond: Raed Romanov, Ivan Glušenkov, Dmitri Kamaldinov, Viktor Krapivensky, Vitali Ivanin.

Allikas: www.habr.com

Lisa kommentaar