Cinque studienti è trè magazzini di chjave-valore distribuiti

O cumu avemu scrittu una biblioteca cliente C++ per ZooKeeper, etcd è Consul KV

In u mondu di i sistemi distribuiti, ci sò una quantità di funzioni tipiche: almacenà l'infurmazioni nantu à a cumpusizioni di u cluster, gestione a cunfigurazione di i nodi, detectà i nodi difettu, sceglie un capu. è altri. Per risolve questi prublemi, sò stati creati sistemi distribuiti speciali - servizii di coordinazione. Avà avemu da esse interessatu in trè di elli: ZooKeeper, etcd è Consul. Fora di tutte e ricche funziunalità di Consul, ci concentreremu nantu à Consul KV.

Cinque studienti è trè magazzini di chjave-valore distribuiti

In essenza, tutti questi sistemi sò magazzini di valori chjave linearizabili, toleranti à i difetti. Ancu s'è i so mudelli di dati anu differenzi significati, chì discutemu dopu, risolve i stessi prublemi pratichi. Ovviamente, ogni applicazione chì usa u serviziu di coordinazione hè ligata à unu di elli, chì pò purtà à a necessità di sustene parechji sistemi in un centru di dati chì risolve i stessi prublemi per diverse applicazioni.

L'idea di risolve stu prublema hè urigginata in una agenzia australiana di cunsultazione, è ci hè toccu à noi, una piccula squadra di studienti, di implementà, chì hè ciò chì parlu.

Avemu riesciutu à creà una biblioteca chì furnisce una interfaccia cumuna per travaglià cù ZooKeeper, etcd è Consul KV. A biblioteca hè scritta in C++, ma ci sò piani di portà in altre lingue.

Modelli di dati

Per sviluppà una interfaccia cumuna per trè sistemi diffirenti, avete bisognu di capiscenu ciò chì anu in cumunu è cumu si sò diffirenti. Scupritemu.

ZooKeeper

Cinque studienti è trè magazzini di chjave-valore distribuiti

I chjavi sò urganizati in un arbulu è sò chjamati nodi. In cunsiquenza, per un node pudete uttene una lista di i so figlioli. L'operazioni di creà un znode (create) è cambià un valore (setData) sò siparati: solu e chjave esistenti ponu esse leghjite è cambiate. Watches ponu esse attaccati à l'operazioni di verificà l'esistenza di un node, leghje un valore, è uttene i zitelli. Watch hè un attivatore una volta chì spara quandu a versione di e dati currispondenti nantu à u servitore cambia. I nodi effimeri sò usati per detectà fallimenti. Sò ligati à a sessione di u cliente chì l'hà creatu. Quandu un cliente chjude una sessione o ferma di notificà ZooKeeper di a so esistenza, sti nodi sò automaticamente eliminati. I transazzioni simplici sò supportati - un inseme di operazioni chì o tutti riescenu o fallenu s'ellu ùn hè micca pussibule per almenu unu di elli.

eccd

Cinque studienti è trè magazzini di chjave-valore distribuiti

I sviluppatori di stu sistema eranu chjaramente ispirati da ZooKeeper, è per quessa anu fattu tuttu in modu diversu. Ùn ci hè micca ghjerarchia di chjave, ma formanu un inseme lessicugraficamente urdinatu. Pudete ottene o sguassà tutte e chjave chì appartenenu à una certa gamma. Sta struttura pò parè strana, ma in realtà hè assai espressiva, è una vista gerarchica pò esse facilmente emulata per ella.

etcd ùn hà micca una operazione standard di paragunà è set, ma hà qualcosa di megliu: transacciones. Di sicuru, esistenu in tutti i trè sistemi, ma e transacciones etcd sò soprattuttu boni. Sò custituiti da trè blocchi: cuntrollu, successu, fallimentu. U primu bloccu cuntene un settore di cundizioni, u sicondu è u terzu - operazioni. A transazzione hè eseguita atomicamente. Se tutte e cundizioni sò veri, u bloccu di successu hè eseguitu, altrimente u bloccu di fallimentu hè eseguitu. In l'API 3.3, i blocchi di successu è fallimentu ponu cuntene transazzione nidificate. Vale à dì, hè pussibile eseguisce atomicamente custruzzioni cundiziunali di livellu di nidificazione quasi arbitrariu. Pudete amparà più nantu à ciò chì i cuntrolli è l'operazioni esistenu ducumentazione.

Orologi esiste ancu quì, anche si sò un pocu più complicati è sò riutilizzabili. Vale à dì, dopu avè installatu un watch in una gamma chjave, riceverete tutte l'aghjurnamenti in questa gamma finu à annullà u watch, è micca solu u primu. In etcd, l'analogicu di e sessioni di u cliente ZooKeeper sò affitti.

Consul K.V.

Ùn ci hè ancu una struttura gerarchica stretta quì, ma Consul pò creà l'apparizione chì esiste: pudete uttene è sguassà tutte e chjave cù u prefissu specificatu, vale à dì, travaglià cù u "subtree" di a chjave. Tali dumande sò chjamati recursive. In più, Consul pò selezziunà solu i chjavi chì ùn cuntenenu micca u caratteru specificatu dopu à u prefissu, chì currisponde à ottene "i zitelli" immediata. Ma vale a pena ricurdà chì questu hè precisamente l'apparizione di una struttura gerarchica: hè abbastanza pussibule di creà una chjave se u so parente ùn esiste micca o sguassate una chjave chì hà figlioli, mentre chì i zitelli continuanu à esse guardatu in u sistema.

Cinque studienti è trè magazzini di chjave-valore distribuiti
Invece di orologi, Consul hà bluccatu e richieste HTTP. In essenza, sò chjamati ordinariu à u metudu di lettura di dati, per quale, cù altri parametri, l'ultima versione cunnisciuta di e dati hè indicata. Se a versione attuale di e dati currispundenti nantu à u servitore hè più grande di quella specificata, a risposta hè tornata immediatamente, altrimenti - quandu u valore cambia. Ci sò ancu sessioni chì ponu esse attaccati à e chjave in ogni mumentu. Hè da nutà chì, à u cuntrariu di etcd è ZooKeeper, induve l'eliminazione di sessioni porta à l'eliminazione di e chjavi assuciati, ci hè un modu in quale a sessione hè simplicemente unlinked da elli. Disponibile transazzione, senza rami, ma cù ogni tipu di cuntrolli.

Mettendu tuttu inseme

ZooKeeper hà u mudellu di dati più rigurosu. E dumande di gamma espressiva dispunibili in etcd ùn ponu esse emulate in modu efficace in ZooKeeper o Consul. Pruvate di piglià u megliu da tutti i servizii, avemu finitu cù una interfaccia quasi equivalente à l'interfaccia ZooKeeper cù e seguenti eccezzioni significativi:

  • sequenza, contenitore è nodi TTL micca supportatu
  • L'ACL ùn sò micca supportati
  • u metudu set crea una chjave s'ellu ùn esiste micca (in ZK setData torna un errore in questu casu)
  • I metudi set è cas sò separati (in ZK sò essenzialmente a stessa cosa)
  • u metudu di sguassà sguassate un node cù u so subtree (in ZK cancella torna un errore se u node hà figlioli)
  • Per ogni chjave ci hè una sola versione - a versione di valore (in ZK ci sò trè)

U rifiutu di i nodi sequenziali hè duvuta à u fattu chì etcd è Consul ùn anu micca supportu integratu per elli, è ponu esse facilmente implementati da l'utilizatori nantu à l'interfaccia di a biblioteca resultanti.

L'implementazione di cumportamentu simili à ZooKeeper quandu sguassate un vertice avaristi bisognu di mantene un contatore di u zitellu separatu per ogni chjave in etcd è Consul. Siccomu avemu pruvatu à evità di almacenà a meta infurmazione, hè statu decisu di sguassà tuttu u subtree.

Sottilità di implementazione

Fighjemu un ochju più vicinu à certi aspetti di l'implementazione di l'interfaccia di a biblioteca in diversi sistemi.

Gerarchia in etcd

Mantene una vista gerarchica in etcd hè stata una di e attività più interessanti. E dumande di gamma facilitanu a ricuperazione di una lista di chjave cù un prefissu specificatu. Per esempiu, sè avete bisognu di tuttu ciò chì principia "/foo", dumandu un intervallu ["/foo", "/fop"). Ma questu vultà tuttu u subtree di a chjave, chì pò esse micca accettatu se u subtree hè grande. À u principiu, avemu pensatu à aduprà un mecanismu di traduzzione chjave, implementatu in zetcd. Implica l'aghjunghje un byte à u principiu di a chjave, uguale à a prufundità di u node in l'arbulu. Lasciami dà un esempiu.

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

Allora uttene tutti i figlioli immediata di a chjave "/foo" pussibule dumandendu una gamma ["u02/foo/", "u02/foo0"). Sì, in ASCII "0" sta ghjustu dopu "/".

Ma cumu implementà a rimuzione di un vertice in questu casu? Ci hè chì avete bisognu di sguassà tutte e varieghja di u tipu ["uXX/foo/", "uXX/foo0") per XX da 01 à FF. E poi avemu intruduciutu limitu di u numeru di operazione in una transazzione.

In u risultatu, hè statu inventatu un sistema simplice di cunversione di chjave, chì hà permessu di implementà in modu efficace sia sguassà una chjave è ottene una lista di i zitelli. Hè abbastanza per aghjunghje un caratteru speciale prima di l'ultimu token. Per esempiu:

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

Allora sguassà a chjave "/very" si trasforma in cancellazione "/u00very" è gamma ["/very/", "/very0"), è ottene tutti i zitelli - in una dumanda di chjave da a gamma ["/very/u00", "/very/u01").

Eliminazione di una chjave in ZooKeeper

Cumu l'aghju digià dettu, in ZooKeeper ùn pudete micca sguassà un node s'ellu hà figlioli. Vulemu sguassà a chjave cù u subtree. Chì devu fà? Facemu questu cun ottimisimu. Prima, avemu recursively traversu u subtree, ottenendu i figlioli di ogni vertice cù una query separata. Allora custruemu una transazzione chì prova di sguassà tutti i nodi di u subtree in l'ordine currettu. Di sicuru, i cambiamenti ponu accade trà leghje un subtree è sguassà. In questu casu, a transazzione falla. Inoltre, u subtree pò cambià durante u prucessu di lettura. Una dumanda per i zitelli di u prossimu node pò rinvià un errore se, per esempiu, stu node hè digià sguassatu. In i dui casi, ripetemu u prucessu tutale di novu.

Stu approcciu fa sguassà una chjave assai inefficace s'ellu hà figlioli, è ancu di più se l'applicazione cuntinueghja à travaglià cù u subtree, sguassate è creanu chjave. Tuttavia, questu ci hà permessu di evità di cumplicà l'implementazione di altri metudi in etcd è Consul.

stabilitu in ZooKeeper

In ZooKeeper ci sò metudi separati chì travaglianu cù a struttura di l'arburu (create, delete, getChildren) è chì travaglianu cù dati in nodes (setData, getData) Inoltre, tutti i metudi anu precondizioni strette: create torna un errore se u node hà digià statu creatu, sguassà o setData - s'ellu ùn esiste digià. Avemu bisognu di un metudu sette chì pò esse chjamatu senza pensà à a presenza di una chjave.

Una opzione hè di piglià un accostu ottimista, cum'è cù l'eliminazione. Verificate s'ellu esiste un node. Se esiste, chjamate setData, altrimenti creanu. Se l'ultimu metudu hà tornatu un errore, repite tuttu novu. A prima cosa da nutà hè chì a prova di esistenza hè inutile. Pudete subitu chjamà creà. U cumpletu successu significarà chì u node ùn esiste micca è hè statu creatu. Altrimenti, crià torna l'errore apprupriatu, dopu chì avete bisognu di chjamà setData. Di sicuru, trà e chjama, un vertice puderia esse sguassatu da una chjama cuncurrente, è setData torna ancu un errore. In questu casu, pudete fà tuttu di novu, ma vale a pena?

Se i dui metudi tornanu un errore, allora sapemu sicuru chì una eliminazione cuncurrente hè accaduta. Imaginemu chì sta eliminazione hè accaduta dopu à u set chjamatu. Allora qualunque significatu chì avemu da pruvà à stabilisce hè digià sguassatu. Questu significa chì pudemu assume chì u set hè statu eseguitu cù successu, ancu s'ellu ùn hè micca scrittu nunda.

Più dettagli tecnichi

In questa sezione, faremu una pausa da i sistemi distribuiti è parlemu di codificazione.
Unu di i bisogni principali di u cliente era cross-platform: almenu unu di i servizii deve esse supportatu in Linux, MacOS è Windows. Inizialmente, avemu sviluppatu solu per Linux, è hà cuminciatu à pruvà in altri sistemi dopu. Questu hà causatu assai prublemi, chì per qualchì tempu ùn era micca chjaru cumu avvicinà. In u risultatu, tutti i trè servizii di coordinazione sò avà supportati in Linux è MacOS, mentri solu Consul KV hè supportatu in Windows.

Da u principiu, avemu pruvatu à utilizà biblioteche pronti per accede à i servizii. In u casu di ZooKeeper, a scelta hè cascata ZooKeeper C++, chì in ultimamente hà fiascatu à cumpilà in Windows. Questu, però, ùn hè micca surprisante: a biblioteca hè posizionata cum'è Linux solu. Per Consul l'unica opzione era ppconsul. Supportu avia da esse aghjuntu à questu sessioni и transazzioni. Per etcd, una biblioteca cumpleta chì sustene l'ultima versione di u protokollu ùn hè micca stata truvata, cusì simpricimenti cliente grpc generatu.

Ispirati da l'interfaccia asincrona di a biblioteca ZooKeeper C++, avemu decisu di implementà ancu una interfaccia asincrona. ZooKeeper C++ usa primitivi futuri/promessi per questu. In STL, sfurtunatamenti, sò implementati assai modestamente. Per esempiu, nò tandu metudu, chì applica a funzione passata à u risultatu di u futuru quandu diventa dispunibule. In u nostru casu, un tali mètudu hè necessariu di cunvertisce u risultatu in u furmatu di a nostra biblioteca. Per aggira stu prublema, avemu avutu à implementà u nostru propiu pool di fili simplici, postu chì à a dumanda di u cliente ùn pudemu micca aduprà biblioteche pesanti di terze parti cum'è Boost.

A nostra implementazione allora funziona cusì. Quandu si chjama, una prumessa supplementu / futuru paru hè creatu. U novu futuru hè tornatu, è u passatu hè postu cù a funzione currispondente è una prumessa supplementaria in a fila. Un filu da a piscina selezziunà parechji futuri da a fila è li sonda cù wait_for. Quandu u risultatu hè dispunibule, a funzione currispondente hè chjamata è u so valore di ritornu hè passatu à a prumessa.

Avemu usatu u listessu pool di fili per eseguisce dumande à etcd è Consul. Questu significa chì e biblioteche sottostanti ponu accede da parechje fili diffirenti. ppconsul ùn hè micca un filu sicuru, cusì i chjamati sò prutetti da serrature.
Pudete travaglià cù grpc da parechji fili, ma ci sò suttilità. In eccd watches sò implementati via flussi grpc. Quessi sò canali bidirezionali per i missaghji di un certu tipu. A biblioteca crea un filu unicu per tutti l'orologi è un filu unicu chì processa i missaghji entranti. Allora grpc pruibisce scrittura parallela à u flussu. Questu significa chì quandu inizializza o sguassate un orologio, duvete aspittà finu à chì a dumanda precedente hà finitu di mandà prima di mandà u prossimu. Avemu aduprà per a sincronizazione variabili cundiziunali.

U risultatu

Vede per voi stessu: liboffkv.

A nostra squadra: Raed Romanov, Ivan Glushenkov, Dmitri Kamaldinov, Victor Krapivensky, Vitali Ivanin.

Source: www.habr.com

Add a comment