Bioyino - distributed, scalable metrics aggregator

Kaya mangolekta ka ng mga sukatan. Bilang tayo. Nangongolekta din kami ng mga sukatan. Siyempre, kailangan para sa negosyo. Ngayon ay pag-uusapan natin ang tungkol sa pinakaunang link ng aming monitoring system - isang server ng pagsasama-sama ng statsd bioyino, kung bakit namin ito isinulat at kung bakit namin tinalikuran ang brubeck.

Bioyino - distributed, scalable metrics aggregator

Mula sa aming mga nakaraang artikulo (1, 2) maaari mong malaman na hanggang sa ilang oras ay nakolekta namin ang mga marka gamit brubeck. Ito ay nakasulat sa C. Mula sa isang code point of view, ito ay kasing simple ng isang plug (ito ay mahalaga kapag gusto mong mag-ambag) at, higit sa lahat, ito ay humahawak sa aming mga volume na 2 milyong metrics per second (MPS) sa peak nang walang anumang problema. Ang dokumentasyon ay nagsasaad ng suporta para sa 4 na milyong MPS na may asterisk. Nangangahulugan ito na makukuha mo ang nakasaad na figure kung i-configure mo nang tama ang network sa Linux. (Hindi namin alam kung gaano karaming MPS ang makukuha mo kung aalis ka sa network kung saan). Sa kabila ng mga pakinabang na ito, nagkaroon kami ng ilang seryosong reklamo tungkol sa brubeck.

Claim 1. Ang Github, ang developer ng proyekto, ay huminto sa pagsuporta dito: pag-publish ng mga patch at pag-aayos, pagtanggap sa amin at (hindi lamang sa amin) PR. Sa nakalipas na ilang buwan (sa isang lugar mula Pebrero-Marso 2018), nagpatuloy ang aktibidad, ngunit bago iyon mayroong halos 2 taon ng kumpletong kalmado. Bilang karagdagan, ang proyekto ay binuo para sa panloob na pangangailangan ng Gihub, na maaaring maging isang seryosong balakid sa pagpapakilala ng mga bagong feature.

Claim 2. Katumpakan ng mga kalkulasyon. Kinokolekta ng Brubeck ang kabuuang 65536 na halaga para sa pagsasama-sama. Sa aming kaso, para sa ilang sukatan, sa panahon ng pagsasama-sama (30 segundo), higit pang mga halaga ang maaaring dumating (1 sa tuktok). Bilang resulta ng sampling na ito, ang maximum at minimum na mga halaga ay tila walang silbi. Halimbawa, tulad nito:

Bioyino - distributed, scalable metrics aggregator
Gaya noon

Bioyino - distributed, scalable metrics aggregator
Paano ito dapat

Para sa parehong dahilan, ang mga halaga ay karaniwang nakalkula nang hindi tama. Magdagdag dito ng bug na may 32-bit float overflow, na karaniwang nagpapadala sa server sa segfault kapag tumatanggap ng tila inosenteng sukatan, at lahat ay nagiging mahusay. Ang bug, sa pamamagitan ng paraan, ay hindi naayos.

At, sa wakas, I-claim ang X. Sa oras ng pagsulat, handa kaming ipakita ito sa lahat ng 14 higit pa o mas kaunting gumaganang mga implementasyon ng istatistika na aming nahanap. Isipin natin na ang ilang solong imprastraktura ay lumago nang husto kaya hindi na sapat ang pagtanggap ng 4 milyong MPS. O kahit na hindi pa ito lumalago, ngunit ang mga sukatan ay napakahalaga na sa iyo na kahit na maikli, 2-3 minutong pagbaba sa mga chart ay maaari nang maging kritikal at magdulot ng hindi malulutas na depresyon sa mga manager. Dahil ang paggamot sa depresyon ay isang walang pasasalamat na gawain, kailangan ang mga teknikal na solusyon.

Una, fault tolerance, upang ang isang biglaang problema sa server ay hindi maging sanhi ng isang psychiatric zombie apocalypse sa opisina. Pangalawa, ang pag-scale upang makatanggap ng higit sa 4 na milyong MPS, nang walang malalim na paghuhukay sa stack ng network ng Linux at mahinahong lumalaki "sa lawak" sa kinakailangang laki.

Dahil mayroon kaming puwang para sa pag-scale, nagpasya kaming magsimula sa fault tolerance. "TUNGKOL! Fault tolerance! Simple lang, kaya namin,” naisip namin at naglunsad ng 2 server, na nagtaas ng kopya ng brubeck sa bawat isa. Upang gawin ito, kinailangan naming kopyahin ang trapiko na may mga sukatan sa parehong mga server at magsulat para dito maliit na utility. Nalutas namin ang problema sa fault tolerance dito, ngunit... hindi masyadong maayos. Sa una ay tila maganda ang lahat: ang bawat brubeck ay nangongolekta ng sarili nitong bersyon ng pagsasama-sama, nagsusulat ng data sa Graphite isang beses bawat 30 segundo, na pinatungan ang lumang agwat (ginagawa ito sa gilid ng Graphite). Kung biglang nabigo ang isang server, palagi kaming may pangalawa na may sariling kopya ng pinagsama-samang data. Ngunit narito ang problema: kung nabigo ang server, lilitaw ang isang "saw" sa mga graph. Ito ay dahil sa ang katunayan na ang 30-segundong agwat ng brubeck ay hindi naka-synchronize, at sa sandali ng pag-crash ay hindi na-overwrite ang isa sa mga ito. Kapag nagsimula ang pangalawang server, ganoon din ang mangyayari. Medyo matatagalan, ngunit gusto ko ng mas mahusay! Hindi rin nawala ang problema sa scalability. Ang lahat ng mga sukatan ay "lumipad" pa rin sa isang server, at samakatuwid kami ay limitado sa parehong 2-4 milyong MPS, depende sa antas ng network.

Kung iniisip mo nang kaunti ang problema at sabay na maghukay ng niyebe gamit ang isang pala, kung gayon ang sumusunod na malinaw na ideya ay maaaring maisip: kailangan mo ng statsd na maaaring gumana sa distributed mode. Iyon ay, isa na nagpapatupad ng pag-synchronize sa pagitan ng mga node sa oras at mga sukatan. "Siyempre, malamang na umiiral na ang ganoong solusyon," sabi namin at pumunta sa Google.... At wala silang nakita. Matapos dumaan sa dokumentasyon para sa iba't ibang statsd (https://github.com/etsy/statsd/wiki#server-implementations noong Disyembre 11.12.2017, XNUMX), wala kaming nakitang ganap. Tila, ang mga developer o ang mga gumagamit ng mga solusyon na ito ay hindi pa nakatagpo ng napakaraming sukatan, kung hindi, tiyak na magkakaroon sila ng isang bagay.

At pagkatapos ay naalala namin ang tungkol sa "laruan" statsd - bioyino, na isinulat sa Just for Fun hackathon (ang pangalan ng proyekto ay nabuo ng script bago magsimula ang hackathon) at napagtanto namin na kailangan namin ang aming sariling statsd. Para saan?

  • dahil napakakaunting mga statsd clone sa mundo,
  • dahil posibleng ibigay ang ninanais o malapit sa nais na fault tolerance at scalability (kabilang ang pag-synchronize ng pinagsama-samang sukatan sa pagitan ng mga server at paglutas ng problema sa pagpapadala ng mga salungatan),
  • dahil posibleng kalkulahin ang mga sukatan nang mas tumpak kaysa sa ginagawa ng brubeck,
  • dahil maaari kang mangolekta ng mas detalyadong mga istatistika sa iyong sarili, na halos hindi ibinigay sa amin ng brubeck,
  • dahil nagkaroon ako ng pagkakataon na i-program ang sarili kong hyperperformance distributed scale lab application, na hindi ganap na uulitin ang arkitektura ng isa pang katulad na hyperfor... well, iyon lang.

Ano ang isusulat? Siyempre, sa Rust. Bakit?

  • dahil mayroon nang prototype na solusyon,
  • dahil kilala na ng may-akda ng artikulo si Rust noong panahong iyon at sabik na magsulat dito para sa produksyon na may pagkakataong ilagay ito sa open-source,
  • dahil ang mga wikang may GC ay hindi angkop para sa amin dahil sa likas na katangian ng trapikong natanggap (halos realtime) at ang mga pag-pause ng GC ay halos hindi katanggap-tanggap,
  • dahil kailangan mo ng pinakamataas na pagganap na maihahambing sa C
  • dahil binibigyan tayo ni Rust ng walang takot na pagkakasabay, at kung sinimulan natin itong isulat sa C/C++, mas marami pa sana tayong mga kahinaan, buffer overflow, kundisyon ng lahi at iba pang nakakatakot na salita kaysa brubeck.

Nagkaroon din ng pagtatalo laban kay Rust. Ang kumpanya ay walang karanasan sa paglikha ng mga proyekto sa Rust, at ngayon ay hindi rin namin planong gamitin ito sa pangunahing proyekto. Samakatuwid, may mga malubhang takot na walang mangyayari, ngunit nagpasya kaming kumuha ng pagkakataon at sinubukan.

Lumipas ang oras...

Sa wakas, pagkatapos ng ilang mga nabigong pagtatangka, handa na ang unang gumaganang bersyon. Anong nangyari? Ito ang nangyari.

Bioyino - distributed, scalable metrics aggregator

Ang bawat node ay tumatanggap ng sarili nitong hanay ng mga sukatan at iniipon ang mga ito, at hindi nagsasama-sama ng mga sukatan para sa mga uri kung saan ang kanilang buong hanay ay kinakailangan para sa panghuling pagsasama-sama. Ang mga node ay konektado sa isa't isa sa pamamagitan ng ilang uri ng ipinamahagi na lock protocol, na nagbibigay-daan sa iyong piliin sa kanila ang isa lamang (dito kami sumigaw) na karapat-dapat na magpadala ng mga sukatan sa Dakila. Ang problemang ito ay kasalukuyang nireresolba ng Consul, ngunit sa hinaharap ang mga ambisyon ng may-akda ay umaabot sa sariling pagpapatupad Balsa, kung saan ang pinaka-karapat-dapat ay, siyempre, ang node ng pinuno ng pinagkasunduan. Bilang karagdagan sa consensus, madalas na ipinapadala ng mga node (isang beses bawat segundo bilang default) sa kanilang mga kapitbahay ang mga bahagi ng paunang pinagsama-samang sukatan na nagawa nilang kolektahin sa segundong iyon. Lumalabas na ang scaling at fault tolerance ay napanatili - ang bawat node ay may hawak pa ring buong hanay ng mga sukatan, ngunit ang mga sukatan ay ipinapadala nang pinagsama-sama, sa pamamagitan ng TCP at na-encode sa isang binary protocol, kaya ang mga gastos sa pagdoble ay makabuluhang nababawasan kumpara sa UDP. Sa kabila ng medyo malaking bilang ng mga papasok na sukatan, ang akumulasyon ay nangangailangan ng napakakaunting memorya at mas kaunting CPU. Para sa aming mataas na compressible mertics, ito ay ilang sampu ng megabytes ng data. Bilang karagdagang bonus, wala kaming natatanggap na hindi kinakailangang pag-rewrite ng data sa Graphite, tulad ng nangyari sa burbeck.

Ang mga UDP packet na may mga sukatan ay hindi balanse sa pagitan ng mga node sa network equipment sa pamamagitan ng isang simpleng Round Robin. Siyempre, ang network hardware ay hindi nag-parse ng mga nilalaman ng mga packet at samakatuwid ay maaaring humila ng higit sa 4M na mga packet bawat segundo, hindi pa banggitin ang mga sukatan na wala itong nalalaman. Kung isasaalang-alang namin na ang mga sukatan ay hindi dumarating nang paisa-isa sa bawat packet, hindi namin nahuhulaan ang anumang mga problema sa pagganap sa lugar na ito. Kung nag-crash ang isang server, mabilis na matutukoy ng network device (sa loob ng 1-2 segundo) ang katotohanang ito at aalisin ang na-crash na server mula sa pag-ikot. Bilang resulta nito, ang mga passive (i.e., non-leader) na mga node ay maaaring i-on at i-off nang halos hindi napapansin ang mga drawdown sa mga chart. Ang maximum na nawala sa amin ay bahagi ng mga sukatan na dumating sa huling segundo. Ang isang biglaang pagkawala/pagsara/pagpalit ng isang pinuno ay lilikha pa rin ng isang maliit na anomalya (ang 30 segundong pagitan ay wala pa rin sa sync), ngunit kung mayroong komunikasyon sa pagitan ng mga node, ang mga problemang ito ay maaaring mabawasan, halimbawa, sa pamamagitan ng pagpapadala ng mga packet ng pag-synchronize .

Kaunti tungkol sa panloob na istraktura. Ang application ay, siyempre, multithreaded, ngunit ang threading architecture ay naiiba mula sa ginamit sa brubeck. Ang mga thread sa brubeck ay pareho - bawat isa sa kanila ay responsable para sa parehong pagkolekta at pagsasama-sama ng impormasyon. Sa bioyino, ang mga manggagawa ay nahahati sa dalawang grupo: ang mga responsable para sa network at ang mga responsable para sa pagsasama-sama. Binibigyang-daan ka ng dibisyong ito na mas madaling pamahalaan ang application depende sa uri ng mga sukatan: kung saan kinakailangan ang masinsinang pagsasama-sama, maaari kang magdagdag ng mga aggregator, kung saan mayroong maraming trapiko sa network, maaari mong idagdag ang bilang ng mga daloy ng network. Sa ngayon, sa aming mga server nagtatrabaho kami sa 8 network at 4 na aggregation flow.

Ang bahagi ng pagbibilang (responsable para sa pagsasama-sama) ay medyo boring. Ang mga buffer na napunan ng mga daloy ng network ay ipinamamahagi sa mga pagbibilang ng mga daloy, kung saan ang mga ito ay pagkatapos ay na-parse at pinagsama-sama. Kapag hiniling, ibinibigay ang mga sukatan para sa pagpapadala sa iba pang mga node. Ang lahat ng ito, kabilang ang pagpapadala ng data sa pagitan ng mga node at pagtatrabaho sa Consul, ay ginaganap nang asynchronously, na tumatakbo sa framework tokio.

Higit pang mga problema sa panahon ng pag-unlad ay sanhi ng bahagi ng network na responsable para sa pagtanggap ng mga sukatan. Ang pangunahing layunin ng paghihiwalay ng mga daloy ng network sa magkakahiwalay na entity ay ang pagnanais na bawasan ang oras na ginugugol ng isang daloy hindi para basahin ang data mula sa socket. Mabilis na nawala ang mga opsyon na gumagamit ng asynchronous na UDP at regular na recvmsg: ang una ay gumagamit ng masyadong maraming user-space na CPU para sa pagproseso ng kaganapan, ang pangalawa ay nangangailangan ng masyadong maraming switch ng konteksto. Samakatuwid ito ay ginagamit ngayon recvmmsg na may malalaking buffer (at mga buffer, mga ginoong opisyal, ay wala sa iyo!). Ang suporta para sa regular na UDP ay nakalaan para sa mga magaan na kaso kung saan hindi kailangan ang recvmmsg. Sa multimessage mode, posibleng makamit ang pangunahing bagay: sa karamihan ng oras, ang network thread ay nag-rake ng OS queue - nagbabasa ng data mula sa socket at inililipat ito sa userspace buffer, paminsan-minsan lamang lumilipat sa pagbibigay ng punong buffer sa mga aggregator. Ang pila sa socket ay halos hindi maipon, ang bilang ng mga nahulog na packet ay halos hindi lumalaki.

Nota

Sa mga default na setting, ang laki ng buffer ay nakatakda sa medyo malaki. Kung bigla kang magpasya na subukan ang server sa iyong sarili, maaari kang makatagpo ng katotohanan na pagkatapos magpadala ng isang maliit na bilang ng mga sukatan, hindi sila darating sa Graphite, na natitira sa network stream buffer. Upang gumana sa isang maliit na bilang ng mga sukatan, kailangan mong itakda ang bufsize at task-queue-size sa mas maliliit na value sa config.

Panghuli, ilang chart para sa mga mahilig sa chart.

Mga istatistika sa bilang ng mga papasok na sukatan para sa bawat server: higit sa 2 milyong MPS.

Bioyino - distributed, scalable metrics aggregator

Hindi pagpapagana ng isa sa mga node at muling pamamahagi ng mga papasok na sukatan.

Bioyino - distributed, scalable metrics aggregator

Mga istatistika sa papalabas na sukatan: isang node lang ang laging ipinapadala - ang raid boss.

Bioyino - distributed, scalable metrics aggregator

Mga istatistika ng pagpapatakbo ng bawat node, isinasaalang-alang ang mga error sa iba't ibang mga module ng system.

Bioyino - distributed, scalable metrics aggregator

Pagdetalye ng mga papasok na sukatan (nakatago ang mga pangalan ng sukatan).

Bioyino - distributed, scalable metrics aggregator

Ano ang pinaplano naming gawin sa lahat ng ito sa susunod? Siyempre, magsulat ng code, sumpain...! Ang proyekto ay orihinal na binalak na maging open-source at mananatili ito sa buong buhay nito. Kasama sa aming mga agarang plano ang paglipat sa sarili naming bersyon ng Raft, pagpapalit ng peer protocol sa isang mas portable, pagpapakilala ng karagdagang mga panloob na istatistika, mga bagong uri ng sukatan, pag-aayos ng bug at iba pang mga pagpapahusay.

Siyempre, malugod na inaanyayahan ang lahat na tumulong sa pagbuo ng proyekto: lumikha ng PR, Mga Isyu, kung maaari ay tutugon kami, pagbutihin, atbp.

With that being said, yun lang mga kabayan, bilhin mo na ang ating mga elepante!



Pinagmulan: www.habr.com

Magdagdag ng komento