Fënnef Studenten an dräi verdeelt Schlësselwäert Geschäfter

Oder wéi mir e Client C geschriwwen ++ Bibliothéik fir ZooKeeper, etcd an Consul KV

An der Welt vun verdeelt Systemer ginn et eng Rei vun typesch Aufgaben: Informatiounen iwwert d'Zesummesetzung vum Stärekoup späicheren, d'Konfiguratioun vun Wirbelen Gestioun, Feeler Wirbelen z'entdecken, e Leader auswielen an anerer. Fir dës Problemer ze léisen, goufen speziell verdeelt Systemer geschaf - Koordinatiounsservicer. Elo wäerte mir un dräi vun hinnen interesséiert sinn: ZooKeeper, etcd a Consul. Aus all de räiche Funktionalitéit vum Consul konzentréiere mir eis op Consul KV.

Fënnef Studenten an dräi verdeelt Schlësselwäert Geschäfter

Am Wesentlechen, all dës Systemer sinn Feeler-tolerant, linearizable Schlëssel-Wäert Geschäfter. Obwuel hir Datemodeller bedeitend Differenzen hunn, déi mir spéider diskutéieren, léisen se déiselwecht praktesch Problemer. Natierlech ass all Applikatioun déi de Koordinatiounsservice benotzt un ee vun hinnen gebonnen, wat zu der Bedierfnes féiere kann fir verschidde Systemer an engem Datenzenter z'ënnerstëtzen, déi déiselwecht Probleemer fir verschidden Uwendungen léisen.

D'Iddi fir dëse Problem ze léisen entstanen an enger australescher Berodungsagentur, an et ass op eis, e klengt Team vu Studenten, gefall fir se ëmzesetzen, iwwer wat ech wäert schwätzen.

Mir hunn et fäerdeg bruecht eng Bibliothéik ze kreéieren déi eng gemeinsam Interface gëtt fir mat ZooKeeper ze schaffen, etcd a Consul KV. D'Bibliothéik ass an C++ geschriwwen, awer et gi Pläng fir se an aner Sproochen ze portéieren.

Daten Modeller

Fir eng gemeinsam Interface fir dräi verschidde Systemer z'entwéckelen, musst Dir verstoen wat se gemeinsam hunn a wéi se ënnerscheeden. Loosst eis et erausfannen.

ZooKeeper

Fënnef Studenten an dräi verdeelt Schlësselwäert Geschäfter

D'Schlësselen sinn an e Bam organiséiert a ginn Noden genannt. Deementspriechend, fir en Node kënnt Dir eng Lëscht vu senge Kanner kréien. D'Operatiounen fir en Znode ze kreéieren (erstellen) an e Wäert z'änneren (setData) sinn getrennt: nëmmen existéierend Schlëssele kënne gelies a geännert ginn. Auere kënnen un d'Operatiounen befestegt ginn fir d'Existenz vun engem Node ze kontrolléieren, e Wäert ze liesen a Kanner ze kréien. Waacht ass en eemolegen Ausléiser dee brennt wann d'Versioun vun den entspriechende Donnéeën um Server ännert. Ephemeral Node gi benotzt fir Feeler z'entdecken. Si sinn un d'Sessioun vum Client gebonnen, deen se erstallt huet. Wann e Client eng Sessioun zoumaacht oder ophält ZooKeeper vu senger Existenz matdeelen, ginn dës Node automatesch geläscht. Einfach Transaktiounen ginn ënnerstëtzt - eng Rei vun Operatiounen déi entweder all Erfolleg oder versoen wann dëst net méiglech ass fir op d'mannst ee vun hinnen.

usw

Fënnef Studenten an dräi verdeelt Schlësselwäert Geschäfter

D'Entwéckler vun dësem System ware kloer vun ZooKeeper inspiréiert, an hunn dofir alles anescht gemaach. Et gëtt keng Hierarchie vu Schlësselen, awer si bilden e lexikographesch bestallt Set. Dir kënnt all Schlësselen, déi zu enger bestëmmter Gamme gehéieren, kréien oder läschen. Dës Struktur kann komesch schéngen, awer et ass eigentlech ganz expressiv, an eng hierarchesch Vue kann einfach duerch et emuléiert ginn.

etcd huet keng Norm vergläichen-a-set Operatioun, mee et huet eppes besser: Transaktiounen. Natierlech, existéieren se an all dräi Systemer, mee etcd Transaktiounen sinn besonnesch gutt. Si besteet aus dräi Blocken: Scheck, Erfolleg, Echec. Den éischte Block enthält eng Rei vu Konditiounen, déi zweet an drëtt - Operatiounen. D'Transaktioun gëtt atomesch ausgefouert. Wann all Konditioune wouer sinn, da gëtt den Erfollegsblock ausgefouert, soss gëtt de Feelerblock ausgefouert. An API 3.3 kënnen Erfolleg an Echec Blocken nestéiert Transaktiounen enthalen. Dat ass, et ass méiglech atomesch Konditiounskonstruktioune vu bal arbiträren Nistniveau auszeféieren. Dir kënnt méi léieren iwwer wat Schecken an Operatiounen existéieren Dokumentatioun.

Aueren existéieren och hei, obwuel se e bësse méi komplizéiert sinn a wiederverwendbar sinn. Dat ass, nodeems Dir eng Auer op engem Schlësselbereich installéiert hutt, kritt Dir all Aktualiséierungen an dësem Beräich bis Dir d'Auer annuléiert, an net nëmmen déi éischt. An etcd, den Analog vun ZooKeeper Client Sessiounen sinn Leasing.

Konsul K.V.

Et gëtt och keng strikt hierarchesch Struktur hei, awer de Consul kann d'Erscheinung erstellen datt et existéiert: Dir kënnt all Schlësselen mat dem spezifizéierte Präfix kréien a läschen, dat heescht, mat dem "Subtree" vum Schlëssel schaffen. Esou Ufroe ginn rekursiv genannt. Zousätzlech kann de Consul nëmmen Schlësselen auswielen, déi net de spezifizéierte Charakter nom Präfix enthalen, wat entsprécht direkt "Kanner" ze kréien. Awer et ass derwäert ze erënneren datt dat genau d'Erscheinung vun enger hierarchescher Struktur ass: et ass ganz méiglech e Schlëssel ze kreéieren wann hiren Elterendeel net existéiert oder e Schlëssel läschen deen Kanner huet, während d'Kanner weider am System gespäichert ginn.

Fënnef Studenten an dräi verdeelt Schlësselwäert Geschäfter
Amplaz vu Aueren huet de Consul HTTP-Ufroe blockéiert. Am Wesentlechen sinn dës gewéinlech Uruff un d'Dateliesmethod, fir déi, zesumme mat anere Parameteren, déi lescht bekannte Versioun vun den Donnéeën uginn. Wann déi aktuell Versioun vun den entspriechende Donnéeën um Server méi grouss ass wéi déi spezifizéiert, gëtt d'Äntwert direkt zréckginn, soss - wann de Wäert ännert. Et ginn och Sessiounen déi zu all Moment un d'Schlësselen befestegt kënne ginn. Et ass derwäert ze notéieren datt am Géigesaz zu etcd an ZooKeeper, wou d'Läsche vun Sessiounen zu der Läschung vun de verbonne Schlësselen féiert, gëtt et e Modus an deem d'Sessioun einfach vun hinnen ofgeschloss ass. Verfügbar Transaktiounen, ouni Branchen, awer mat all Zorte vu Schecken.

Alles zesummen ze setzen

ZooKeeper huet de strengsten Datemodell. Déi expressiv Gamme Ufroen verfügbar an etcd kënnen net effektiv an ZooKeeper oder Consul emuléiert ginn. Probéieren dat Bescht vun all de Servicer ze integréieren, hu mir mat engem Interface bal gläichwäerteg zu der ZooKeeper Interface mat de folgende bedeitende Ausnahmen opgehalen:

  • Sequenz, Container an TTL Noden net ënnerstëtzt
  • ACLs ginn net ënnerstëtzt
  • d'Setmethod erstellt e Schlëssel wann et net existéiert (an ZK setData gëtt an dësem Fall e Feeler zréck)
  • Set a Cas Methoden sinn getrennt (am ZK si se am Fong datselwecht)
  • d'Läschmethod läscht e Node zesumme mat sengem Ënnerbaum (an ZK Läschen gëtt e Feeler zréck wann de Node Kanner huet)
  • Fir all Schlëssel gëtt et nëmmen eng Versioun - d'Wäertversioun (an ZK et ginn der dräi)

D'Oflehnung vu sequentiellen Noden ass wéinst der Tatsaach datt etcd a Consul keng gebauter Ënnerstëtzung fir si hunn, a si kënne ganz einfach vum Benotzer iwwer déi resultéierend Bibliothéiksinterface ëmgesat ginn.

Ëmsetzung Verhalen ähnlech zu ZooKeeper wann e Wirbelen läschen géif verlaangen eng separat Kand Konter fir all Schlëssel an etcd an Consul erhalen. Well mir probéiert hunn d'Meta-Informatioun ze vermeiden, gouf decidéiert de ganze Subtree ze läschen.

Subtleties vun Ëmsetzung

Loosst eis e puer Aspekter vun der Ëmsetzung vun der Bibliothéik Interface a verschiddene Systemer méi no kucken.

Hierarchie an etc

Eng hierarchesch Vue an etcd erhalen huet sech als eng vun den interessantsten Aufgaben erausgestallt. Range Ufroen maachen et einfach eng Lëscht vu Schlësselen mat engem spezifizéierte Präfix ze recuperéieren. Zum Beispill, wann Dir alles braucht wat ufänkt mat "/foo", Dir frot eng Rei ["/foo", "/fop"). Awer dëst géif de ganze Subtree vum Schlëssel zréckginn, wat vläicht net akzeptabel ass wann de Subtree grouss ass. Am Ufank hu mir geplangt e Schlëssel Iwwersetzungsmechanismus ze benotzen, am Zetcd ëmgesat. Et handelt sech ëm ee Byte am Ufank vum Schlëssel derbäi ze ginn, gläich wéi d'Tiefe vum Node am Bam. Loosst mech Iech e Beispill ginn.

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

Da kréien all direkt Kanner vum Schlëssel "/foo" méiglech duerch Ufro eng Rei ["u02/foo/", "u02/foo0"). Jo, an ASCII "0" steet direkt no "/".

Awer wéi d'Entfernung vun engem Wirbel an dësem Fall ëmzesetzen? Et stellt sech eraus datt Dir all Beräicher vum Typ läschen musst ["uXX/foo/", "uXX/foo0") fir XX vun 01 bis FF. An dunn hu mir gerannt Operatioun Zuel Limite bannent enger Transaktioun.

Als Resultat gouf en einfache Schlësselkonvertéierungssystem erfonnt, wat et méiglech gemaach huet effektiv souwuel e Schlëssel ze läschen an eng Lëscht vu Kanner ze kréien. Et ass genuch fir e spezielle Charakter virum leschten Token ze addéieren. Zum Beispill:

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

Da läschen de Schlëssel "/very" gëtt zu Läschen "/u00very" an Gamme ["/very/", "/very0"), a kréien all Kanner - an enger Demande fir Schlësselen aus der Gamme ["/very/u00", "/very/u01").

E Schlëssel an ZooKeeper erofhuelen

Wéi ech scho gesot hunn, am ZooKeeper kënnt Dir en Node net läschen wann et Kanner huet. Mir wëllen de Schlëssel zesumme mam Subtree läschen. Wat sollt ech maachen? Mir maachen dat mat Optimismus. Als éischt fuere mir de Subtree rekursiv duerch, kréien d'Kanner vun all Wirbel mat enger separater Ufro. Da bauen mir eng Transaktioun déi probéiert all Wirbelen vum Subtree an der korrekter Uerdnung ze läschen. Natierlech kënnen Ännerunge geschéien tëscht dem Liesen vun engem Subtree an dem Läschen. An dësem Fall wäert d'Transaktioun versoen. Ausserdeem kann de Subtree während dem Liesprozess änneren. Eng Ufro fir d'Kanner vum nächste Node kann e Feeler zréckginn, wann zum Beispill dësen Node scho geläscht gouf. A béide Fäll widderhuelen mir de ganze Prozess erëm.

Dës Approche mécht d'Läschen vun engem Schlëssel ganz ineffektiv wann et Kanner huet, an nach méi wann d'Applikatioun weider mam Subtree funktionnéiert, Schlësselen läschen an erstellen. Wéi och ëmmer, dëst erlaabt eis d'Ëmsetzung vun anere Methoden an etcd a Consul ze komplizéieren ze vermeiden.

an ZooKeeper gesat

Am ZooKeeper ginn et getrennte Methoden déi mat der Bamstruktur funktionnéieren (create, delete, getChildren) an déi mat Daten an Wirbelen (setData, getData) funktionnéieren.Desweideren hunn all Methoden strikt Viraussetzunge: create wäert e Feeler zréckginn wann de Node schonn huet geschaf, läschen oder setData - wann et net schonn existéiert. Mir hunn eng festgeluecht Method gebraucht déi ka genannt ginn ouni iwwer d'Präsenz vun engem Schlëssel ze denken.

Eng Optioun ass eng optimistesch Approche ze huelen, wéi mat Läschen. Kontrolléiert ob en Node existéiert. Wann existéiert, ruffen setData, soss schafen. Wann déi lescht Method e Feeler zréckgeet, widderhuelen et nach eng Kéier. Déi éischt Saach ze notéieren ass datt den Existenztest sënnlos ass. Dir kënnt direkt Opruff schafen. Erfollegräich Ofschloss bedeit datt den Node net existéiert an et gouf erstallt. Soss, schafen wäert de passenden Feeler zréck, no deem Dir SetData Opruff muss. Natierlech, tëscht Uriff, kéint e Vertex vun engem konkurréierende Uruff geläscht ginn, an setData géif och e Feeler zréckginn. An dësem Fall kënnt Dir alles erëm maachen, awer ass et derwäert?

Wa béid Methoden e Feeler zréckginn, da wësse mir sécher datt eng kompetitiv Läschung stattfonnt huet. Loosst eis virstellen datt dës Läschung geschitt ass nodeems se uruffen. Dann ass déi Bedeitung, déi mir probéieren ze festleeën, scho geläscht. Dëst bedeit datt mir kënnen unhuelen datt de Set erfollegräich ausgefouert gouf, och wann tatsächlech näischt geschriwwe gouf.

Méi technesch Detailer

An dëser Rubrik wäerte mir eng Paus vun verdeelt Systemer huelen a schwätzen iwwer coding.
Ee vun den Haaptfuerderunge vum Client war Cross-Plattform: op d'mannst ee vun de Servicer muss op Linux, MacOS a Windows ënnerstëtzt ginn. Am Ufank hu mir nëmme fir Linux entwéckelt, a spéider ugefaang op anere Systemer ze testen. Dëst huet vill Problemer gesuergt, déi eng Zäit laang komplett onkloer waren, wéi et soll ugoen. Als Resultat ginn all dräi Koordinatiounsservicer elo op Linux a MacOS ënnerstëtzt, während nëmmen Consul KV op Windows ënnerstëtzt gëtt.

Vun Ufank un hu mir probéiert fäerdeg Bibliothéike fir Zougang zu Servicer ze benotzen. Am Fall vun ZooKeeper ass de Choix gefall ZooKeeper C++, déi schlussendlech net op Windows kompiléiere konnt. Dëst ass awer net iwwerraschend: d'Bibliothéik ass nëmmen als Linux positionéiert. Fir de Consul war déi eenzeg Optioun ppkonsul. Ënnerstëtzung huet missen dobäi ginn Seancen и Transaktiounen. Fir etcd ass eng vollwäerteg Bibliothéik déi déi lescht Versioun vum Protokoll ënnerstëtzt net fonnt, sou datt mir einfach generéiert grpc Client.

Inspiréiert vun der asynchroner Interface vun der ZooKeeper C++ Bibliothéik, hu mir beschloss och eng asynchron Interface ëmzesetzen. ZooKeeper C ++ benotzt Zukunft / verspriechen Primitiv fir dës. Am STL gi se leider ganz bescheiden ëmgesat. Zum Beispill, nee dann Method, déi d'passéiert Funktioun op d'Resultat vun der Zukunft applizéiert wann et verfügbar ass. An eisem Fall ass esou eng Method néideg fir d'Resultat an d'Format vun eiser Bibliothéik ze konvertéieren. Fir dëst Problem ëmzegoen, hu mir eisen eegenen einfache Fuedempool misse implementéieren, well mir op Ufro vum Client keng schwéier Drëttbibliothéike wéi Boost benotze kënnen.

Eis dann Ëmsetzung funktionéiert esou. Wann opgeruff, en zousätzleche Verspriechen / zukünfteg Paar erstallt. Déi nei Zukunft gëtt zréck, an de passéierte gëtt zesumme mat der entspriechender Funktioun an engem zousätzleche Verspriechen an der Schlaang gesat. E Fuedem aus dem Pool wielt e puer Futures aus der Schlaang a pollt se mat wait_for. Wann e Resultat verfügbar ass, gëtt déi entspriechend Funktioun genannt a säi Retourwäert gëtt un d'Versprieche weiderginn.

Mir hunn deeselwechte Fuedempool benotzt fir Ufroen op etcd a Consul auszeféieren. Dëst bedeit datt déi ënnerierdesch Bibliothéike vu ville verschiddene Threads zougänglech sinn. ppconsul ass net thread sécher, sou datt d'Uriff dohinner geschützt sinn duerch Schleisen.
Dir kënnt mat grpc vu ville Threads schaffen, awer et gi Subtilitéiten. An etcd Aueren ginn iwwer grpc Streams ëmgesat. Dëst sinn bidirektional Kanäl fir Messagen vun engem bestëmmten Typ. D'Bibliothéik erstellt en eenzege Fuedem fir all Aueren an en eenzege Fuedem deen erakommende Messagen veraarbecht. Also grpc verbitt parallel Schreiwen fir ze streamen. Dëst bedeit datt wann Dir eng Auer initialiséiert oder läscht, musst Dir waarden bis déi viregt Ufro ofgeschloss ass ze schécken ier Dir déi nächst schéckt. Mir benotzen fir Synchroniséierung bedingt Verännerlechen.

D 'Resultat

Kuckt fir Iech selwer: liboffkv.

Eis Equipe: Raed Romanov, Ivan Glushenkov, Dmitry Kamaldinov, Victor Krapivensky, Vitaly Ivanin.

Source: will.com

Setzt e Commentaire