Pra ndaj, po mbledhni metrika. Ashtu si ne. Edhe ne mbledhim metrika. Sigurisht, ato të nevojshme për biznesin. Sot do të flasim për lidhjen e parë të sistemit tonë të monitorimit - një server agregimi i përputhshëm me statsd. , përse e shkruam dhe pse u tërhoqëm nga brubeck.

Nga artikujt tanë të mëparshëm (, ) mund të mësoni se deri në një moment të caktuar ne i mbledhim etiketat përmes . Ai është shkruar në C. Në aspektin e kodit - është aq i thjeshtë sa një korke (kjo është e rëndësishme kur doni të kontribuoni) dhe, më e rëndësishmja, përballon pa ndonjë problem volumin tonë deri në 2 milion metrika në sekondë (MPS) në pikë. Dokumentacioni pret 4 milion MPS me yll. Kjo do të thotë se do të merrni numrin e deklaruar, nëse e konfiguroni në mënyrë të duhur rrjetin në Linux. (Sa MPS mund të merrni, nëse e lë ne web siç është, nuk e dimë). Pavarësisht këtyre avantazheve, kishim disa ankesat serioze për brubeck.
Ankesa 1. Github - zhvilluesi i projektit - ka ndaluar mbështetje: nuk publikojnë patches e fiksimeve, nuk pranojnë PR-të tona (e as të tjerëve). Në muajt e fundit (diku që nga shkurti-marsi 2018) aktiviteti u rikthye, por para kësaj kishte pothuajse 2 vjet qetësi totale. Për më tepër, projekti është zhvilluar , që mund të bëhet një pengesë serioze për implementimin e mundësive të reja.
Ankesa 2. Saktësia e llogaritjeve. Brubeck mbledh vetëm 65536 vlera për agregim. Në rastin tonë, për disa metrika gjatë periudhës së agregimit (30 sekonda) mund të vijnë shumë më tepër vlera (1,527,392 në pikë). Si rezultat i këtij sampling, vlerat e maksimumeve dhe minimumeve duken të pa dobishme. Për shembull, ashtu si kjo:

Siç ishte

Si duhet të ishte
Për të njëjtat arsye, shumat gjithashtu llogariten në mënyrë të pasaktë. Shtoni këtu një bug me mbingarkesën e float-it 32-bit, që e çon serverin në segfault kur merr një metrikë që duket harbuar, dhe bëhet shumë mirë. Bug-u, për më tepër, nuk është rregulluar ende.
Dhe, përfundimisht, Ankesa X. Në momentin e shkruar të artikullit, jemi të gatshëm ta paraqesim atë të gjithë 14 implementimeve më se pak funksionale të statsd, që kemi arritur të gjejmë. Le të supozojmë se një infrastrukturë e veçantë është rritur aq shumë, sa të pranojë 4 milion MPS tashmë nuk është e mjaftueshme. Ose le të themi se ende nuk është rritur, por metrikat për ju janë aq të rëndësishme saqë madje edhe humbjet e shkurtra, 2-3 minuta, në grafikun tuaj mund të bëhen kritike dhe të shkaktojnë episode të pa kontrolluara depresioni ndër menaxherë. Duke ditur se trajtimi i depresionit është një punë e parëndësishme, nevojiten zgjidhje teknike.
Së pari, qëndrueshmëria, që ndonjë problem i papritur në server të mos shkaktojë një apokalips të zombi psikiatrik në zyrë. Së dyti, shkallëzimi, për të pasur mundësi të pranojmë më shumë se 4 milion MPS, pa nxjerrë në pah thellësitë e shtresave të rrjetit në Linux dhe duke u rritur pa stres "në gjerësi" deri në përmasat e nevojshme.
Duke pasur parasysh se kishim rezerva për shkallëzim, vendosëm të fillojmë nga qëndrueshmëria. "O! Qëndrueshmëria! Kjo është e thjeshtë, ne e dimë këtë," menduam ne dhe ngritëm 2 serverë, duke ngjitur në secilin një kopje të brubeck. Për këtë, na duhej të kopjonim trafikun me metrika në të dy serverët dhe madje të shkruajmë për këtë . Problemin e qëndrueshmërisë e zgjidhëm, por... jo shumë mirë. Fillimisht gjithçka dukej mjaft mirë: çdo brubeck mbledh versionin e tij të agregimit, shkruan të dhënat në Graphite çdo 30 sekonda, duke ripërsëritur intervalin e vjetër (kjo bëhet në anën e Graphite). Nëse ndonjëherë një server dështoi, gjithmonë kemi një të dytë me një kopje të dhënash të agreguara. Por problemi është: nëse serveri dështon, grafiku merr "saw". Kjo është e lidhur me faktin se intervalet 30-sekondëshe në brubeck nuk janë të sinkronizuara, dhe në momentin e rënies, njëra prej tyre nuk ripërsëritet. Në momentin e nisjes së serverit të dytë ndodh po e njëjta gjë. Plotësisht e tolerueshme, por dëshirojmë më mirë! Problemi i shkallëzimit gjithashtu nuk ka zhdukur. Të gjitha metrikat përsëri "fluturojnë" në një server të vetëm, dhe kështu ne jemi të kufizuar nga ato të njëjtat 2-4 milion MPS në varësi të kapacitetit të rrjetit.
Nëse mendoni pak rreth problemit dhe njëkohësisht koponi pak borën me lopatë, një ide e tillë duket e qartë: na nevojitet një statsd që di të punojë në mënyrë të shpërndarë. Domethënë, një që implementon sinkronizimin midis node-ve sipas kohës dhe metrikave. "Sigurisht, një zgjidhje e tillë ekziston" - thamë ne dhe filluam të kërkojmë... Dhe nuk gjetëm asgjë. Duke shqyrtuar dokumentacionin për statsd të ndryshëm ( Në momentin 11.12.2017, ne nuk gjetëm asgjë. Më duket se as zhvilluesit, as përdoruesit e këtyre zgjidhjeve me KAQ shumë metrika nuk kanë përballuar akoma, përndryshe do të kishin shpikur ndonjë gjë.
Dhe këtu na kujtohet statsd-i "lojtari" — bioyino, i cili u shkrua në hackathon vetëm për argëtim (emri i projektit u gjenerua nga një skenar para fillimit të hackathon-it) dhe e kuptuam se na duhej një statsd i vet. Pse?
- sepse në botë ka shumë pak klonë të statsd,
- sepse mund të sigurojmë disponueshmëri dhe shkallëzim të dëshiruar ose të ngjashëm (përfshirë sinqronizimin e metrikave të agreguara midis serverëve dhe zgjidhjen e problemeve të konflikteve gjatë dërgimit),
- sepse mund të llogarisim metrikat më saktë se sa e bën brubeck,
- sepse mund të mbledhim statistika më të detajuara, të cilat brubeck nuk na i siguronte pothuajse fare,
- sepse na u dha mundësia të programojmë aplikacionin tonë të hiperperformancës, i cili nuk do të përsërisë plotësisht arkitekturën e një aplikacioni tjetër të tillë të hiperperformancës.
Në çfarë duhet ta shkruajmë? Sigurisht në Rust. Pse?
- sepse tashmë kishte një prototip zgjidhjeje,
- sepse autori i artikullit në atë kohë tashmë dinte Rust dhe donte të shkruante diçka për prodhim me mundësinë për ta publikuar atë në open-source,
- sepse gjuhët me GC nuk na përshtaten për shkak të natyrës së trafikut që marrim (praktiisht realtime) dhe pauzat e GC janë praktikisht të papranueshme,
- sepse na nevojitet performancë maksimale, të krahasueshme me C
- sepse Rust na ofron koncurrencë pa frikë, dhe nëse do t’i fillonim ta shkruanim këtë në C/C++, do të përballeshim me edhe më shumë, sesa me brubeck, dobësi, mbingarkime buferësh, kushte garancie dhe fjalë të tjera të frikshme.
Ishte gjithashtu një argument kundër Rust. Kompania nuk kishte përvojë në krijimin e projekteve në Rust, dhe tani ne gjithashtu nuk planifikojmë ta përdorim atë në projektin tonë kryesor. Prandaj, kishte shqetësime të konsiderueshme se nuk do të arrinim asgjë, por vendosëm të rrezikojmë dhe të provojmë.
Kalonin ditët...
Më në fund, pas disa përpjekjeve të dështuara, versione e para funksionale ishte gati. Çfarë dolën? Doli kështu.

Çdo nod merr grupin e tij të metrikave dhe i akumulon ato, pa i agreguar metrikat për ato lloje ku për agregimin përfundimtar kërkohet grupi i plotë. Nodat janë të lidhur mes tyre me një protokoll ndarjeje të shpërndarë që i lejon të zgjedhin atë të vetmen (këtë herë qamit) që është e denjë për të dërguar metrikat e Madhe. Aktualisht ky problem zgjidhet me mjete, por në të ardhmen ambicjet e autorit shtrihen deri në Raft, ku ajo e denjë do të jetë, sigurisht, nodi-lider i konsensusit. Përveç konsensusit, nodat shpesh (me default një herë në sekondë) dërgojnë fqinjëve të tyre ato pjesë të metrikave të paragreguara, të cilat arritën të akumulojnë në atë sekondë. Kështu, shkallëzimi dhe disponueshmëria mbeten — çdo nod përmban akoma grupin e plotë të metrikave, por metrikat tani dërgohen të agreguara, përmes TCP dhe me kodim në një protokoll binar, kështu që kostot për kopjimin krahasuar me UDP reduktohen ndjeshëm. Pavarësisht numrit të madh të metrikave hyrëse, akumulimi kërkon shumë pak memorie dhe akoma më pak CPU. Për metrikan tonë që comprimon mirë, kjo është vetëm disa dhjetëra megabajt të dhënash. Një bonus shtesë përfitojmë mungesën e ripersëritjeve të panevojshme të të dhënave në Graphite, siç ndodhi me burbeck.
Paketa UDP me metrika janë të shpërndara midis nodave në pajisjen rrjetëse përmes një Round Robin të thjeshtë. Natyrisht, pajisja rrjetësore nuk analizon përmbajtjen e paketimeve dhe prandaj mund të përballojë shumë më tepër se 4M paketa në sekondë, e nuk flasim për metrikat, për të cilat ajo nuk di asgjë. Nëse merret parasysh se metrikat nuk vijnë një nga një në çdo paketë, atëherë nuk parashikojmë probleme me performancën në këtë pikë. Në rast të rënies së serverit pajisja rrjetësore e zbulohet shpejt (brenda 1-2 sekondash) dhe e heq serverin e rënë nga rotacioni. Si rezultat, nodat pasive (dmth. që nuk janë lider), mund të fikën dhe ndizet duke mos e vërejtur pothuajse rënien në grafike. Maksimumi që humbasim — është një pjesë e metrikave që erdhën në sekondën e fundit. Humbja/ndalimi/ribashkimi i papritur i liderit do të pikturojnë akoma një anomali të vogël (mbi një interval 30-sekondësh, akoma është jashtë sinkronizimit), por nëse ka lidhje midis nodave mund të minimizohen edhe këto probleme, për shembull, duke dërguar paketa sinkronizuese.
Pak më shumë në lidhje me strukturën e brendshme. Aplikacioni, sigurisht, është multithread, por arkitektura e rrjedhave ndryshon nga ajo e përdorur në brubeck. Rrjedhat në brubeck janë të njëjta — secila prej tyre përgjigjet njëkohësisht për mbledhjen e informacionit dhe për agregimin. Në bioyino, rrjedhat e punës (workers) janë të ndara në dy grupe: ato përgjegjëse për rrjetin dhe ato përgjegjëse për agregimin. Kjo ndarje lejon menaxhim më fleksibël të aplikacionit në varësi të llojit të metrikave: aty ku kërkohet agregim intensiv, mund të shtohen agregatorët, ndërsa aty ku ka shumë trafik rrjeti — të shtohet numri i rrjedhave rrjet. Momentalisht, në serverat tanë punojmë me 8 rrjedha rrjeti dhe 4 agreguese.
Pjesa llogaritëse (përgjegjëse për agregimin) është mjaft e mërzitshme. Buffers e mbushura me rrjedha rrjeti shpërndahen midis rrjedhave llogaritëse, ku më pas paraqiten dhe agregohen. Metrikat kërkohen për t'u dorëzuar në node të tjera. Të gjitha këto, përfshirë dërgimin e të dhënave midis node-ve dhe punën me Consul, kryhen në mënyrë asinkrone, duke punuar mbi frameworkun .
Shumë më shumë probleme në zhvillim ka shkaktuar pjesa rrjetore, përgjegjëse për marrjen e metrikave. Qëllimi kryesor i ndarjes së rrjedhave rrjetore në entitete të veçanta ishte përpjekja për të reduktuar kohën që rrjedha shpenzon nuk në leximin e të dhënave nga socket. Opcioni për përdorimin e UDP asinkron dhe recvmsg u hodh poshtë shpejt: e para konsumon shumë CPU në hapësirën e përdoruesit për përpunimin e ngjarjeve, e dyta — shumë shkëputje konteksti. Prandaj tani po përdoret me buffers të mëdhenj (dhe buffers, zotërinj oficerë, nuk janë thjesht ndonjë gjë!). Mbështetje për UDP-në tradicionale është lënë për raste të papregatitura, kur nuk ka nevojë për recvmmsg. Në modin multimessage arrihet qëllimi kryesor: shumica dërrmuese e kohës, rrjedha rrjet shkarkon radhën e OS — lexon të dhënat nga socket dhe i kalon ato në buffer-in e hapësirës së përdoruesit, duke kaluar vetëm hera-herës për të dorëzuar buffer-in e mbushur tek agregatorët. Radhët në socket praktikisht nuk grumbullohen, numri i paketave të përjashtuara rritet praktikisht jo.
Shënim
Në konfigurimin e paracaktuar, madhësia e buffer-it është vendosur mjaft e madhe. Nëse ndonjëherë vendosni ta provoni serverin vetë, ndoshta do të përballeni me faktin se pas dërgimit të një sasi të vogël metrikash, ato nuk do të arrijnë në Graphite, duke mbetur në buffer-in e rrjedhës rrjetore. Për punën me një numër të vogël metrikash, duhet të vendosni në konfigurimin e bufsize dhe task-queue-size vlera më të vogla.
Për fund — disa grafika për adhuruesit e grafikëve.
Statistika e numrit të metrikave në hyrje për secilin server: mbi 2 milion MPS.

Çkyçja e një nga node-ve dhe shpërndarja e metrikave në hyrje.

Statistika e metrikave në dalje: gjithmonë dërgon vetëm një node — raidboss.

Statistika e punës së secilës node me përllogaritjen e gabimeve në modulet e ndryshme të sistemit.

Detajimi i metrikave në hyrje (emrat e metrikave janë të fshira).

Çfarë planifikojmë të bëjmë më tej me të gjitha këto? Sigurisht, të shkruajmë kod, është...! Projekti ishte planifikuar fillimisht si open-source dhe do të mbetet i tillë gjatë gjithë jetës së tij. Plani më i afërt është kalimi në versionin tonë të Raft, ndryshimi i protokollit peer në një më të portazhuar, shtimi i statistikave të brendshme, llojeve të reja të metrikave, rregullimi i gabimeve dhe përmirësime të tjera.
Sigurisht, të gjithë ata që dëshirojnë të ndihmojnë në zhvillimin e projektit janë të mirëpritur: krijoni PR, Issues, nëse është e mundur do të përgjigjemi, do të punojmë dhe etj.
Me këtë, siç thonë, that’s all folks, blini elefantët tanë!

Burimi: habr.com
