Таван оюутан, гурван гол үнэ цэнийн дэлгүүр тараасан

Эсвэл бид ZooKeeper, etcd болон Consul KV-д зориулж C++ клиент номын санг хэрхэн бичсэн

Түгээмэл системүүдийн ертөнцөд хэд хэдэн ердийн ажил байдаг: кластерын бүтцийн талаархи мэдээллийг хадгалах, зангилааны тохиргоог удирдах, алдаатай зангилаа илрүүлэх, удирдагчийг сонгох. болон бусад. Эдгээр асуудлыг шийдвэрлэхийн тулд тусгай хуваарилагдсан системийг бий болгосон - зохицуулалтын үйлчилгээ. Одоо бид эдгээрийн гурвыг сонирхох болно: ZooKeeper, etcd болон Consul. Консулын бүх баялаг функцүүдээс бид Консул К.В.-д анхаарлаа хандуулах болно.

Таван оюутан, гурван гол үнэ цэнийн дэлгүүр тараасан

Үндсэндээ эдгээр бүх системүүд нь алдааг тэсвэрлэдэг, шугаман хэлбэрт оруулах боломжтой түлхүүр-үнэ цэнийн хадгалалтууд юм. Хэдийгээр тэдгээрийн өгөгдлийн загварууд нь ихээхэн ялгаатай байгаа бөгөөд бид үүнийг дараа хэлэлцэх болно, гэхдээ тэдгээр нь ижил практик асуудлыг шийддэг. Мэдээжийн хэрэг, зохицуулалтын үйлчилгээг ашигладаг програм бүр нь тэдгээрийн аль нэгэнд холбогдсон байдаг бөгөөд энэ нь өөр өөр програмуудад ижил асуудлыг шийддэг нэг мэдээллийн төвд хэд хэдэн системийг дэмжих хэрэгцээг бий болгож болзошгүй юм.

Энэ асуудлыг шийдэх санаа нь Австралийн нэгэн зөвлөх агентлагаас үүссэн бөгөөд үүнийг хэрэгжүүлэх нь бидний багахан оюутнуудын баг болох миний ярих зүйл юм.

Бид ZooKeeper, etcd болон Consul KV-тэй ажиллах нийтлэг интерфэйсийг хангасан номын санг бий болгож чадсан. Номын сан нь C++ хэл дээр бичигдсэн боловч бусад хэл рүү шилжүүлэхээр төлөвлөж байна.

Өгөгдлийн загварууд

Гурван өөр системд зориулсан нийтлэг интерфейсийг хөгжүүлэхийн тулд тэдгээр нь юугаараа нийтлэг байдаг, юугаараа ялгаатай болохыг ойлгох хэрэгтэй. Үүнийг олж мэдье.

Амьтны хүрээлэнгийн ажилтан

Таван оюутан, гурван гол үнэ цэнийн дэлгүүр тараасан

Түлхүүрүүд нь мод хэлбэрээр зохион байгуулагдаж, зангилаа гэж нэрлэгддэг. Үүний дагуу зангилааны хувьд та түүний хүүхдүүдийн жагсаалтыг авах боломжтой. Znode үүсгэх (үүсгэх) болон утгыг өөрчлөх (setData) үйлдлүүд тусгаарлагдсан: зөвхөн одоо байгаа түлхүүрүүдийг уншиж, өөрчлөх боломжтой. Зангилаа байгаа эсэхийг шалгах, утгыг унших, хүүхэд авах зэрэгт бугуйн цаг зүүж болно. Watch нь сервер дээрх харгалзах өгөгдлийн хувилбар өөрчлөгдөх үед ажилладаг нэг удаагийн гох юм. Түр зуурын зангилаа нь бүтэлгүйтлийг илрүүлэхэд ашиглагддаг. Тэдгээрийг үүсгэсэн үйлчлүүлэгчийн сесстэй холбоотой байдаг. Үйлчлүүлэгч сессийг хаах эсвэл ZooKeeper-д байгаа тухай мэдэгдэхээ зогсоох үед эдгээр зангилаанууд автоматаар устгагдана. Энгийн гүйлгээг дэмждэг - эдгээрийн дор хаяж нэг нь боломжгүй тохиолдолд бүгд амжилттай эсвэл бүтэлгүйтдэг үйлдлийн багц.

гэх мэт

Таван оюутан, гурван гол үнэ цэнийн дэлгүүр тараасан

Энэ системийг хөгжүүлэгчид ZooKeeper-ээс илт сүнслэгээр нөлөөлсөн тул бүх зүйлийг өөрөөр хийсэн. Түлхүүрүүдийн шатлал байхгүй, гэхдээ тэдгээр нь толь бичгийн дараалсан багцыг бүрдүүлдэг. Та тодорхой мужид хамаарах бүх түлхүүрүүдийг авах эсвэл устгах боломжтой. Энэ бүтэц нь хачирхалтай мэт санагдаж болох ч энэ нь үнэхээр илэрхийлэлтэй бөгөөд шаталсан үзлийг түүгээр дамжуулан хялбархан дуурайж болно.

etcd-д жишиг харьцуулах, тохируулах үйлдэл байхгүй, гэхдээ гүйлгээ гэсэн илүү сайн зүйлтэй. Мэдээжийн хэрэг, тэдгээр нь бүх гурван системд байдаг, гэхдээ etcd гүйлгээ нь ялангуяа сайн байдаг. Тэд шалгах, амжилт, бүтэлгүйтэл гэсэн гурван блокоос бүрдэнэ. Эхний блок нь олон нөхцөл, хоёр дахь, гурав дахь нь үйлдлүүдийг агуулдаг. Гүйлгээ нь атомаар хийгддэг. Хэрэв бүх нөхцөл үнэн бол амжилтын блок, эс бөгөөс бүтэлгүйтлийн блок гүйцэтгэгдэнэ. API 3.3-д амжилт болон бүтэлгүйтлийн блокууд нь үүрлэсэн гүйлгээг агуулж болно. Өөрөөр хэлбэл, бараг дур зоргоороо үүрлэх түвшний нөхцөлт бүтцийг атомаар гүйцэтгэх боломжтой. Та ямар шалгалт, үйл ажиллагаа байдаг талаар илүү ихийг мэдэх боломжтой баримт бичиг.

Цаг нь арай илүү төвөгтэй, дахин ашиглах боломжтой хэдий ч энд бас байдаг. Өөрөөр хэлбэл, цагийг гол мужид суулгасны дараа та зөвхөн эхнийх нь биш цагийг цуцлах хүртэл энэ хүрээн дэх бүх шинэчлэлтийг хүлээн авах болно. etcd дээр ZooKeeper үйлчлүүлэгч сессийн аналог нь түрээс юм.

Консул К.В.

Энд бас хатуу шаталсан бүтэц байхгүй, гэхдээ консул нь байгаа дүр төрхийг бий болгож чадна: та заасан угтвар бүхий бүх түлхүүрүүдийг авч устгаж болно, өөрөөр хэлбэл түлхүүрийн "дэд мод" -той ажиллах боломжтой. Ийм асуултуудыг рекурсив гэж нэрлэдэг. Нэмж дурдахад, Консул зөвхөн угтварын дараа заасан тэмдэгт агуулаагүй түлхүүрүүдийг сонгох боломжтой бөгөөд энэ нь шууд "хүүхдүүд" авахтай тохирч байна. Гэхдээ энэ нь шаталсан бүтцийн дүр төрх гэдгийг санах нь зүйтэй: хэрэв түүний эцэг эх байхгүй бол түлхүүр үүсгэх эсвэл хүүхэдтэй түлхүүрийг устгах боломжтой, харин хүүхдүүд системд хадгалагдах болно.

Таван оюутан, гурван гол үнэ цэнийн дэлгүүр тараасан
Цагны оронд Консул HTTP хүсэлтийг блоклодог. Үндсэндээ эдгээр нь өгөгдөл унших аргын ердийн дуудлага бөгөөд бусад параметрүүдийн хамт өгөгдлийн хамгийн сүүлийн мэдэгдэж буй хувилбарыг зааж өгсөн болно. Хэрэв сервер дээрх харгалзах өгөгдлийн одоогийн хувилбар нь заасан хэмжээнээс их байвал хариуг шууд буцаана, эс тэгвээс утга өөрчлөгдөхөд. Мөн ямар ч үед түлхүүрүүдийг хавсаргаж болох сессүүд байдаг. Сессийг устгах нь холбогдох түлхүүрүүдийг устгахад хүргэдэг etcd болон ZooKeeper-ээс ялгаатай нь сессийг тэднээс зүгээр л салгах горим байдаг гэдгийг тэмдэглэх нь зүйтэй. Боломжтой гүйлгээ, салбаргүй, гэхдээ бүх төрлийн чектэй.

Бүгдийг нь нийлүүлж байна

ZooKeeper нь хамгийн хатуу өгөгдлийн загвартай. etcd-д байгаа илэрхий хүрээний асуулгыг ZooKeeper эсвэл Consul аль алинд нь үр дүнтэй дуурайх боломжгүй. Бүх үйлчилгээнүүдээс хамгийн сайныг нь нэгтгэхийг хичээж, бид 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") XX-ийн хувьд 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 нь алдаа гаргах болно. Энэ тохиолдолд та бүгдийг дахин хийж болно, гэхдээ энэ нь үнэ цэнэтэй юу?

Хэрэв хоёр арга хоёулаа алдаа гаргавал бид өрсөлдөгч устгасан гэдгийг баттай мэдэж байна. Энэ устгалт нь set дуудагдсаны дараа болсон гэж төсөөлье. Тэгвэл бидний тогтоох гэж байгаа ямар ч утга учир аль хэдийн арилчихсан байдаг. Энэ нь үнэндээ юу ч бичээгүй байсан ч багц амжилттай хэрэгжсэн гэж бид үзэж болно гэсэн үг юм.

Илүү техникийн дэлгэрэнгүй мэдээлэл

Энэ хэсэгт бид тархсан системээс завсарлага авч, кодчиллын талаар ярих болно.
Үйлчлүүлэгчийн гол шаардлагуудын нэг нь кросс платформ байсан: дор хаяж нэг үйлчилгээг Linux, MacOS болон Windows дээр дэмждэг байх ёстой. Эхэндээ бид зөвхөн Линукс-д зориулж хөгжүүлсэн бөгөөд дараа нь бусад системүүд дээр туршилт хийж эхэлсэн. Энэ нь маш олон асуудал үүсгэсэн бөгөөд хэсэг хугацаанд хэрхэн хандах нь тодорхойгүй байв. Үүний үр дүнд бүх гурван зохицуулалтын үйлчилгээг Linux болон MacOS дээр дэмждэг болсон бол Windows дээр зөвхөн Consul KV дэмждэг.

Бид анхнаасаа л бэлэн номын сангуудыг ашиглан үйлчилгээ авахыг хичээсэн. ZooKeeper-ийн хувьд сонголт нь унасан ZooKeeper C++, эцэст нь Windows дээр хөрвүүлж чадаагүй. Гэхдээ энэ нь гайхмаар зүйл биш юм: номын сан нь зөвхөн линукс байдаг. Консулын хувьд цорын ганц сонголт байсан консул. Үүн дээр дэмжлэг нэмэх шаардлагатай байсан сессүүд и гүйлгээ. etcd-ийн хувьд протоколын хамгийн сүүлийн хувилбарыг дэмждэг бүрэн хэмжээний номын сан олдсонгүй, тиймээс бид зүгээр л grpc клиент үүсгэсэн.

ZooKeeper C++ номын сангийн асинхрон интерфейсээс санаа авсан бид асинхрон интерфэйсийг хэрэгжүүлэхээр шийдсэн. ZooKeeper C++ нь үүний тулд ирээдүй/амлалт командуудыг ашигладаг. STL-д харамсалтай нь тэдгээрийг маш даруухан хэрэгжүүлдэг. Жишээлбэл, үгүй дараа нь арга, энэ нь дамжуулагдсан функцийг боломжтой болсон үед ирээдүйн үр дүнд ашигладаг. Манай тохиолдолд үр дүнг манай номын сангийн формат руу хөрвүүлэхийн тулд ийм арга шаардлагатай. Хэрэглэгчийн хүсэлтээр бид Boost гэх мэт гуравдагч талын хүнд сангуудыг ашиглах боломжгүй байсан тул энэ асуудлыг даван туулахын тулд бид өөрсдийн энгийн урсгалын санг хэрэгжүүлэх шаардлагатай болсон.

Бидний тэр үеийн хэрэгжилт ийм л явж байна. Дуудлага хийх үед нэмэлт амлалт/ирээдүйн хос бий болно. Шинэ ирээдүйг буцааж, дамжуулсан нь харгалзах функц болон нэмэлт амлалтын хамт дараалалд байрлана. Цөөрөм дэх хэлхээ нь дарааллаас хэд хэдэн фьючерс сонгож, wait_for ашиглан санал асуулга явуулдаг. Үр дүн гарах үед харгалзах функцийг дуудаж, түүний буцах утгыг амлалт руу шилжүүлнэ.

Бид etcd болон Consul-д хүсэлтийг гүйцэтгэхийн тулд ижил thread-ийн санг ашигласан. Энэ нь үндсэн номын сангуудад олон өөр урсгалаар хандах боломжтой гэсэн үг юм. ppconsul нь thread-д аюулгүй биш тул түүн рүү залгах нь түгжээгээр хамгаалагдсан байдаг.
Та олон урсгалтай grpc-тэй ажиллах боломжтой, гэхдээ нарийн талууд байдаг. etcd-д цагнууд grpc урсгалаар хэрэгждэг. Эдгээр нь тодорхой төрлийн мессежийн хоёр чиглэлтэй сувгууд юм. Номын сан нь бүх цагны хувьд нэг хэлхээ, ирж буй мессежийг боловсруулдаг нэг хэлхээ үүсгэдэг. Тиймээс grpc нь урсгал руу зэрэгцээ бичихийг хориглодог. Энэ нь цагийг эхлүүлэх эсвэл устгах үед дараагийн хүсэлтийг илгээхийн өмнө өмнөх хүсэлтийг илгээж дуустал хүлээх хэрэгтэй гэсэн үг юм. Бид синхрончлолд ашигладаг нөхцөлт хувьсагч.

Үр дүн

Өөртөө зориулж: liboffkv.

Манай баг: Раед Романов, Иван Глушенков, Дмитрий Камалдинов, Виктор Крапивенский, Виталий Иванин.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх