Бес студент және үш негізгі құнды дүкендер таратылды

Немесе ZooKeeper, etcd және Consul KV үшін клиенттік C++ кітапханасын қалай жаздық

Бөлінген жүйелер әлемінде бірқатар типтік міндеттер бар: кластердің құрамы туралы ақпаратты сақтау, түйіндердің конфигурациясын басқару, ақаулы түйіндерді анықтау, көшбасшыны таңдау және басқалар. Бұл мәселелерді шешу үшін арнайы бөлінген жүйелер – үйлестіру қызметтері құрылды. Енді бізді олардың үшеуі қызықтырады: ZooKeeper, etcd және Consul. Консулдың барлық бай функцияларының ішінен біз Консул К.В.-ға тоқталамыз.

Бес студент және үш негізгі құнды дүкендер таратылды

Негізінде, бұл жүйелердің барлығы ақауларға төзімді, сызықтық кілттер қоймалары болып табылады. Олардың деректер үлгілерінің айтарлықтай айырмашылықтары болса да, біз кейінірек талқылаймыз, олар бірдей практикалық мәселелерді шешеді. Әлбетте, үйлестіру қызметін пайдаланатын әрбір қолданба олардың біреуіне байланысты, бұл әртүрлі қолданбалар үшін бірдей мәселелерді шешетін бір деректер орталығында бірнеше жүйені қолдау қажеттілігіне әкелуі мүмкін.

Бұл мәселені шешу идеясы австралиялық консалтингтік агенттікте пайда болды және оны жүзеге асыру бізге, студенттердің шағын тобына түсті, мен бұл туралы айтатын боламын.

Біз ZooKeeper, etcd және Consul KV бағдарламаларымен жұмыс істеу үшін ортақ интерфейсті қамтамасыз ететін кітапхана жасай алдық. Кітапхана C++ тілінде жазылған, бірақ оны басқа тілдерге көшіру жоспарда бар.

Деректер үлгілері

Үш түрлі жүйенің ортақ интерфейсін жасау үшін олардың ортақ неде екенін және олардың айырмашылығы неде екенін түсіну керек. Оны анықтап көрейік.

ZooKeeper

Бес студент және үш негізгі құнды дүкендер таратылды

Кілттер ағаш түрінде ұйымдастырылған және түйіндер деп аталады. Сәйкесінше, түйін үшін оның еншілестерінің тізімін алуға болады. Znode құру (жасау) және мәнді өзгерту (setData) операциялары бөлінген: тек бар кілттерді оқуға және өзгертуге болады. Сағаттарды түйіннің бар-жоғын тексеру, мәнді оқу және балаларды алу операцияларына қосуға болады. Watch - сервердегі сәйкес деректердің нұсқасы өзгерген кезде іске қосылатын бір реттік триггер. Эфемерлік түйіндер сәтсіздіктерді анықтау үшін қолданылады. Олар оларды жасаған клиенттің сеансына байланысты. Клиент сеансты жапқанда немесе оның бар екендігі туралы ZooKeeper хабарлауын тоқтатқанда, бұл түйіндер автоматты түрде жойылады. Қарапайым транзакцияларға қолдау көрсетіледі - олардың кем дегенде біреуі үшін бұл мүмкін болмаса, барлығы сәтті болатын немесе сәтсіз аяқталатын операциялар жиынтығы.

және т.б.

Бес студент және үш негізгі құнды дүкендер таратылды

Бұл жүйені әзірлеушілер ZooKeeper-ден шабыт алды, сондықтан бәрін басқаша жасады. Пернелердің иерархиясы жоқ, бірақ олар лексикографиялық реттелген жиынды құрайды. Белгілі бір ауқымға жататын барлық кілттерді алуға немесе жоюға болады. Бұл құрылым біртүрлі болып көрінуі мүмкін, бірақ ол шын мәнінде өте мәнерлі және иерархиялық көріністі ол арқылы оңай эмуляциялауға болады.

etcd стандартты салыстыру және орнату операциясы жоқ, бірақ одан жақсырақ нәрсе бар: транзакциялар. Әрине, олар үш жүйеде де бар, бірақ т.б. транзакциялар әсіресе жақсы. Олар үш блоктан тұрады: тексеру, сәттілік, сәтсіздік. Бірінші блокта шарттар жиыны, екінші және үшінші – амалдар бар. Мәміле атомдық түрде орындалады. Егер барлық шарттар дұрыс болса, онда сәтті блок орындалады, әйтпесе сәтсіздік блогы орындалады. API 3.3 жүйесінде сәтті және сәтсіздік блоктары кірістірілген транзакцияларды қамтуы мүмкін. Яғни, еркін дерлік ұя салу деңгейінің шартты конструкцияларын атомдық түрде орындауға болады. Қандай тексерулер мен операциялар бар екені туралы көбірек біле аласыз құжаттама.

Бұл жерде сағаттар да бар, бірақ олар біршама күрделірек және қайта пайдалануға болады. Яғни, сағатты негізгі диапазонға орнатқаннан кейін, сіз тек бірінші емес, сағаттан бас тартпайынша, осы ауқымдағы барлық жаңартуларды аласыз. т.б., ZooKeeper клиенттік сеанстарының аналогы жалдау болып табылады.

Консул К.В.

Мұнда да қатаң иерархиялық құрылым жоқ, бірақ консул оның бар көрінісін жасай алады: көрсетілген префикспен барлық кілттерді алуға және жоюға болады, яғни кілттің «ішкі ағашымен» жұмыс істеуге болады. Мұндай сұраулар рекурсивті деп аталады. Сонымен қатар, Консул бірден «балаларды» алуға сәйкес келетін префикстен кейін көрсетілген таңбаны қамтымайтын пернелерді ғана таңдай алады. Бірақ бұл иерархиялық құрылымның дәл көрінісі екенін есте ұстаған жөн: егер оның ата-анасы болмаса, кілтті жасауға немесе балалары бар кілтті жоюға әбден болады, ал балалар жүйеде сақталады.

Бес студент және үш негізгі құнды дүкендер таратылды
Консул сағаттардың орнына HTTP сұрауларын блоктайды. Негізінде бұл деректерді оқу әдісіне қарапайым қоңыраулар, олар үшін басқа параметрлермен бірге деректердің соңғы белгілі нұсқасы көрсетіледі. Сервердегі сәйкес деректердің ағымдағы нұсқасы көрсетілгеннен үлкен болса, жауап дереу қайтарылады, әйтпесе - мән өзгерген кезде. Сондай-ақ кез келген уақытта кілттерге қосылуға болатын сеанстар бар. Сеанстарды жою байланысты кілттерді жоюға әкелетін etcd және ZooKeeper-тен айырмашылығы, сеанс олардан жай ғана ажыратылатын режим бар екенін атап өткен жөн. Қол жетімді транзакциялар, тармақтары жоқ, бірақ чектердің барлық түрлерімен.

Барлығын біріктіру

ZooKeeper ең қатаң деректер үлгісіне ие. etcd қол жетімді экспрессивті ауқым сұрауларын ZooKeeper немесе Консулда тиімді эмуляциялау мүмкін емес. Барлық қызметтердің ең жақсысын біріктіруге тырысып, біз келесі маңызды ерекшеліктермен ZooKeeper интерфейсіне дерлік баламалы интерфейске ие болдық:

  • реттілік, контейнер және TTL түйіндері қолдау көрсетілмейді
  • ACL-ге қолдау көрсетілмейді
  • орнатылған әдіс кілтті жасайды, егер ол жоқ болса (ZK setData бұл жағдайда қатені қайтарады)
  • set және cas әдістері бөлінген (ZK-де олар бірдей нәрсе)
  • өшіру әдісі түйінді ішкі ағашымен бірге жояды (ZK-де жою түйінде балалар болса, қатені қайтарады)
  • Әрбір кілт үшін бір ғана нұсқа бар - мәндік нұсқа (ZK олардың үшеуі бар)

Тізбекті түйіндерді қабылдамау etcd және Consul қолданбаларында оларға кіріктірілген қолдаудың жоқтығына байланысты және оларды пайдаланушы нәтижесінде алынған кітапхана интерфейсінің үстіне оңай іске асыра алады.

Шыңды жою кезінде ZooKeeper сияқты әрекетті орындау etcd және Consul ішіндегі әрбір кілт үшін бөлек еншілес есептегішті сақтауды талап етеді. Мета ақпаратты сақтаудан аулақ болуға тырысқандықтан, ішкі ағашты толығымен жою туралы шешім қабылданды.

Іске асырудың нәзіктіктері

Әртүрлі жүйелерде кітапхана интерфейсін жүзеге асырудың кейбір аспектілерін толығырақ қарастырайық.

Иерархия т.б

etcd-де иерархиялық көріністі сақтау ең қызықты тапсырмалардың бірі болып шықты. Ауқым сұраулары көрсетілген префиксі бар кілттер тізімін шығарып алуды жеңілдетеді. Мысалы, сізге басталатын барлық нәрсе қажет болса "/foo", сіз ауқымды сұрап жатырсыз ["/foo", "/fop"). Бірақ бұл кілттің барлық ішкі ағашын қайтарады, егер ішкі ағаш үлкен болса, ол қабылданбауы мүмкін. Бастапқыда біз негізгі аударма механизмін қолдануды жоспарладық, zetcd жүйесінде жүзеге асырылады. Ол кілттің басына ағаштағы түйіннің тереңдігіне тең бір байтты қосуды қамтиды. Бір мысал келтірейін.

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

Содан кейін кілттің барлық балаларын алыңыз "/foo" ауқымды сұрау арқылы мүмкін ["u02/foo/", "u02/foo0"). Иә, ASCII-де "0" кейін бірден тұрады "/".

Бірақ бұл жағдайда шыңды жоюды қалай жүзеге асыруға болады? Түрдің барлық ауқымдарын жою керек екен ["uXX/foo/", "uXX/foo0") ХХ үшін 01-ден FF-ге дейін. Сосын біз жүгірдік операция санының шегі бір транзакция аясында.

Нәтижесінде қарапайым кілтті түрлендіру жүйесі ойлап табылды, ол кілтті жоюды да, балалар тізімін алуды да тиімді жүзеге асыруға мүмкіндік берді. Соңғы таңбалауыштың алдында арнайы таңбаны қосу жеткілікті. Мысалы:

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

Содан кейін кілт жойылады "/very" жоюға айналады "/u00very" және диапазон ["/very/", "/very0"), және барлық балаларды алу - диапазондағы кілттерді сұрауда ["/very/u00", "/very/u01").

ZooKeeper қолданбасында кілтті жою

Жоғарыда айтқанымдай, ZooKeeper бағдарламасында егер оның балалары болса, түйінді жою мүмкін емес. Біз кілтті ішкі ағашпен бірге жойғымыз келеді. Не істейін? Біз мұны оптимизммен жасаймыз. Біріншіден, біз ішкі ағашты рекурсивті түрде айналып өтеміз, әр шыңның еншілестерін бөлек сұраумен аламыз. Содан кейін біз ішкі ағаштың барлық түйіндерін дұрыс ретпен жоюға әрекеттенетін транзакция жасаймыз. Әрине, ішкі ағашты оқу және оны жою арасында өзгерістер болуы мүмкін. Бұл жағдайда транзакция сәтсіз болады. Сонымен қатар, оқу процесінде ішкі ағаш өзгеруі мүмкін. Келесі түйіннің еншілестеріне арналған сұрау, мысалы, бұл түйін әлдеқашан жойылған болса, қатені қайтаруы мүмкін. Екі жағдайда да бүкіл процесті қайталаймыз.

Бұл тәсіл, егер оның балалары болса, кілтті жоюды өте тиімсіз етеді және одан да көп қолданба ішкі ағашпен жұмыс істеуді жалғастырса, кілттерді жою және жасау. Дегенмен, бұл бізге etcd және Consul басқа әдістерді енгізуді қиындатпауға мүмкіндік берді.

ZooKeeper бағдарламасында орнатыңыз

ZooKeeper-де ағаш құрылымымен жұмыс істейтін (жасау, жою, getChildren) және түйіндердегі деректермен жұмыс істейтін (setData, getData) бөлек әдістер бар.Сонымен қатар, барлық әдістердің қатаң алғы шарттары бар: егер түйінде бұрыннан бар болса, құру қатені қайтарады. жасалған, жою немесе setData – егер ол бұрыннан жоқ болса. Бізге кілттің бар-жоғын ойламай шақыруға болатын жиынтық әдіс қажет болды.

Бір нұсқа - жою сияқты оптимистік көзқарасты қабылдау. Түйін бар-жоғын тексеріңіз. Бар болса, setData деп атаңыз, әйтпесе жасаңыз. Соңғы әдіс қатені қайтарса, оны қайтадан қайталаңыз. Бірінші ескеретін нәрсе, бар болу сынағы мағынасыз. Сіз бірден құруға қоңырау шала аласыз. Сәтті аяқталу түйіннің болмағанын және ол жасалғанын білдіреді. Әйтпесе, create сәйкес қатені қайтарады, содан кейін setData шақыру керек. Әрине, қоңыраулар арасында шыңы бәсекелес қоңырау арқылы жойылуы мүмкін және setData да қатені қайтарады. Бұл жағдайда сіз бәрін қайтадан жасай аласыз, бірақ бұл тұр ма?

Егер екі әдіс қатені қайтарса, біз бәсекелес жою орын алғанын нақты білеміз. Бұл жою жиынды шақырғаннан кейін орын алды деп елестетіп көрейік. Содан кейін біз орнатуға тырысатын қандай мағына болса да жойылады. Бұл шын мәнінде ештеңе жазылмаса да, жиын сәтті орындалды деп болжауға болатынын білдіреді.

Қосымша техникалық мәліметтер

Бұл бөлімде біз бөлінген жүйелерден үзіліс жасап, кодтау туралы сөйлесеміз.
Тұтынушының негізгі талаптарының бірі кросс-платформа болды: қызметтердің кем дегенде біреуіне Linux, MacOS және Windows жүйелерінде қолдау көрсету қажет. Бастапқыда біз тек Linux үшін әзірледік, ал кейінірек басқа жүйелерде тестілеуді бастадық. Бұл біраз уақыттан бері қалай қарау керектігі белгісіз болған көптеген проблемаларды тудырды. Нәтижесінде, барлық үш үйлестіру қызметіне Linux және MacOS жүйелерінде қолдау көрсетіледі, ал Windows жүйесінде тек Consul KV қызметіне қолдау көрсетіледі.

Әу бастан қызметтерге қол жеткізу үшін дайын кітапханаларды пайдалануға тырыстық. ZooKeeper жағдайында таңдау оған түсті ZooKeeper C++, сайып келгенде Windows жүйесінде құрастыра алмады. Дегенмен, бұл таңқаларлық емес: кітапхана тек Linux үшін орналасқан. Консул үшін жалғыз нұсқа болды консул. Оған қолдауды қосу керек болды сеанстар и транзакциялар. etcd үшін протоколдың соңғы нұсқасын қолдайтын толыққанды кітапхана табылмады, сондықтан біз жай ғана grpc клиенті жасалған.

ZooKeeper C++ кітапханасының асинхронды интерфейсінен шабыттана отырып, біз асинхронды интерфейсті де енгізуді шештік. ZooKeeper C++ бұл үшін болашақ/уәде примитивтерін пайдаланады. STL-де, өкінішке орай, олар өте қарапайым түрде жүзеге асырылады. Мысалы, жоқ содан кейін әдіс, ол қол жетімді болған кезде берілген функцияны болашақ нәтижеге қолданады. Біздің жағдайда мұндай әдіс нәтижені кітапханамыздың форматына түрлендіру үшін қажет. Бұл мәселені шешу үшін бізге қарапайым жіптер пулын енгізу керек болды, өйткені тұтынушының өтініші бойынша біз Boost сияқты ауыр үшінші тарап кітапханаларын пайдалана алмадық.

Біздің сол кездегі іске асыруымыз осылай жұмыс істейді. Шақырылған кезде қосымша уәде/болашақ жұп жасалады. Жаңа болашақ қайтарылады, ал өткені сәйкес функциямен және кезекке қосымша уәдемен бірге орналастырылады. Пулдағы ағын кезектен бірнеше фьючерстерді таңдайды және wait_for көмегімен оларды сұрайды. Нәтиже қол жетімді болғанда, сәйкес функция шақырылады және оның қайтару мәні уәдеге беріледі.

Біз etcd және Consul сұрауларын орындау үшін бірдей ағын пулын пайдаландық. Бұл негізгі кітапханаларға бірнеше түрлі ағындар арқылы қол жеткізуге болатынын білдіреді. ppconsul жіп қауіпсіз емес, сондықтан оған қоңыраулар құлыптармен қорғалған.
Сіз grpc-пен бірнеше ағындардан жұмыс істей аласыз, бірақ оның нәзік тұстары бар. etcd сағаттары grpc ағындары арқылы жүзеге асырылады. Бұл белгілі бір түрдегі хабарламаларға арналған екі бағытты арналар. Кітапхана барлық сағаттар үшін бір ағынды және кіріс хабарларды өңдейтін жалғыз ағынды жасайды. Сондықтан grpc ағынға параллель жазуға тыйым салады. Бұл сағатты инициализациялау немесе жою кезінде келесі сұрауды жібермес бұрын алдыңғы сұрау жіберуді аяқтағанша күту керек дегенді білдіреді. Синхрондау үшін қолданамыз шартты айнымалылар.

Нәтиже

Өзіңізді қараңыз: liboffkv.

Біздің команда: Раед Романов, Иван Глушенков, Дмитрий Камалдинов, Виктор Крапивенский, Виталий Иванин.

Ақпарат көзі: www.habr.com

пікір қалдыру