Bioyino - distribuerad, skalbar statistikaggregator

Så du samlar in mätvärden. Som vi är. Vi samlar också in mätvärden. Naturligtvis nödvändigt för affärer. Idag kommer vi att prata om den allra första länken i vårt övervakningssystem - en statsd-kompatibel aggregeringsserver bioyino, varför vi skrev det och varför vi övergav brubeck.

Bioyino - distribuerad, skalbar statistikaggregator

Från våra tidigare artiklar (1, 2) kan du ta reda på att vi fram till en tid samlade märken med hjälp av Brubeck. Den är skriven i C. Ur kodsynpunkt är den enkel som en plugg (detta är viktigt när du vill bidra) och, viktigast av allt, den hanterar våra volymer på 2 miljoner mätvärden per sekund (MPS) på topp utan problem. Dokumentationen anger stöd för 4 miljoner MPS med en asterisk. Det betyder att du får den angivna siffran om du konfigurerar nätverket korrekt på Linux. (Vi vet inte hur många MPS du kan få om du lämnar nätverket som det är). Trots dessa fördelar hade vi flera allvarliga klagomål om brubeck.

Påstående 1. Github, utvecklaren av projektet, slutade stödja det: publicera patchar och korrigeringar, acceptera vår och (inte bara vår) PR. De senaste månaderna (någonstans från februari-mars 2018) har aktiviteten återupptagits, men innan dess var det nästan 2 år av fullständigt lugn. Dessutom utvecklas projektet för interna Gihub-behov, vilket kan bli ett allvarligt hinder för introduktionen av nya funktioner.

Påstående 2. Noggrannhet i beräkningar. Brubeck samlar in totalt 65536 värden för aggregering. I vårt fall, för vissa mätvärden, under aggregeringsperioden (30 sekunder), kan mycket fler värden komma fram (1 527 392 på toppen). Som ett resultat av denna provtagning verkar maximi- och minimivärdena värdelösa. Till exempel, så här:

Bioyino - distribuerad, skalbar statistikaggregator
Som det var

Bioyino - distribuerad, skalbar statistikaggregator
Hur det borde ha varit

Av samma skäl beräknas belopp i allmänhet felaktigt. Lägg till här en bugg med ett 32-bitars float overflow, som vanligtvis skickar servern till ett fel när den tar emot ett till synes oskyldigt mått, och allt blir bra. Felet har förresten inte åtgärdats.

Och, slutligen, Anspråk X. I skrivande stund är vi redo att presentera det för alla 14 mer eller mindre fungerande statistiska implementeringar som vi kunde hitta. Låt oss föreställa oss att någon enskild infrastruktur har vuxit så mycket att det inte längre räcker att acceptera 4 miljoner MPS. Eller även om det inte har vuxit ännu, men statistiken är redan så viktig för dig att även korta, 2-3 minuters nedgångar i diagrammen redan kan bli kritiska och orsaka anfall av oöverstiglig depression bland chefer. Eftersom behandling av depression är en otacksam uppgift, behövs tekniska lösningar.

För det första, feltolerans, så att ett plötsligt problem på servern inte orsakar en psykiatrisk zombieapokalyps på kontoret. För det andra, skala för att kunna acceptera mer än 4 miljoner MPS, utan att gräva djupt i Linux-nätverksstacken och lugnt växa "i bredd" till önskad storlek.

Eftersom vi hade utrymme för skalning bestämde vi oss för att börja med feltolerans. "HANDLA OM! Feltolerans! Det är enkelt, vi kan göra det”, tänkte vi och lanserade 2 servrar, och tog upp en kopia av brubeck på var och en. För att göra detta var vi tvungna att kopiera trafik med mätvärden till båda servrarna och till och med skriva för detta liten nytta. Vi löste feltoleransproblemet med detta, men... inte särskilt bra. Till en början verkade allt bra: varje brubeck samlar in sin egen version av aggregering, skriver data till Graphite en gång var 30:e sekund och skriver över det gamla intervallet (detta görs på grafitsidan). Om en server plötsligt misslyckas har vi alltid en andra med en egen kopia av den samlade informationen. Men här är problemet: om servern misslyckas visas en "såg" på graferna. Detta beror på att brubecks 30-sekundersintervaller inte är synkroniserade, och i ögonblicket för en krasch skrivs inte en av dem över. När den andra servern startar händer samma sak. Ganska uthärdligt, men jag vill ha bättre! Problemet med skalbarhet har inte heller försvunnit. Alla mätvärden "flyger" fortfarande till en enda server, och därför är vi begränsade till samma 2-4 miljoner MPS, beroende på nätverksnivå.

Om du tänker lite på problemet och samtidigt gräver upp snö med en spade, kan följande uppenbara idé dyka upp: du behöver en statsd som kan fungera i distribuerat läge. Det vill säga en som implementerar synkronisering mellan noder i tid och mått. "Naturligtvis finns en sådan lösning förmodligen redan," sa vi och gick till Google... Och de hittade ingenting. Efter att ha gått igenom dokumentationen för olika statistik (https://github.com/etsy/statsd/wiki#server-implementations den 11.12.2017 december XNUMX) hittade vi absolut ingenting. Tydligen har varken utvecklarna eller användarna av dessa lösningar ännu stött på SÅ många mätvärden, annars skulle de definitivt komma på något.

Och sedan kom vi ihåg "leksak" statsd - bioyino, som skrevs på Just for Fun hackathon (namnet på projektet genererades av manuset innan hackathonet började) och insåg att vi akut behövde vår egen statistik. För vad?

  • eftersom det finns för få statd-kloner i världen,
  • eftersom det är möjligt att tillhandahålla önskad eller nära önskad feltolerans och skalbarhet (inklusive synkronisering av aggregerade mätvärden mellan servrar och lösa problemet med att skicka konflikter),
  • eftersom det är möjligt att beräkna mätvärden mer exakt än vad brubeck gör,
  • eftersom du själv kan samla in mer detaljerad statistik, som brubeck praktiskt taget inte försåg oss med,
  • eftersom jag hade en chans att programmera min egen hyperperformance distribuerade skala labbapplikation, som inte helt kommer att upprepa arkitekturen för en annan liknande hyper för... ja, det är det.

Vad ska man skriva på? Naturligtvis i Rust. Varför?

  • eftersom det redan fanns en prototyplösning,
  • eftersom författaren till artikeln redan kände till Rust vid den tiden och var angelägen om att skriva något i den för produktion med möjlighet att lägga den i öppen källkod,
  • eftersom språk med GC inte är lämpliga för oss på grund av arten av den mottagna trafiken (nästan realtid) och GC-pauser är praktiskt taget oacceptabla,
  • eftersom du behöver maximal prestanda jämförbar med C
  • eftersom Rust förser oss med orädd samtidighet, och om vi började skriva det i C/C++, skulle vi ha fått in ännu fler sårbarheter, buffertspill, rasförhållanden och andra skrämmande ord än brubeck.

Det fanns också ett argument mot Rust. Företaget hade ingen erfarenhet av att skapa projekt i Rust, och nu planerar vi inte heller att använda det i huvudprojektet. Därför fanns det allvarliga farhågor för att inget skulle lösa sig, men vi bestämde oss för att chansa och försökte.

Tiden gick...

Slutligen, efter flera misslyckade försök, var den första fungerande versionen klar. Vad hände? Det här är vad som hände.

Bioyino - distribuerad, skalbar statistikaggregator

Varje nod tar emot sin egen uppsättning mätvärden och ackumulerar dem, och aggregerar inte mätvärden för de typer där deras fullständiga uppsättning krävs för slutlig aggregering. Noderna är anslutna till varandra med något slags distribuerat låsprotokoll, vilket låter dig välja bland dem den enda (här grät vi) som är värd att skicka mätvärden till den Store. Detta problem löses för närvarande av Konsul, men i framtiden sträcker sig författarens ambitioner till egen genomförande Flotten, där den mest värdiga kommer naturligtvis att vara konsensusledarnoden. Utöver konsensus skickar noder ganska ofta (en gång per sekund som standard) till sina grannar de delar av föraggregerade mätvärden som de lyckades samla in under den sekunden. Det visar sig att skalning och feltolerans är bevarad - varje nod innehåller fortfarande en hel uppsättning mätvärden, men mätvärdena skickas redan aggregerade, via TCP och kodade till ett binärt protokoll, så dupliceringskostnaderna minskar avsevärt jämfört med UDP. Trots det ganska stora antalet inkommande mätvärden kräver ackumulering väldigt lite minne och ännu mindre CPU. För våra mycket komprimerbara mertics är detta bara några tiotals megabyte data. Som en extra bonus får vi inga onödiga dataomskrivningar i Graphite, vilket var fallet med burbeck.

UDP-paket med mått är obalanserade mellan noder på nätverksutrustning genom en enkel Round Robin. Naturligtvis analyserar inte nätverkshårdvaran innehållet i paket och kan därför dra mycket mer än 4M paket per sekund, för att inte tala om mätvärden som den inte vet något om alls. Om vi ​​tar hänsyn till att mätvärdena inte kommer en i taget i varje paket, förutser vi inga prestandaproblem på denna plats. Om en server kraschar upptäcker nätverksenheten snabbt (inom 1-2 sekunder) detta faktum och tar bort den kraschade servern från rotation. Som ett resultat av detta kan passiva (d.v.s. icke-ledande) noder slås på och av praktiskt taget utan att notera neddragningar på diagrammen. Det maximala vi förlorar är en del av mätvärdena som kom in i sista sekund. En plötslig förlust/avstängning/byte av en ledare skapar fortfarande en mindre anomali (30 sekunders intervallet är fortfarande osynkroniserat), men om det finns kommunikation mellan noder kan dessa problem minimeras, till exempel genom att skicka ut synkroniseringspaket .

Lite om den inre strukturen. Applikationen är naturligtvis flertrådad, men gängningsarkitekturen skiljer sig från den som används i brubeck. Trådarna i brubeck är desamma - var och en av dem ansvarar för både informationsinsamling och aggregering. I bioyino delas arbetare in i två grupper: de som ansvarar för nätverket och de som ansvarar för aggregeringen. Denna uppdelning gör att du kan hantera applikationen mer flexibelt beroende på typen av mätvärden: där intensiv aggregering krävs kan du lägga till aggregatorer, där det finns mycket nätverkstrafik kan du lägga till antalet nätverksflöden. För tillfället arbetar vi på våra servrar i 8 nätverk och 4 aggregeringsflöden.

Räknedelen (ansvarig för aggregering) är ganska tråkig. Buffertar fyllda av nätverksflöden fördelas mellan räknande flöden, där de sedan analyseras och aggregeras. På begäran ges mätvärden för att skicka till andra noder. Allt detta, inklusive att skicka data mellan noder och arbeta med Consul, utförs asynkront och körs på ramverket Tokyo.

Mycket fler problem under utvecklingen orsakades av nätverksdelen som ansvarade för att ta emot mätvärden. Huvudmålet med att separera nätverksflöden i separata enheter var önskan att minska tiden som ett flöde spenderar ingen för att läsa data från uttaget. Alternativ som använder asynkron UDP och vanlig recvmsg försvann snabbt: den första förbrukar för mycket användarutrymme CPU för händelsebearbetning, den andra kräver för många kontextväxlar. Därför används den nu recvmmsg med stora buffertar (och buffertar, herrar officerare, är inget för er!). Stöd för vanlig UDP är reserverat för lätta fall där recvmmsg inte behövs. I multimeddelandeläge är det möjligt att uppnå det viktigaste: den stora majoriteten av tiden räcker nätverkstråden till OS-kön - läser data från sockeln och överför den till användarutrymmesbufferten, och byter bara ibland till att ge den fyllda bufferten till aggregatorer. Kön i uttaget ackumuleras praktiskt taget inte, antalet tappade paket växer praktiskt taget inte.

Notera

I standardinställningarna är buffertstorleken inställd på att vara ganska stor. Om du plötsligt bestämmer dig för att prova servern själv, kan du stöta på det faktum att efter att ha skickat ett litet antal mätvärden kommer de inte att anlända till Graphite, utan kvar i nätverksströmbufferten. För att arbeta med ett litet antal mätvärden måste du ställa in bufsize och task-queue-size till mindre värden i konfigurationen.

Slutligen några diagram för diagramälskare.

Statistik över antalet inkommande mätvärden för varje server: mer än 2 miljoner MPS.

Bioyino - distribuerad, skalbar statistikaggregator

Inaktivera en av noderna och omfördelning av inkommande mätvärden.

Bioyino - distribuerad, skalbar statistikaggregator

Statistik över utgående mätvärden: endast en nod skickar alltid - raidchefen.

Bioyino - distribuerad, skalbar statistikaggregator

Statistik över driften av varje nod, med hänsyn till fel i olika systemmoduler.

Bioyino - distribuerad, skalbar statistikaggregator

Detaljering av inkommande mätvärden (måttnamn är dolda).

Bioyino - distribuerad, skalbar statistikaggregator

Vad planerar vi att göra med allt detta härnäst? Självklart, skriv kod, fan...! Projektet var ursprungligen planerat att vara öppen källkod och kommer att förbli så under hela dess livstid. Våra omedelbara planer inkluderar att byta till vår egen version av Raft, ändra peer-protokollet till ett mer portabelt, införa ytterligare intern statistik, nya typer av mätvärden, buggfixar och andra förbättringar.

Självklart är alla välkomna att hjälpa till i utvecklingen av projektet: skapa PR, Issues, om möjligt svarar vi, förbättrar osv.

Med det sagt, det är allt gott folk, köp våra elefanter!



Källa: will.com

Lägg en kommentar