Pogosta vprašanja o arhitekturi in delu VKontakte

Zgodovina nastanka VKontakte je na Wikipediji, povedal jo je sam Pavel. Zdi se, da jo vsi že poznajo. O notranjosti, arhitekturi in strukturi spletnega mesta na HighLoad++ Pavel mi je povedal leta 2010. Od takrat je ušlo veliko strežnikov, zato bomo posodobili informacije: razrezali jih bomo, vzeli notranjost, stehtali in pogledali napravo VK s tehničnega vidika.

Pogosta vprašanja o arhitekturi in delu VKontakte

Aleksej Akulovič (AterCattus) backend razvijalec v ekipi VKontakte. Prepis tega poročila je skupen odgovor na pogosto zastavljena vprašanja o delovanju platforme, infrastrukturi, strežnikih in interakciji med njimi, ne pa o razvoju, tj. o železu. Ločeno o bazah podatkov in o tem, kaj ima VK namesto tega, o zbiranju dnevnikov in spremljanju celotnega projekta kot celote. Detajli pod krojem.



Več kot štiri leta se ukvarjam z najrazličnejšimi nalogami, povezanimi z zaledjem.

  • Nalaganje, shranjevanje, obdelava, distribucija medijev: video, live streaming, audio, fotografije, dokumenti.
  • Infrastruktura, platforma, spremljanje razvijalcev, dnevniki, regionalni predpomnilniki, CDN, lastniški protokol RPC.
  • Integracija z zunanjimi storitvami: potisna obvestila, razčlenjevanje zunanjih povezav, vir RSS.
  • Pomoč sodelavcem pri različnih vprašanjih, katerih odgovori zahtevajo potapljanje v neznano kodo.

V tem času sem imel roko pri številnih komponentah spletnega mesta. Želim deliti to izkušnjo.

Splošna arhitektura

Vse se, kot običajno, začne s strežnikom ali skupino strežnikov, ki sprejema zahteve.

Prednji strežnik

Prednji strežnik sprejema zahteve prek HTTPS, RTMP in WSS.

HTTPS - to so zahteve za glavno in mobilno spletno različico spletnega mesta: vk.com in m.vk.com ter druge uradne in neuradne stranke našega API-ja: mobilne stranke, sporočila. Imamo sprejem RTMP-promet za oddaje v živo z ločenimi sprednjimi strežniki in WSS- povezave za Streaming API.

Za HTTPS in WSS na strežnikih je vredno nginx. Za oddajanje RTMP smo pred kratkim prešli na lastno rešitev kive, vendar presega obseg poročila. Zaradi odpornosti na napake ti strežniki oglašujejo skupne naslove IP in delujejo v skupinah, tako da se uporabniške zahteve ne izgubijo, če pride do težave na enem od strežnikov. Za HTTPS in WSS ti isti strežniki šifrirajo promet, da prevzamejo del obremenitve procesorja nase.

V nadaljevanju ne bomo govorili o WSS in RTMP, ampak samo o standardnih zahtevah HTTPS, ki so običajno povezane s spletnim projektom.

Backend

Zadaj so običajno zaledni strežniki. Obdelujejo zahteve, ki jih sprednji strežnik prejme od strank.

To kPHP strežniki, na katerem se izvaja HTTP demon, ker je HTTPS že dešifriran. kPHP je strežnik, ki deluje na prefork modeli: zažene glavni proces, kup podrejenih procesov, jim posreduje vtičnice za poslušanje in ti obdelajo njihove zahteve. V tem primeru se procesi ne zaženejo znova med vsako zahtevo uporabnika, ampak preprosto ponastavijo svoje stanje na prvotno stanje z ničelno vrednostjo - zahtevo za zahtevo, namesto ponovnega zagona.

Porazdelitev obremenitve

Vsa naša ozadja niso velika skupina strojev, ki bi lahko obdelala katero koli zahtevo. Mi njih razdeljeni v ločene skupine: splošni, mobilni, api, video, uprizarjanje ... Težava na ločeni skupini strojev ne bo vplivala na vse druge. V primeru težav z videom uporabnik, ki posluša glasbo, sploh ne bo vedel za težave. Kateremu ozadju poslati zahtevo se odloči nginx na sprednji strani glede na konfiguracijo.

Zbiranje metrike in ponovno uravnoteženje

Da bi razumeli, koliko avtomobilov moramo imeti v vsaki skupini, mi ne zanašajte se na QPS. Zaledja so različna, imajo različne zahteve, vsaka zahteva ima drugačno kompleksnost izračunavanja QPS. Zato smo operiramo s konceptom obremenitve strežnika kot celote - na CPU in perf.

Imamo na tisoče takih strežnikov. Vsak fizični strežnik izvaja skupino kPHP za recikliranje vseh jeder (ker je kPHP enoniten).

Strežnik vsebine

CS ali Content Server je shramba. CS je strežnik, ki shranjuje datoteke in tudi obdeluje naložene datoteke ter vse vrste sinhronih opravil v ozadju, ki mu jih dodeli glavni spletni vmesnik.

Imamo na desettisoče fizičnih strežnikov, ki shranjujejo datoteke. Uporabniki radi nalagajo datoteke, mi pa jih radi shranjujemo in delimo. Nekatere od teh strežnikov zapirajo posebni pu/pp strežniki.

pu/pp

Če ste v VK odprli zavihek omrežja, ste videli pu/pp.

Pogosta vprašanja o arhitekturi in delu VKontakte

Kaj je pu/pp? Če zapiramo en strežnik za drugim, sta na voljo dve možnosti za nalaganje in prenos datoteke na strežnik, ki je bil zaprt: neposredno prek http://cs100500.userapi.com/path ali prek vmesnega strežnika - http://pu.vk.com/c100500/path.

Pu je zgodovinsko ime za nalaganje fotografij, pp pa je foto proxy. To pomeni, da je en strežnik za nalaganje fotografij, drugi pa za nalaganje. Zdaj se ne nalagajo samo fotografije, ampak je ime ohranjeno.

Ti strežniki prekiniti seje HTTPSza odstranitev obremenitve procesorja iz pomnilnika. Poleg tega, ker se uporabniške datoteke obdelujejo na teh strežnikih, manj občutljivih informacij je shranjenih na teh napravah, tem bolje. Na primer šifrirni ključi HTTPS.

Ker stroje zapirajo naši drugi stroji, si lahko privoščimo, da jim ne damo "belih" zunanjih IP-jev in dati "sivo". Na ta način smo prihranili pri bazenu IP in zagotovili zaščito strojev pred zunanjim dostopom - preprosto ni IP-ja, da bi vstopil vanj.

Odpornost prek IP-jev v skupni rabi. Kar zadeva toleranco napak, shema deluje enako - več fizičnih strežnikov ima skupni fizični IP, strojna oprema pred njimi pa izbere, kam bo poslala zahtevo. O drugih možnostih bom govoril kasneje.

Sporna točka je, da v tem primeru odjemalec ohrani manj povezav. Če obstaja isti IP za več strojev - z istim gostiteljem: pu.vk.com ali pp.vk.com, ima brskalnik odjemalca omejitev števila hkratnih zahtev enemu gostitelju. A v času vseprisotnega HTTP/2 menim, da to ni več tako relevantno.

Očitna pomanjkljivost sheme je, da mora črpati ves promet, ki gre v shrambo, prek drugega strežnika. Ker črpamo promet prek strojev, še ne moremo črpati velikega prometa, na primer videa, z isto shemo. Posredujemo ga neposredno - ločena direktna povezava za ločene shrambe posebej za video. Lažje vsebine prenašamo preko proxyja.

Pred kratkim smo dobili izboljšano različico proxyja. Zdaj vam bom povedal, v čem se razlikujejo od navadnih in zakaj je to potrebno.

ne

Septembra 2017 je Oracle, ki je pred tem kupil Sun, odpustili ogromno Sunovih zaposlenih. Lahko rečemo, da je podjetje v tem trenutku prenehalo obstajati. Pri izbiri imena za nov sistem so se naši skrbniki odločili pokloniti spominu na to podjetje in novi sistem poimenovali Sun. Med seboj jo imenujemo preprosto »sončki«.

Pogosta vprašanja o arhitekturi in delu VKontakte

pp je imel nekaj težav. En IP na skupino - neučinkovit predpomnilnik. Več fizičnih strežnikov si deli skupni naslov IP in ni mogoče nadzorovati, na kateri strežnik bo šla zahteva. Torej, če različni uporabniki pridejo po isto datoteko, potem, če je na teh strežnikih predpomnilnik, datoteka konča v predpomnilniku vsakega strežnika. To je zelo neučinkovita shema, vendar ni mogoče storiti ničesar.

Posledično - ne moremo deliti vsebine, ker za to skupino ne moremo izbrati določenega strežnika - imajo skupen IP. Tudi zaradi nekih notranjih razlogov, ki jih imamo v regijah ni bilo mogoče namestiti takih strežnikov. Stali so samo v Sankt Peterburgu.

Pri sončkih smo spremenili sistem selekcije. Zdaj imamo anycast usmerjanje: dinamično usmerjanje, anycast, demon za samopreverjanje. Vsak strežnik ima svoj individualni IP, vendar skupno podomrežje. Vse je konfigurirano tako, da se v primeru okvare enega strežnika promet samodejno porazdeli po drugih strežnikih iste skupine. Zdaj je mogoče izbrati določen strežnik, brez odvečnega predpomnjenja, zanesljivost pa ni bila prizadeta.

Podpora za težo. Sedaj si lahko privoščimo vgradnjo strojev različnih moči po potrebi in tudi, v primeru začasnih težav, spremenimo težo delujočih "sončkov", da jih zmanjšamo, tako da "odpočijejo" in ponovno začnejo delovati.

Sharding glede na ID vsebine. Smešna stvar pri razdeljevanju: vsebino običajno razdelimo tako, da različni uporabniki odprejo isto datoteko prek istega »sonca«, tako da imajo skupen predpomnilnik.

Pred kratkim smo lansirali aplikacijo “Deteljica”. To je spletni kviz v neposrednem prenosu, kjer voditelj postavlja vprašanja, uporabniki pa odgovarjajo v realnem času in izbirajo možnosti. Aplikacija ima klepet, kjer lahko uporabniki klepetajo. Lahko se hkrati poveže z oddajo več kot 100 tisoč ljudi. Vsi pišejo sporočila, ki se pošljejo vsem udeležencem, skupaj s sporočilom pa pride tudi avatar. Če pride 100 tisoč ljudi po en avatar v enem "soncu", potem se lahko včasih zavije za oblak.

Da zdržimo rafalne zahteve po isti datoteki, prav za določeno vrsto vsebine vklopimo neumno shemo, ki porazdeli datoteke po vseh razpoložljivih »sončkih« v regiji.

Sonce od znotraj

Povratni proxy na nginx, predpomnilnik v RAM-u ali na hitrih diskih Optane/NVMe. primer: http://sun4-2.userapi.com/c100500/path — povezava do »sonca«, ki se nahaja v četrti regiji, drugi skupini strežnikov. Zapre datoteko poti, ki fizično leži na strežniku 100500.

Cache

V našo arhitekturno shemo dodamo še eno vozlišče - okolje za predpomnjenje.

Pogosta vprašanja o arhitekturi in delu VKontakte

Spodaj je diagram postavitve regionalni predpomnilniki, okoli 20 jih je. To so mesta, kjer se nahajajo predpomnilniki in "sonca", ki lahko predpomnijo promet skozi sebe.

Pogosta vprašanja o arhitekturi in delu VKontakte

To je predpomnjenje večpredstavnostnih vsebin; tu niso shranjeni uporabniški podatki - samo glasba, videoposnetki, fotografije.

Za določitev regije uporabnika smo zbiramo omrežne predpone BGP, objavljene v regijah. V primeru nadomestne nastavitve moramo razčleniti tudi bazo geoip, če IP-ja ne moremo najti po predponah. Regijo določimo glede na IP uporabnika. V kodi lahko pogledamo eno ali več regij uporabnika – tistih točk, ki jim je geografsko najbližje.

Kako deluje?

Priljubljenost datotek štejemo po regijah. Obstaja številka regionalnega predpomnilnika, kjer se nahaja uporabnik, in identifikator datoteke - vzamemo ta par in povečamo oceno z vsakim prenosom.

Hkrati demoni - storitve v regijah - občasno pridejo do API-ja in rečejo: »Jaz sem tak in tak predpomnilnik, dajte mi seznam najbolj priljubljenih datotek v moji regiji, ki še niso na meni. ” API dostavi kup datotek, razvrščenih po ocenah, demon jih prenese, prenese v regije in od tam dostavi datoteke. To je bistvena razlika med pu/pp in Sun od predpomnilnikov: datoteko dajo takoj skozi sebe, tudi če te datoteke ni v predpomnilniku, in predpomnilnik najprej prenese datoteko k sebi, nato pa jo začne vračati.

V tem primeru dobimo vsebino približati uporabnikom in porazdelitev obremenitve omrežja. Na primer, samo iz moskovskega predpomnilnika distribuiramo več kot 1 Tbit/s v konicah.

Vendar obstajajo težave - cache strežniki niso gumijasti. Za zelo priljubljene vsebine včasih ni dovolj omrežja za ločen strežnik. Naši cache strežniki so 40-50 Gbit/s, vendar so vsebine, ki tak kanal popolnoma zamašijo. Približujemo se izvajanju shranjevanja več kot ene kopije priljubljenih datotek v regiji. Upam, da ga bomo uresničili do konca leta.

Ogledali smo si splošno arhitekturo.

  • Prednji strežniki, ki sprejemajo zahteve.
  • Zaledja, ki obdelujejo zahteve.
  • Shrambe, ki jih zapirata dve vrsti posrednikov.
  • Regionalni zakladi.

Kaj manjka v tem diagramu? Seveda baze podatkov, v katerih hranimo podatke.

Baze podatkov ali motorji

Imenujemo jih ne baze podatkov, ampak motorji - Motorji, ker baz podatkov v splošno sprejetem pomenu praktično nimamo.

Pogosta vprašanja o arhitekturi in delu VKontakte

To je nujen ukrep. To se je zgodilo, ker je v letih 2008–2009, ko je VK eksplozivno narasel v priljubljenosti, projekt v celoti deloval na MySQL in Memcache in je prišlo do težav. MySQL se je rad zrušil in poškodoval datoteke, potem pa se ni mogel obnoviti, Memcache pa je postopoma slabšal in ga je bilo treba znova zagnati.

Izkazalo se je, da je vse bolj priljubljen projekt imel trajno shranjevanje, ki pokvari podatke, in predpomnilnik, ki upočasnjuje. V takih razmerah je težko razviti rastoči projekt. Odločeno je bilo, da poskusimo kritične stvari, na katere je bil projekt osredotočen, ponovno napisati na lastnih kolesih.

Rešitev je bila uspešna. Za to je bila priložnost, pa tudi skrajna potreba, ker drugih načinov skaliranja takrat ni bilo. Ni bilo kopice baz podatkov, NoSQL še ni obstajal, bili so samo MySQL, Memcache, PostrgreSQL - in to je to.

Univerzalno delovanje. Razvoj je vodila naša ekipa razvijalcev C in vse je bilo narejeno na dosleden način. Ne glede na motor so imeli vsi približno enako obliko zapisa datoteke na disk, enake parametre zagona, obdelavo signalov na enak način in približno enako obnašanje v primeru robnih situacij in težav. Z rastjo motorjev je administratorjem priročno upravljati sistem - ni živalskega vrta, ki bi ga bilo treba vzdrževati, in se morajo znova naučiti, kako upravljati vsako novo bazo podatkov tretjih oseb, kar je omogočilo hitro in priročno povečati njihovo število.

Vrste motorjev

Ekipa je napisala kar nekaj motorjev. Tukaj je le nekaj izmed njih: prijatelj, namigi, slika, ipdb, pisma, seznami, dnevniki, memcached, meowdb, novice, nostradamus, fotografija, seznami predvajanja, pmemcached, peskovnik, iskanje, shranjevanje, všečki, opravila, …

Za vsako nalogo, ki zahteva specifično podatkovno strukturo ali obdeluje netipične zahteve, skupina C napiše nov motor. Zakaj ne.

Imamo ločen motor memcached, ki je podoben navadnemu, a s kupom dobrot in ki ne upočasnjuje. Ni ClickHouse, ampak tudi deluje. Na voljo ločeno pmemcached - Je obstojni memcached, ki lahko shranjuje podatke tudi na disk, poleg tega pa se prilega v RAM, da ne izgubi podatkov ob ponovnem zagonu. Obstajajo različni motorji za posamezna opravila: čakalne vrste, seznami, nizi - vse, kar zahteva naš projekt.

Grozdi

Z vidika kode motorjev ali baz podatkov ni treba obravnavati kot procese, entitete ali primerke. Koda deluje posebej z grozdi, s skupinami motorjev - eno vrsto na grozd. Recimo, da obstaja memcached gruča - to je samo skupina strojev.

Kodi sploh ni treba poznati fizične lokacije, velikosti ali števila strežnikov. V gručo gre z uporabo določenega identifikatorja.

Da bo to delovalo, morate dodati še eno entiteto, ki se nahaja med kodo in motorji - proxy.

RPC proxy

Zastopnik povezovalni avtobus, na katerem poteka skoraj celotno spletno mesto. Hkrati imamo brez odkritja storitve — namesto tega obstaja konfiguracija za ta proxy, ki pozna lokacijo vseh gruč in vseh drobcev te gruče. To delajo administratorji.

Programerjem je popolnoma vseeno, koliko, kje in kaj stane - preprosto gredo v gručo. To nam omogoča veliko. Proxy ob prejemu zahteve preusmeri zahtevo, pri čemer ve kam – to določi sam.

Pogosta vprašanja o arhitekturi in delu VKontakte

V tem primeru je proxy točka zaščite pred izpadom storitve. Če se kateri od motorjev upočasni ali se zruši, proxy to razume in se ustrezno odzove na stran odjemalca. To vam omogoča, da odstranite časovno omejitev - koda ne čaka, da se motor odzove, ampak razume, da ne deluje in se mora obnašati nekako drugače. Koda mora biti pripravljena na dejstvo, da baze podatkov ne delujejo vedno.

Specifične izvedbe

Včasih si še vedno resnično želimo imeti kakšno nestandardno rešitev kot motor. Hkrati smo se odločili, da ne bomo uporabili našega že pripravljenega rpc-proxyja, ustvarjenega posebej za naše motorje, ampak da bomo za nalogo izdelali ločen proxy.

Za MySQL, ki ga tu in tam še imamo, uporabljamo db-proxy, za ClickHouse pa - Kittenhouse.

Na splošno deluje takole. Obstaja določen strežnik, poganja kPHP, Go, Python - na splošno vsako kodo, ki lahko uporablja naš protokol RPC. Koda se izvaja lokalno na proxyju RPC - vsak strežnik, kjer se nahaja koda, izvaja svoj lokalni proxy. Na zahtevo proxy razume, kam naj gre.

Pogosta vprašanja o arhitekturi in delu VKontakte

Če en motor želi iti k drugemu, tudi če je sosednji, gre prek posrednika, ker je sosed morda v drugem podatkovnem centru. Motor se ne bi smel zanašati na poznavanje lokacije česar koli drugega kot samega sebe - to je naša standardna rešitev. So pa seveda izjeme :)

Primer TL-sheme, po kateri delujejo vsi motorji.

memcache.not_found                                = memcache.Value;
memcache.strvalue	value:string flags:int = memcache.Value;
memcache.addOrIncr key:string flags:int delay:int value:long = memcache.Value;

tasks.task
    fields_mask:#
    flags:int
    tag:%(Vector int)
    data:string
    id:fields_mask.0?long
    retries:fields_mask.1?int
    scheduled_time:fields_mask.2?int
    deadline:fields_mask.3?int
    = tasks.Task;
 
tasks.addTask type_name:string queue_id:%(Vector int) task:%tasks.Task = Long;

To je binarni protokol, katerega najbližji analog je protobuf. Shema vnaprej opisuje neobvezna polja, kompleksne tipe - razširitve vgrajenih skalarjev in poizvedbe. Vse deluje po tem protokolu.

RPC prek TL prek TCP/UDP… UDP?

Imamo protokol RPC za izvajanje zahtev motorja, ki deluje na vrhu sheme TL. Vse to deluje prek povezave TCP/UDP. TCP je razumljiv, ampak zakaj pogosto potrebujemo UDP?

UDP pomaga izognili problemu velikega števila povezav med strežniki. Če ima vsak strežnik proxy RPC in na splošno lahko gre do katerega koli mehanizma, potem obstaja več deset tisoč povezav TCP na strežnik. Tovor je, vendar je neuporaben. V primeru UDP ta problem ne obstaja.

Brez odvečnega rokovanja TCP. To je tipična težava: ko se zažene nov motor ali nov strežnik, se naenkrat vzpostavi veliko povezav TCP. Za majhne lahke zahteve, na primer obremenitev UDP, je vsa komunikacija med kodo in motorjem dva paketa UDP: ena leti v eno smer, druga v drugo. Ena povratna vožnja - in koda je prejela odgovor motorja brez rokovanja.

Da, vse deluje z zelo majhnim odstotkom izgube paketov. Protokol ima podporo za ponovne prenose in časovne omejitve, a če veliko izgubimo, bomo dobili skoraj TCP, kar ni v korist. UDP ne vozimo čez oceane.

Takšnih strežnikov imamo na tisoče, shema pa je enaka: na vsak fizični strežnik je nameščen paket motorjev. Večinoma so enonitne, da delujejo čim hitreje brez blokiranja, in so razdeljene kot enonitne rešitve. Hkrati pa nimamo nič bolj zanesljivega od teh motorjev, veliko pozornosti pa namenjamo obstojnemu shranjevanju podatkov.

Trajno shranjevanje podatkov

Motorji pišejo binloge. Binlog je datoteka, na koncu katere se doda dogodek za spremembo stanja ali podatkov. V različnih rešitvah se imenuje drugače: binarni dnevnik, WAL, AOF, vendar je princip enak.

Da motor ob ponovnem zagonu več let ne bi ponovno prebral celotnega binloga, motorji pišejo posnetki - trenutno stanje. Če je treba, najprej preberejo iz njega, nato pa dokončajo branje iz binloga. Vsi binlogi so zapisani v enaki binarni obliki - po shemi TL, tako da jih skrbniki lahko enakopravno upravljajo s svojimi orodji. Te potrebe po posnetkih ni. Obstaja splošna glava, ki označuje, čigav posnetek je int, magija motorja in katero telo ni pomembno za nikogar. To je težava z motorjem, ki je posnel posnetek.

Na hitro bom opisal princip delovanja. Obstaja strežnik, na katerem teče motor. Odpre nov prazen binlog za pisanje in vanj zapiše dogodek za spremembo.

Pogosta vprašanja o arhitekturi in delu VKontakte

V nekem trenutku se sam odloči, da bo posnel posnetek, ali pa prejme signal. Strežnik ustvari novo datoteko, vanjo zapiše njeno celotno stanje, doda trenutno velikost binloga - offset - na konec datoteke in nadaljuje s pisanjem naprej. Nov binlog se ne ustvari.

Pogosta vprašanja o arhitekturi in delu VKontakte

Na neki točki, ko se bo motor znova zagnal, bosta na disku binlog in posnetek. Motor prebere celoten posnetek in na določeni točki dvigne svoje stanje.

Pogosta vprašanja o arhitekturi in delu VKontakte

Prebere položaj, ki je bil v času, ko je bil posnetek ustvarjen, in velikost binloga.

Pogosta vprašanja o arhitekturi in delu VKontakte

Prebere konec binloga, da dobi trenutno stanje, in nadaljuje s pisanjem nadaljnjih dogodkov. To je preprosta shema, vsi naši motorji delujejo po njej.

Replikacija podatkov

Kot rezultat, podvajanje podatkov v našem na podlagi izjave — v binlog ne zapišemo kakršnih koli sprememb strani, ampak namreč zahteve za spremembo. Zelo podoben tistemu, kar prihaja po omrežju, le malo spremenjen.

Ista shema se uporablja ne samo za replikacijo, ampak tudi za ustvarjanje varnostnih kopij. Imamo motor - pisalni mojster, ki piše v binlog. Na katerem koli drugem mestu, kjer ga skrbniki nastavijo, se ta binlog kopira in to je to - imamo varnostno kopijo.

Pogosta vprašanja o arhitekturi in delu VKontakte

Če je potrebno branje replikeZa zmanjšanje obremenitve CPE pri branju se preprosto zažene motor za branje, ki prebere konec binloga in te ukaze izvede lokalno.

Zaostanek je tukaj zelo majhen in mogoče je ugotoviti, koliko replika zaostaja za mojstrom.

Razdelitev podatkov v proxyju RPC

Kako deluje sharding? Kako proxy razume, kateri del gruče poslati? Koda ne pravi: "Pošlji po 15 drobcev!" - ne, to naredi pooblaščenec.

Najenostavnejša shema je firstint — prva številka v zahtevku.

get(photo100_500) => 100 % N.

To je primer preprostega besedilnega protokola memcached, seveda pa so lahko poizvedbe zapletene in strukturirane. Primer vzame prvo število v poizvedbi in preostanek, ko ga delimo z velikostjo gruče.

To je uporabno, ko želimo imeti lokalnost podatkov ene same entitete. Recimo, da je 100 ID uporabnika ali skupine in želimo, da so vsi podatki ene entitete na enem segmentu za zapletene poizvedbe.

Če nam ni vseeno, kako so zahteve porazdeljene po gruči, obstaja še ena možnost - zgoščevanje celotnega drobca.

hash(photo100_500) => 3539886280 % N

Dobimo tudi hash, preostanek delitve in številko drobca.

Obe možnosti delujeta le, če smo pripravljeni na dejstvo, da ga bomo pri povečanju velikosti gruče razdelili ali večkrat povečali. Na primer, imeli smo 16 drobcev, nimamo dovolj, želimo več - varno jih lahko dobimo 32 brez izpadov. Če želimo povečati ne večkratnike, bo prišlo do izpadov, ker ne bomo mogli vsega natančno razdeliti brez izgub. Te možnosti so uporabne, vendar ne vedno.

Če moramo dodati ali odstraniti poljubno število strežnikov, uporabimo Dosledno zgoščevanje na prstanu a la Ketama. Toda hkrati popolnoma izgubimo lokalnost podatkov; zahtevo moramo združiti v gručo, tako da vsak kos vrne svoj majhen odgovor, in nato združiti odgovore posredniku.

Obstajajo zelo specifične zahteve. Videti je takole: RPC proxy prejme zahtevo, določi, v katero gručo naj gre, in določi shard. Potem so tu bodisi zapisovalni mojstri ali, če ima gruča podporo za replike, pošlje replici na zahtevo. Vse to naredi proxy.

Pogosta vprašanja o arhitekturi in delu VKontakte

Dnevniki

Dnevnike pišemo na več načinov. Najbolj očiten in preprost je pisanje dnevnikov v memcache.

ring-buffer: prefix.idx = line

Obstaja ključna predpona - ime dnevnika, vrstica, in velikost tega dnevnika - število vrstic. Vzamemo naključno število od 0 do števila vrstic minus 1. Ključ v predpomnilniku je predpona, povezana s to naključno številko. Vrstico dnevnika in trenutni čas shranimo v vrednost.

Kadar je potrebno branje dnevnikov, izvedemo Multi Get vse ključe, razvrščene po času in tako dobite produkcijski dnevnik v realnem času. Shema se uporablja, ko morate razhroščevati nekaj v produkciji v realnem času, ne da bi kar koli zlomili, ne da bi ustavili ali dovolili promet drugim strojem, vendar ta dnevnik ne traja dolgo.

Za zanesljivo skladiščenje hlodovine imamo motor log-motor. Ravno zato je bil ustvarjen in se široko uporablja v ogromnem številu grozdov. Največji grozd, ki ga poznam, hrani 600 TB zapakiranih dnevnikov.

Motor je zelo star, obstajajo grozdi, ki so stari že 6-7 let. Pri tem obstajajo težave, ki jih poskušamo rešiti, na primer začeli smo aktivno uporabljati ClickHouse za shranjevanje dnevnikov.

Zbiranje dnevnikov v ClickHouse

Ta diagram prikazuje, kako vstopamo v naše motorje.

Pogosta vprašanja o arhitekturi in delu VKontakte

Obstaja koda, ki gre lokalno prek RPC do RPC-proxyja in razume, kam naj gre do motorja. Če želimo pisati dnevnike v ClickHouse, moramo spremeniti dva dela v tej shemi:

  • zamenjajte nekaj motorjev s ClickHouse;
  • zamenjajte proxy RPC, ki ne more dostopati do ClickHouse, z neko rešitvijo, ki lahko, in prek RPC.

Motor je preprost - zamenjamo ga s strežnikom ali gručo strežnikov s ClickHouse.

In da bi šli v ClickHouse, smo KittenHouse. Če gremo neposredno iz KittenHouse v ClickHouse, ne bo kos. Tudi brez zahtev se sešteva iz HTTP povezav ogromnega števila strojev. Da shema deluje, na strežniku s ClickHouse lokalni povratni proxy je postavljen, ki je napisana tako, da prenese zahtevane količine povezav. Prav tako lahko razmeroma zanesljivo medpomni podatke v sebi.

Pogosta vprašanja o arhitekturi in delu VKontakte

Včasih ne želimo implementirati sheme RPC v nestandardne rešitve, na primer v nginx. Zato ima KittenHouse možnost prejemanja dnevnikov prek UDP.

Pogosta vprašanja o arhitekturi in delu VKontakte

Če pošiljatelj in prejemnik dnevnikov delujeta na istem računalniku, je verjetnost izgube paketa UDP znotraj lokalnega gostitelja precej majhna. Kot kompromis med potrebo po implementaciji RPC v rešitev tretje osebe in zanesljivostjo preprosto uporabljamo pošiljanje UDP. K tej shemi se bomo vrnili pozneje.

Spremljanje

Imamo dve vrsti dnevnikov: tiste, ki jih zbirajo skrbniki na svojih strežnikih, in tiste, ki jih napišejo razvijalci iz kode. Ustrezajo dvema vrstama meritev: sistem in izdelek.

Sistemske metrike

Deluje na vseh naših strežnikih netdata, ki zbira statistiko in jo pošilja v Grafitni ogljik. Zato se kot sistem za shranjevanje uporablja ClickHouse in ne na primer Whisper. Po potrebi lahko neposredno preberete iz ClickHouse ali uporabite grafana za meritve, grafe in poročila. Kot razvijalci imamo dovolj dostopa do Netdata in Grafana.

Meritve izdelka

Za udobje smo napisali veliko stvari. Na primer, obstaja niz običajnih funkcij, ki vam omogočajo, da v statistiko zapišete vrednosti Counts, UniqueCounts, ki se pošljejo nekam dlje.

statlogsCountEvent   ( ‘stat_name’,            $key1, $key2, …)
statlogsUniqueCount ( ‘stat_name’, $uid,    $key1, $key2, …)
statlogsValuetEvent  ( ‘stat_name’, $value, $key1, $key2, …)

$stats = statlogsStatData($params)

Kasneje lahko uporabimo filtre za razvrščanje in združevanje ter počnemo vse, kar želimo od statistike – gradimo grafe, konfiguriramo Watchdoge.

Pišemo zelo veliko meritev število dogodkov je od 600 milijard do 1 bilijona na dan. Vendar jih želimo obdržati vsaj nekaj letrazumeti trende v meritvah. Sestaviti vse skupaj je velik problem, ki ga še nismo rešili. Povedal vam bom, kako je delovalo zadnjih nekaj let.

Imamo funkcije, ki pišejo te metrike v lokalni predpomnilnikda zmanjšate število vnosov. Enkrat v kratkem času lokalno lansiran stats-daemon zbira vse zapise. Nato demon združi metrike v dve plasti strežnikov zbiralci hlodov, ki združuje statistične podatke iz množice naših strojev, tako da sloj za njimi ne umre.

Pogosta vprašanja o arhitekturi in delu VKontakte

Po potrebi lahko pišemo neposredno zbiralcem dnevnikov.

Pogosta vprašanja o arhitekturi in delu VKontakte

Toda pisanje iz kode neposredno v zbiralnike, mimo stas-daemom, je slabo razširljiva rešitev, ker poveča obremenitev zbiralnika. Rešitev je primerna samo, če iz nekega razloga ne moremo dvigniti memcache stats-daemon na stroju ali če se je zrušil in smo šli neposredno.

Nato zbiralci dnevnikov združijo statistiko v meowDB - to je naša baza podatkov, ki lahko hrani tudi metrike.

Pogosta vprašanja o arhitekturi in delu VKontakte

Nato lahko iz kode naredimo binarne izbire »skoraj SQL«.

Pogosta vprašanja o arhitekturi in delu VKontakte

Poskus

Poleti 2018 smo imeli interni hackathon in porodila se je ideja, da poskusimo zamenjati rdeči del diagrama z nečim, kar bi lahko hranilo metrike v ClickHouse. Na ClickHouse imamo dnevnike – zakaj ne bi poskusili?

Pogosta vprašanja o arhitekturi in delu VKontakte

Imeli smo shemo, ki je pisala dnevnike prek KittenHouse.

Pogosta vprašanja o arhitekturi in delu VKontakte

Odločili smo se diagramu dodajte še eno »*hišo«., ki bo prejel točno tiste meritve v formatu, kot jih naša koda zapiše prek UDP. Nato jih ta *House spremeni v vložke, kot hlode, kar KittenHouse razume. Te dnevnike lahko popolnoma dostavi podjetju ClickHouse, ki bi jih moralo prebrati.

Pogosta vprašanja o arhitekturi in delu VKontakte

Shema z bazo podatkov memcache, stats-daemon in logs-collectors je nadomeščena s to.

Pogosta vprašanja o arhitekturi in delu VKontakte

Shema z bazo podatkov memcache, stats-daemon in logs-collectors je nadomeščena s to.

  • Tukaj je odpošiljanje kode, ki je napisana lokalno v StatsHouse.
  • StatsHouse zapisuje metrike UDP, ki so že pretvorjene v vstavke SQL, v KittenHouse v serijah.
  • KittenHouse jih pošlje ClickHouse.
  • Če jih želimo prebrati, jih preberemo mimo StatsHouse – neposredno iz ClickHouse z običajnim SQL.

Ali je še poskus, vendar nam je všeč, kako se izkaže. Če odpravimo težave s shemo, potem bomo morda popolnoma prešli nanjo. Osebno upam, da je tako.

Shema ne varčuje z železom. Potrebujemo manj strežnikov, lokalni statistični demoni in zbiralci dnevnikov niso potrebni, vendar ClickHouse zahteva večji strežnik od tistih v trenutni shemi. Potrebujemo manj strežnikov, vendar morajo biti dražji in zmogljivejši.

Razporedi

Najprej si poglejmo uvedbo PHP. Razvijamo se v git: uporaba GitLab и TeamCity za uvajanje. Razvojne veje se združijo v master vejo, iz mastera za testiranje se združijo v staging, iz staginga pa v proizvodnjo.

Pred uvedbo se vzameta trenutna proizvodna veja in prejšnja, v njih pa se upoštevajo diff datoteke - spremembe: ustvarjene, izbrisane, spremenjene. Ta sprememba se zabeleži v binlog posebnega mehanizma copyfast, ki lahko hitro ponovi spremembe na naši celotni floti strežnikov. Tukaj se ne uporablja neposredno kopiranje, ampak ponavljanje tračev, ko en strežnik pošlje spremembe svojim najbližjim sosedom, ti svojim sosedom itd. To vam omogoča posodobitev kode v desetinah in enotah sekund v celotnem voznem parku. Ko sprememba doseže lokalno repliko, uporabi te popravke za svojo lokalni datotečni sistem. Povratek se izvaja tudi po isti shemi.

Veliko uporabljamo tudi kPHP, ki ima tudi svoj razvoj git glede na zgornji diagram. Od tega Binarni strežnik HTTP, potem ne moremo izdelati diff - izdajna binarna datoteka tehta na stotine MB. Zato obstaja še ena možnost - različica je zapisana binlog copyfast. Z vsako gradnjo se povečuje, med povrnitvijo pa se tudi povečuje. Različica replicirano na strežnike. Lokalni prepisovalci vidijo, da je nova različica vstopila v binlog, in z isto replikacijo tračev zase vzamejo najnovejšo različico binarne datoteke, ne da bi utrudili naš glavni strežnik, ampak skrbno porazdelijo obremenitev po omrežju. Kar sledi eleganten ponovni zagon za novo različico.

Za naše motorje, ki so prav tako v bistvu binarni, je shema zelo podobna:

  • git glavna veja;
  • dvojiško v deb;
  • različica je zapisana v binlog copyfast;
  • replicirano na strežnike;
  • strežnik izvleče svež .dep;
  • dpkg -i;
  • eleganten ponovni zagon na novo različico.

Razlika je v tem, da je naša binarna datoteka zapakirana v arhive deb, pri črpanju pa jih dpkg -i so postavljeni v sistem. Zakaj je kPHP nameščen kot binarni, motorji pa kot dpkg? Zgodilo se je tako. Deluje – ne dotikajte se ga.

Koristne povezave:

Alexey Akulovich je eden tistih, ki kot del programskega odbora pomaga PHP Rusija 17. maja bo postal največji dogodek za razvijalce PHP v zadnjem času. Poglejte, kakšen kul računalnik imamo, kaj zvočniki (dva od njih razvijata jedro PHP!) - zdi se nekaj, česar ne morete zgrešiti, če pišete PHP.

Vir: www.habr.com

Dodaj komentar