FAQ nantu à l'architettura è u travagliu di VKontakte

A storia di a creazione di VKontakte hè in Wikipedia; hè statu cuntatu da Pavel stessu. Pare chì tutti a cunnoscenu digià. Circa l'internu, l'architettura è a struttura di u situ nantu à HighLoad++ Pavel m'hà dettu in u 2010. Parechje servitori sò filtrati da tandu, cusì aghjurnà l'infurmazioni: l'avemu disseccatu, caccià l'internu, pisà, è fighjemu u dispusitivu VK da un puntu di vista tecnicu.

FAQ nantu à l'architettura è u travagliu di VKontakte

Alexey Akulovich (AterCattus) sviluppatore di backend in a squadra VKontakte. A trascrizione di stu rapportu hè una risposta cullettiva à e dumande frequenti nantu à u funziunamentu di a piattaforma, l'infrastruttura, i servitori è l'interazzione trà elli, ma micca nantu à u sviluppu, vale à dì. circa u ferru. Separatamente, nantu à e basa di dati è ciò chì VK hà invece, nantu à a cullizzioni di logs è u seguimentu di tuttu u prughjettu in tuttu. Dettagli sottu u cut.



Dapoi più di quattru anni aghju trattatu cù ogni tipu di attività ligati à u backend.

  • Caricà, almacenà, trasfurmà, distribuzione media: video, live streaming, audio, photos, documents.
  • Infrastruttura, piattaforma, monitoraghju di sviluppatori, logs, cache regiunale, CDN, protocolu RPC propiu.
  • Integrazione cù servizii esterni: notificazioni push, analisi di ligami esterni, feed RSS.
  • Aiutà i culleghi cù diverse dumande, e risposte à quale esigenu immersione in codice scunnisciutu.

Duranti stu tempu, aghju avutu una manu in parechji cumpunenti di u situ. Vogliu sparte sta sperienza.

Architettura generale

Tuttu, cum'è di solitu, principia cù un servitore o un gruppu di servitori chì accettanu richieste.

Servitore di fronte

U servitore frontale accetta richieste via HTTPS, RTMP è WSS.

HTTPS - Quessi sò dumande per e versioni web principale è mobile di u situ: vk.com è m.vk.com, è altri clienti ufficiali è micca ufficiali di a nostra API: clienti mobili, messageri. Avemu una ricezione RTMP-traffic per trasmissioni in diretta cù servitori frontali separati è WSS-connessioni per Streaming API.

Per HTTPS è WSS nantu à i servitori vale a pena nginx. Per e trasmissioni RTMP, recentemente avemu cambiatu à a nostra propria suluzione kive, ma hè fora di u scopu di u rapportu. Per a tolleranza di difetti, questi servitori publicità indirizzi IP cumuni è agisce in gruppi per chì se ci hè un prublema in unu di i servitori, e dumande di l'utilizatori ùn sò micca persu. Per HTTPS è WSS, sti stessi servitori criptanu u trafficu per piglià parte di a carica di CPU nantu à elli stessi.

Ùn parlemu micca più di WSS è RTMP, ma solu di e richieste HTTPS standard, chì sò generalmente assuciati cù un prughjettu web.

Backend

Daretu à u fronte ci sò generalmente servitori backend. Prucessanu e dumande chì u servitore frontale riceve da i clienti.

issu servitori kPHP, nantu à quale u daemon HTTP hè in esecuzione, perchè HTTPS hè digià decriptatu. kPHP hè un servitore chì funziona mudelli di prefork: principia un prucessu maestru, una mansa di prucessi di i zitelli, passanu sockets d'ascoltu à elli è processanu e so dumande. In questu casu, i prucessi ùn sò micca riavviati trà ogni dumanda da l'utilizatore, ma solu resettate u so statu à u statu uriginale di valore zero - dumanda dopu a dumanda, invece di riavvia.

Distribuzione di carichi

Tutti i nostri backends ùn sò micca una grande piscina di macchine chì ponu processà ogni dumanda. Noi elli divisu in gruppi separati: generale, mobile, api, video, staging... U prublema nantu à un gruppu separatu di machini ùn affetterà micca tutti l'altri. In casu di prublemi cù u video, l'utilizatori chì ascolta a musica ùn anu mancu sapè di i prublemi. Quale backend per mandà a dumanda hè decisu da nginx in fronte secondu a cunfigurazione.

Raccolta metrica è riequilibriu

Per capisce quantu vitture avemu bisognu à avè in ogni gruppu, noi ùn fiate micca in QPS. I backends sò sfarenti, anu richieste diverse, ogni dumanda hà una cumplessità sfarente di calculà QPS. Hè per quessa noi operamu cù u cuncettu di carica nantu à u servitore in tuttu - nantu à u CPU è perf.

Avemu millaie di tali servitori. Ogni servitore fisicu gestisce un gruppu kPHP per riciclà tutti i core (perchè kPHP hè un filu unicu).

Servitore di cuntenutu

CS o Content Server hè un almacenamentu. CS hè un servitore chì guarda i fugliali è ancu processa i fugliali caricati è ogni tipu di attività sincrone di fondo chì u frontend web principale assigna à ellu.

Avemu decine di millaie di servitori fisici chì guardanu i fugliali. L'utilizatori amanu caricate fugliali, è ci piace à almacenà è sparte. Certi di sti servitori sò chjusi da servitori pu / pp speciali.

pu/pp

Se avete apertu a tabulazione di a reta in VK, avete vistu pu / pp.

FAQ nantu à l'architettura è u travagliu di VKontakte

Cos'è pu/pp? Se chjudemu un servitore dopu à l'altru, allora ci sò duie opzioni per carica è scaricate un schedariu à u servitore chì era chjusu: direttamente attraversu http://cs100500.userapi.com/path o via servitore intermediariu - http://pu.vk.com/c100500/path.

Pu hè u nome storicu per a carica di foto, è pp hè un proxy di foto. Vale à dì, un servitore hè per uploading photos, è un altru hè per uploading. Avà micca solu i ritratti sò caricati, ma u nome hè statu cunservatu.

Questi servitori finisce e sessioni HTTPSper sguassà a carica di u processatore da u almacenamiento. Inoltre, postu chì i schedarii di l'utilizatori sò processati nantu à questi servitori, l'infurmazioni menu sensibili guardate in queste macchine, u megliu. Per esempiu, chjavi di criptografia HTTPS.

Siccomu i machini sò chjusi da e nostre altre machini, pudemu permette di ùn dà micca IP esterni "bianchi", è dà "grigiu". In questu modu, avemu salvatu nantu à a piscina IP è garantita per prutezzione di e macchine da l'accessu esternu - ùn ci hè micca solu IP per entra in questu.

Resilienza nantu à IPs spartuti. In quantu à a tolleranza di difetti, u schema funziona u stessu - parechji servitori fisichi anu un IP fisicu cumuni, è u hardware davanti à elli sceglie induve mandà a dumanda. Parlaraghju di altre opzioni dopu.

U puntu cuntruversu hè chì in stu casu u cliente mantene menu cunnessione. Se ci hè a stessa IP per parechje macchine - cù u stessu òspite: pu.vk.com o pp.vk.com, u navigatore di u cliente hà un limitu in u numeru di dumande simultanee à un host. Ma in u tempu di ubiquitous HTTP/2, crede chì questu ùn hè più cusì pertinente.

U svantaghju evidenti di u schema hè chì deve pompa tuttu u trafficu, chì và à l'almacenamiento, attraversu un altru servitore. Siccomu pompà u trafficu attraversu e macchine, ùn pudemu micca ancu pump trafficu pesante, per esempiu, video, utilizendu u listessu schema. Trasmettimu direttamente - una cunnessione diretta separata per almacenamenti separati specificamente per u video. Trasmettemu un cuntenutu più ligeru attraversu un proxy.

Pocu pocu fà avemu avutu una versione mejorata di proxy. Avà vi dicu cumu si sò diffirenti di quelli ordinali è perchè questu hè necessariu.

Sun

En septembre 2017, Oracle, qui avait auparavant acheté Sun, licenziatu un gran numaru di impiegati di Sun. Pudemu dì chì in questu mumentu a cumpagnia hà cessatu di esiste. Quandu sceglie un nome per u novu sistema, i nostri amministratori decisu di rende tributu à a memoria di sta cumpagnia è chjamanu u novu sistema Sun. Trà noi avemu solu chjamà i so "soli".

FAQ nantu à l'architettura è u travagliu di VKontakte

pp hà avutu uni pochi di prublemi. Un IP per gruppu - cache inefficace. Diversi servitori fisichi sparte un indirizzu IP cumuni, è ùn ci hè manera di cuntrullà à quale servitore andrà a dumanda. Dunque, se parechji utilizatori venenu per u stessu schedariu, allora s'ellu ci hè un cache in questi servitori, u schedariu finisci in u cache di ogni servitore. Questu hè un schema assai inefficace, ma nunda puderia esse fattu.

In cunseguenza - ùn pudemu micca sparghje u cuntenutu, perchè ùn pudemu micca selezziunate un servitore specificu per questu gruppu - anu una IP cumuna. Ancu per certi mutivi internu avemu ùn era micca pussibule di stallà tali servitori in regioni. Sò stati solu in San Petruburgu.

Cù i soli, avemu cambiatu u sistema di selezzione. Avà avemu routing anycast: routing dinamicu, anycast, daemon di auto-verifica. Ogni servitore hà u so propiu IP individuale, ma una subnet cumuna. Tuttu hè cunfiguratu in modu chì, se un servitore falla, u trafficu hè spargugliatu in l'altri servitori di u stessu gruppu automaticamente. Avà hè pussibule selezziunà un servitore specificu, senza caching redundante, è a affidabilità ùn hè micca stata affettata.

Supportu di pesu. Avà ci ponu permette di stallà machini di differente putenza cum'è bisognu, è dinù, in casu di prublemi tempuranee, cambià i pesi di i "soli" di travagliu per riduce a carica nantu à elli, in modu chì "riposanu" è cumincianu à travaglià novu.

Sharding da u cuntenutu ID. Una cosa curiosa nantu à u sharding: avemu di solitu shard u cuntenutu in modu chì diversi utilizatori vanu à u stessu schedariu attraversu u stessu "sole" per avè una cache cumuna.

Recentemente avemu lanciatu l'applicazione "Clover". Questu hè un quiz in linea in una trasmissione in diretta, induve l'ospitu face dumande è l'utilizatori rispondenu in tempu reale, scegliendu opzioni. L'app hà un chat induve l'utilizatori ponu chatte. Pò cunnette simultaneamente à a trasmissione più di 100 mila persone. Tutti scrivenu missaghji chì sò mandati à tutti i participanti, è un avatar vene cun u missaghju. Se 100 mila persone venenu per un avatar in un "sole", allora pò volte volte daretu à una nuvola.

Per resiste à e raffiche di richieste per u stessu schedariu, hè per un certu tipu di cuntenutu chì vulemu un schema stupidu chì sparghje i schedari in tutti i "soli" dispunibili in a regione.

Sole da l'internu

Proxy inversu in nginx, cache in RAM o in dischi Optane / NVMe veloci. Esempiu: http://sun4-2.userapi.com/c100500/path - un ligame à u "sole", chì si trova in a quarta regione, u sicondu gruppu di servitori. Chiude u schedariu di strada, chì si trova fisicamente in u servitore 100500.

cupertine, iamme

Aghjunghjemu un node più à u nostru schema architettonicu - l'ambiente di caching.

FAQ nantu à l'architettura è u travagliu di VKontakte

Quì sottu hè u schema di layout caches regionali, ci sò circa 20 di elli. Quessi sò i lochi induve si trovanu cache è "soli", chì ponu cache u trafficu per elli stessi.

FAQ nantu à l'architettura è u travagliu di VKontakte

Questu hè u caching di cuntenutu multimediale; nisuna dati d'utilizatori sò almacenati quì - solu musica, video, foto.

Per determinà a regione di l'utilizatori, avemu cullemu prefissi di rete BGP annunziati in e regioni. In u casu di fallback, avemu ancu analizà a basa di dati geoip se ùn pudemu micca truvà l'IP per prefissi. Determinemu a regione da l'IP di l'utilizatore. In u codice, pudemu guardà una o più regioni di l'utilizatore - quelli punti à quale hè più vicinu geograficamente.

Cumu viaghja?

Contemu a popularità di i schedari per regione. Ci hè una quantità di cache regiunale induve si trova l'utilizatore, è un identificatore di file - pigliamu stu paru è aumentemu a valutazione cù ogni scaricamentu.

À u listessu tempu, i dimònii - servizii in regioni - da u tempu à u tempu venenu à l'API è dicenu: "Sò cusì è un tali cache, dammi una lista di i schedarii più populari in a mo regione chì ùn sò micca ancu nantu à mè. " L'API furnisce una mansa di fugliali ordinati per classificazione, u daemon li scarica, li porta à e regioni è furnisce i schedari da quì. Quissa hè a diffarenza fundamentale trà pu / pp è Sun da caches: si dà u schedariu à traversu iddi subitu, ancu s'è stu schedariu ùn hè micca in u cache, è u cache prima downloads u schedariu à sè stessu, è poi principia à dà torna.

In stu casu, avemu cuntenutu più vicinu à l'utilizatori è sparghje a carica di a rete. Per esempiu, solu da a cache di Mosca distribuemu più di 1 Tbit/s durante l'ora di punta.

Ma ci sò prublemi - I servitori di cache ùn sò micca gomma. Per u cuntenutu super populari, qualchì volta ùn ci hè micca abbastanza rete per un servitore separatu. I nostri servitori di cache sò 40-50 Gbit / s, ma ci hè un cuntenutu chì ostruisce completamente un tali canali. Avanzamu versu l'implementazione di u almacenamentu di più di una copia di i fugliali populari in a regione. Spergu chì l'avemu implementatu à a fine di l'annu.

Avemu vistu l'architettura generale.

  • Servitori frontali chì accettanu richieste.
  • Backends chì processanu e dumande.
  • Storages chì sò chjusi da dui tipi di proxy.
  • Caches regionali.

Chì ci manca in stu diagramma ? Di sicuru, e basa di dati in quale avemu almacenatu dati.

Basi di dati o mutori

Chjamemu micca basa di dati, ma mutori - Motori, perchè ùn avemu praticamenti micca basa di dati in u sensu generalmente accettatu.

FAQ nantu à l'architettura è u travagliu di VKontakte

Questa hè una misura necessaria. Questu hè accadutu perchè in u 2008-2009, quandu VK hà avutu una crescita splusiva in pupularità, u prughjettu hà travagliatu interamente in MySQL è Memcache è ci sò stati prublemi. MySQL hà amatu à crash and corrupt files, after which it would not recover, and Memcache gradually degradated in performance and had to be restarted.

Ci hè chì u prughjettu di più populari hà avutu un almacenamentu persistente, chì corrompe i dati, è una cache, chì rallenta. In tali cundizioni, hè difficiule di sviluppà un prughjettu in crescita. Hè statu decisu di pruvà à riscrive e cose critiche chì u prugettu era focu annantu à e nostre biciclette.

A suluzione hà successu. Ci era l'uppurtunità di fà questu, è ancu una necessità estrema, perchè altri modi di scaling ùn esistenu micca in quellu tempu. Ùn ci era micca una mansa di basa di dati, NoSQL ùn esisteva ancu, ci era solu MySQL, Memcache, PostrgreSQL - è questu hè.

U funziunamentu universale. U sviluppu hè statu guidatu da a nostra squadra di sviluppatori C è tuttu hè statu fattu in una manera coherente. Indipendentemente da u mutore, tutti avianu apprussimatamente u stessu formatu di schedariu scrittu à u discu, i stessi paràmetri di lanciamentu, i signali processati in u listessu modu, è si cumportavanu apprussimatamente u listessu in casu di situazione di punta è prublemi. Cù a crescita di i mutori, hè cunvenutu per l'amministratori per uperà u sistema - ùn ci hè micca un zoo chì deve esse mantinutu, è anu da ricuperà cumu per uperà ogni nova basa di dati di terzu, chì hà permessu di fà rapidamente è aumentà convenientemente u so numeru.

Tipi di mutori

A squadra hà scrittu uni pochi di mutori. Eccu alcuni di elli: amicu, suggerimenti, imagine, ipdb, lettere, liste, logs, memcached, meowdb, news, nostradamus, photo, playlists, pmemcached, sandbox, search, storage, likes, tasks, ...

Per ogni compitu chì richiede una struttura di dati specifica o processa richieste atipiche, a squadra C scrive un novu mutore. Perchè nò.

Avemu un mutore separatu memcached, chì hè simile à una regula regulare, ma cù una mansa di boni, è chì ùn rallenta micca. Ùn ClickHouse, ma funziona ancu. Disponibile separatamente pmemcached Is persistente memcached, chì pò ancu almacenà e dati nantu à u discu, in più, chì si mette in RAM, per ùn perde micca dati quandu si riavvia. Ci sò parechji mutori per i travaglii individuali: fila, listi, setti - tuttu ciò chì u nostru prughjettu esige.

Clusters

Da una perspettiva di codice, ùn ci hè bisognu di pensà à i mutori o basa di dati cum'è prucessi, entità o istanze. U codice travaglia specificamente cù clusters, cù gruppi di motori - un tipu per cluster. Diciamu chì ci hè un cluster memcached - hè solu un gruppu di macchine.

U codice ùn hà micca bisognu di cunnosce u locu fisicu, a dimensione, o u numeru di servitori. Va à u cluster utilizendu un certu identificatore.

Per fà questu, avete bisognu di aghjunghje una altra entità chì si trova trà u codice è i mutori - proxy.

Proxy RPC

Proxy bus di cunnessione, nantu à quale quasi tuttu u situ corre. À u listessu tempu avemu senza scuperta di serviziu - invece, ci hè una cunfigurazione per questu proxy, chì cunnosce u locu di tutti i clusters è tutti i shards di stu cluster. Questu hè ciò chì l'amministratori facenu.

I programatori ùn importa micca à tuttu quantu, induve è ciò chì costa - solu andà in u cluster. Questu ci permette assai. Quandu riceve una dumanda, u proxy redirige a dumanda, sapendu induve - determina questu stessu.

FAQ nantu à l'architettura è u travagliu di VKontakte

In questu casu, u proxy hè un puntu di prutezzione contra u fallimentu di u serviziu. Se qualchì mutore rallenta o crash, allura u proxy capisce questu è risponde in cunseguenza à u cliente. Questu permette di caccià u timeout - u codice ùn aspetta micca chì u mutore risponda, ma capisce chì ùn hè micca travagliatu è deve esse cumportanu in modu diversu. U codice deve esse preparatu per u fattu chì e basa di dati ùn sò micca sempre travagliatu.

Implementazioni specifiche

Calchì volta avemu sempre veramente vulemu avè qualchì tipu di suluzione non standard cum'è un mutore. À u listessu tempu, hè statu decisu di ùn aduprà u nostru rpc-proxy ready-made, creatu apposta per i nostri motori, ma per fà un proxy separatu per u compitu.

Per MySQL, chì avemu sempre quì è quì, usemu db-proxy, è per ClickHouse - Gattini.

Funziona in generale cusì. Ci hè un certu servitore, corre kPHP, Go, Python - in generale, qualsiasi codice chì pò aduprà u nostru protocolu RPC. U codice corre localmente nantu à un proxy RPC - ogni servitore induve u codice hè situatu corre u so propiu proxy locale. À a dumanda, u proxy capisce induve andà.

FAQ nantu à l'architettura è u travagliu di VKontakte

Se un mutore vole andà à un altru, ancu s'ellu hè un vicinu, passa per un proxy, perchè u vicinu pò esse in un altru centru di dati. U mutore ùn deve micca cunfidendu a cunniscenza di u locu di qualcosa altru ch'è ellu stessu - questa hè a nostra suluzione standard. Ma di sicuru ci sò eccezzioni :)

Un esempiu di un schema TL secondu chì tutti i motori operanu.

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;

Questu hè un protocolu binariu, l'analogicu più vicinu di quale hè protobuf. U schema predescribe campi opzionali, tippi cumplessi - estensioni di scalari integrati, è dumande. Tuttu travaglia secondu stu protokollu.

RPC sopra TL sopra TCP/UDP... UDP?

Avemu un protocolu RPC per eseguisce e dumande di u mutore chì corre nantu à u schema TL. Tuttu questu funziona nantu à una cunnessione TCP / UDP. TCP hè comprensibile, ma perchè avemu bisognu di UDP spessu?

UDP aiuta evite u prublema di un gran numaru di cunnessione trà i servitori. Se ogni servitore hà un proxy RPC è, in generale, pò andà à qualsiasi mutore, allora ci sò decine di millaie di cunnessione TCP per servitore. Ci hè una carica, ma hè inutile. In u casu di UDP stu prublema ùn esiste micca.

Nisuna stretta di mano TCP redundante. Questu hè un prublema tipica: quandu un novu mutore o un novu servitore hè lanciatu, parechje cunnessione TCP sò stabilite in una volta. Per i picculi dumande ligeri, per esempiu, UDP payload, tutta a cumunicazione trà u codice è u mutore hè dui pacchetti UDP: unu vola in una direzzione, u sicondu in l'altru. Un viaghju di andata - è u codice hà ricevutu una risposta da u mutore senza una stretta di manu.

Iè, tuttu funziona solu cù una piccula percentuale di perdita di pacchetti. U protokollu hà supportu per retransmits è timeouts, ma se perdemu assai, averemu quasi TCP, chì ùn hè micca benefica. Ùn guidamu micca UDP à traversu l'oceani.

Avemu migghiara di tali servitori, è u schema hè u listessu: un pacchettu di mutori hè stallatu nantu à ogni servitore fisicu. Sò per suprattuttu single-threaded per curriri u più prestu pussibule senza bluccà, è sò sharded cum'è suluzioni single-threaded. À u listessu tempu, ùn avemu nunda di più affidabile chè sti mutori, è assai attenti hè pagatu à u almacenamiento persistente di dati.

Storage persistente di dati

I motori scrivenu binlogs. Un binlog hè un schedariu à a fine di quale un avvenimentu per un cambiamentu di statu o dati hè aghjuntu. In diverse suluzione hè chjamatu in modu diversu: log binariu, SPA, AOF, ma u principiu hè u listessu.

Per impediscenu à u mutore di leghje u binlog sanu per parechji anni quandu si riavvia, i motori scrivenu snapshots - statu attuale. Se ne necessariu, leghjenu prima, è poi finiscinu di leghje da u binlog. Tutti i binlogs sò scritti in u stessu formatu binariu - secondu u schema TL, cusì chì l'amministratori ponu amministrà ugualmente cù i so arnesi. Ùn ci hè micca bisognu di snapshots. Ci hè un capu generale chì indica quale snapshot hè int, magia di u mutore, è quale corpu ùn hè micca impurtante per nimu. Questu hè un prublema cù u mutore chì hà registratu a snapshot.

Descriveraghju rapidamente u principiu di funziunamentu. Ci hè un servitore nantu à quale u mutore corre. Apertura un novu binlog viotu per scrive è scrive un avvenimentu per cambià.

FAQ nantu à l'architettura è u travagliu di VKontakte

À un certu puntu, o decide di piglià un snapshot ellu stessu, o riceve un signalu. U servitore crea un novu schedariu, scrive u so statu sanu in questu, aghjunghje a dimensione di u binlog attuale - offset - à a fine di u schedariu, è cuntinueghja à scrive più. Un novu binlog ùn hè micca creatu.

FAQ nantu à l'architettura è u travagliu di VKontakte

À un certu puntu, quandu u mutore riavvia, ci sarà sia un binlog è una snapshot nantu à u discu. U mutore leghje tutta a snapshot è alza u so statu à un certu puntu.

FAQ nantu à l'architettura è u travagliu di VKontakte

Leghjite a pusizione chì era à u mumentu chì a snapshot hè stata creata è a dimensione di u binlog.

FAQ nantu à l'architettura è u travagliu di VKontakte

Leghjite a fine di u binlog per uttene u statu attuale è cuntinueghja à scrive più avvenimenti. Questu hè un schema simplice; tutti i nostri mutori funzionanu secondu.

Replicazione di dati

In u risultatu, a replicazione di dati in u nostru basatu in dichjarazione - scrivimu in u binlog micca cambiamenti di pagina, ma à dì richieste di cambià. Assai simile à ciò chì vene nantu à a reta, solu ligeramente mudificatu.

U listessu schema hè usatu micca solu per a replicazione, ma ancu per creà backups. Avemu un mutore - un maestru di scrittura chì scrive à u binlog. In ogni altru locu induve l'amministratori u stallanu, stu binlog hè copiatu, è questu hè - avemu una copia di salvezza.

FAQ nantu à l'architettura è u travagliu di VKontakte

Se necessariu lettura di replicaPer riduce a carica di lettura di CPU, u mutore di lettura hè simplicemente lanciatu, chì leghje a fine di u binlog è eseguisce questi cumandamenti in u locu.

U lag quì hè assai chjuca, è hè pussibule di sapè quantu a replica lags daretu à u maestru.

Sharding di dati in proxy RPC

Cumu funziona u sharding? Cumu capisce u proxy à quale frammentu di cluster da mandà? U codice ùn dice micca: "Manda per 15 shards!" - no, questu hè fattu da u proxy.

U schema più simplice hè prima - u primu numeru in a dumanda.

get(photo100_500) => 100 % N.

Questu hè un esempiu per un protokollu di testu memcached simplice, ma, sicuru, e dumande ponu esse cumplesse è strutturate. L'esempiu piglia u primu numeru in a dumanda è u restu quandu hè divisu da a dimensione di u cluster.

Questu hè utile quandu vulemu avè a località di dati di una sola entità. Diciamu chì 100 hè un ID d'utilizatore o di gruppu, è vulemu chì tutti i dati di una entità sia in un shard per e dumande cumplesse.

Se ùn ci importa micca cumu e dumande sò sparse in u cluster, ci hè una altra opzione - sferisce l'intera frammentazione.

hash(photo100_500) => 3539886280 % N

Avemu dinò l'hash, u restu di a divisione è u numeru di shard.

Tramindui sti opzioni funzionanu solu s'ellu simu preparati per u fattu chì quandu avemu aumentatu a dimensione di u cluster, l'avemu spartutu o aumentate da parechje volte. Per esempiu, avemu avutu 16 shards, ùn avemu micca abbastanza, vulemu di più - pudemu ottene in modu sicuru 32 ​​senza downtime. Sè no vulemu aumentà micca multiplici, ci sarà downtime, perchè noi ùn sarà capaci di sparte accuratamenti tuttu senza pèrdite. Queste opzioni sò utili, ma micca sempre.

Se avemu bisognu di aghjunghje o caccià un numeru arbitrariu di servitori, usemu Hashing consistente nantu à l'anellu à a Ketama. Ma à u stessu tempu, perdemu cumplettamente a località di e dati; avemu da unisce a dumanda à u cluster in modu chì ogni pezzu torna a so propria risposta petite, è poi unisce e risposte à u proxy.

Ci sò richieste super-specifiche. Sembra cusì: u proxy RPC riceve a dumanda, determina à quale cluster andà è determina u shard. Allora ci sò o maestri di scrittura, o, se u cluster hà supportu di replica, manda à una replica nantu à dumanda. U proxy face tuttu questu.

FAQ nantu à l'architettura è u travagliu di VKontakte

Logs

Scrivemu logs in parechje manere. U più ovvi è simplice hè scrivite logs in memcache.

ring-buffer: prefix.idx = line

Ci hè un prefissu chjave - u nome di u logu, una linea, è ci hè a dimensione di stu logu - u numeru di linii. Pigliemu un numeru aleatoriu da 0 à u numeru di linii minus 1. A chjave in memcache hè un prefissu concatenatu cù stu numeru aleatoriu. Salvemu a linea di log è l'ora attuale à u valore.

Quandu hè necessariu di leghje i logs, facemu Multi Get tutti i chjavi, ordinati da u tempu, è cusì uttene un log di produzzione in tempu reale. U schema hè utilizatu quandu avete bisognu di debug qualcosa in a produzzione in tempu reale, senza rompe nunda, senza firmà o permette u trafficu à l'altri machini, ma questu logu ùn dura micca longu.

Per un almacenamentu affidabile di logs avemu un mutore logs-motore. Hè precisamente per quessa hè statu creatu è hè largamente utilizatu in un gran numaru di clusters. U più grande cluster chì cunnoscu di magazzini 600 TB di logs imballati.

U mutore hè assai vechju, ci sò clusters chì sò digià 6-7 anni. Ci sò prublemi cù questu chì avemu da pruvà à risolve, per esempiu, avemu cuminciatu à utilizà attivamente ClickHouse per almacenà logs.

Raccolta di log in ClickHouse

Stu diagramma mostra cumu andemu in i nostri mutori.

FAQ nantu à l'architettura è u travagliu di VKontakte

Ci hè un codice chì và in u locu via RPC à u RPC-proxy, è capisce induve andà à u mutore. Se vulemu scrive logs in ClickHouse, avemu bisognu di cambià duie parte in questu schema:

  • rimpiazzà qualchi mutore cù ClickHouse;
  • rimpiazzà u proxy RPC, chì ùn pò micca accede à ClickHouse, cù qualchì suluzione chì pò, è via RPC.

U mutore hè simplice - l'avemu rimpiazzatu cù un servitore o un cluster di servitori cù ClickHouse.

È per andà in ClickHouse, avemu fattu Casa di gattini. Se andemu direttamente da KittenHouse à ClickHouse, ùn averà micca affruntà. Ancu senza richieste, aghjunghje da e cunnessione HTTP di un gran numaru di macchine. Per u schema di travaglià, in un servitore cù ClickHouse proxy inversu lucale hè risuscitatu, chì hè scrittu in tale manera chì pò sustene i volumi necessarii di cunnessione. Puderà ancu buffer dati in sè stessu in modu relativamente affidabile.

FAQ nantu à l'architettura è u travagliu di VKontakte

A volte ùn vulemu micca implementà u schema RPC in suluzioni non standard, per esempiu, in nginx. Dunque, KittenHouse hà a capacità di riceve log via UDP.

FAQ nantu à l'architettura è u travagliu di VKontakte

Se u mittente è u destinatariu di i logs travaglianu nantu à a stessa macchina, allora a probabilità di perde un pacchettu UDP in l'ospitu lucale hè abbastanza bassu. Cum'è un cumprumissu trà a necessità di implementà RPC in una suluzione di terzu è affidabilità, simpricimenti usemu l'invio UDP. Riturneremu à stu schema più tardi.

Monitoramentu

Avemu dui tipi di logs: quelli chì sò cullati da l'amministratori nantu à i so servitori è quelli scritti da i sviluppatori da u codice. Correspondenu à dui tipi di metrica: sistema è pruduttu.

Metri di sistema

Funziona in tutti i nostri servitori netdata, chì raccoglie statistiche è li manda à Carbone di grafite. Per quessa, ClickHouse hè utilizatu cum'è sistema di almacenamiento, è micca Whisper, per esempiu. Sè necessariu, pudete leghje direttamente da ClickHouse, o aduprà Grafana per metriche, grafici è rapporti. Cum'è sviluppatori, avemu abbastanza accessu à Netdata è Grafana.

Metri di u produttu

Per comodità, avemu scrittu assai cose. Per esempiu, ci hè un inseme di funzioni ordinarie chì permettenu di scrive Counts, UniqueCounts valori in statistiche, chì sò mandati in un locu più luntanu.

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

$stats = statlogsStatData($params)

In seguitu, pudemu usà filtri di classificazione è raggruppamentu è fà tuttu ciò chì vulemu da statistiche - custruite grafici, cunfigurà Watchdogs.

Scrivemu assai parechje metriche u numeru di avvenimenti hè da 600 billion à 1 trilion per ghjornu. Tuttavia, vulemu mantene almenu un paru d'anniper capisce i tendenzi in metrica. Mettite tuttu inseme hè un grande prublema chì ùn avemu micca risoltu ancu. Vi dicu cumu hà travagliatu per l'ultimi anni.

Avemu funzioni chì scrivenu sti metrichi à u memcache localeper riduce u numeru di entrate. Una volta in un cortu periodu di tempu lanciatu lucale stats-daemon raccoglie tutti i registri. Dopu, u dimòniu unisce e metriche in dui strati di servitori cullizzioni di tronchi, chì aggregate statistiche da una mansa di i nostri machini per chì a capa daretu à elli ùn mori micca.

FAQ nantu à l'architettura è u travagliu di VKontakte

In casu di necessariu, pudemu scrive direttamente à i logs-collectors.

FAQ nantu à l'architettura è u travagliu di VKontakte

Ma scrivite da u codice direttamente à i cullezzione, bypassing stas-daemom, hè una suluzione pocu scalable perchè aumenta a carica nantu à u cullettore. A suluzione hè adattata solu s'ellu per una certa ragione ùn pudemu micca elevà u memcache stats-daemon in a macchina, o s'hè lampatu è andemu direttamente.

In seguitu, i logs-collectors uniscenu statistiche in meowDB - questu hè a nostra basa di dati, chì pò ancu almacenà metriche.

FAQ nantu à l'architettura è u travagliu di VKontakte

Allora pudemu fà selezzione binaria "quasi-SQL" da u codice.

FAQ nantu à l'architettura è u travagliu di VKontakte

Prufessiunale

In l'estiu di 2018, avemu avutu un hackathon internu, è l'idea hè ghjunta per pruvà à rimpiazzà a parte rossa di u diagramma cù qualcosa chì puderia almacenà metriche in ClickHouse. Avemu logs nantu à ClickHouse - perchè ùn pruvà micca?

FAQ nantu à l'architettura è u travagliu di VKontakte

Avemu avutu un schema chì hà scrittu logs attraversu KittenHouse.

FAQ nantu à l'architettura è u travagliu di VKontakte

Avemu decisu aghjunghje un altru "* Casa" à u diagrama, chì riceverà esattamente e metriche in u formatu cum'è u nostru codice li scrive via UDP. Allora questu * House li trasforma in inserti, cum'è logs, chì KittenHouse capisce. Puderà perfettamente furnisce questi logs à ClickHouse, chì deve esse capaci di leghje.

FAQ nantu à l'architettura è u travagliu di VKontakte

U schema cù memcache, stats-daemon è logs-collectors database hè rimpiazzatu cù questu.

FAQ nantu à l'architettura è u travagliu di VKontakte

U schema cù memcache, stats-daemon è logs-collectors database hè rimpiazzatu cù questu.

  • Ci hè un dispatch da u codice quì, chì hè scrittu in u locu in StatsHouse.
  • StatsHouse scrive metriche UDP, digià cunvertite in inserti SQL, à KittenHouse in batch.
  • KittenHouse li manda à ClickHouse.
  • Se li vulemu leghje, allora li leghjimu bypassendu StatsHouse - direttamente da ClickHouse cù SQL regulare.

Hè sempre un esperimentu, ma ci piace cumu si trova. Se risolvemu i prublemi cù u schema, allora forse avemu da cambià in tuttu. In modu persunale, spergu cusì.

U schema ùn salva micca u ferru. Meno servitori sò necessarii, i stats-daemons lucali è i logs-collectors ùn sò micca necessariu, ma ClickHouse precisa un servitore più grande di quelli in u schema attuale. Meno servitori sò necessarii, ma devenu esse più caru è più putente.

Impulsà

Prima, fighjemu a implementazione di PHP. Avemu sviluppatu in andà: usu GitLab и TeamCity per l'implementazione. I rami di sviluppu sò uniti in u ramu maestru, da u maestru per a prova sò fusionati in staging, è da staging in pruduzzione.

Prima di implementà, u ramu di produzzione attuale è u precedente sò pigliati, è i schedari diff sò cunsiderati in elli - cambiamenti: creatu, sguassatu, cambiatu. Stu cambiamentu hè registratu in u binlog di un mutore copyfast speciale, chì pò riplicà rapidamente i cambiamenti in tutta a nostra flotta di servitori. Ciò chì hè utilizatu quì ùn hè micca copia direttamente, ma replicazione di pettegolezzi, Quandu un servitore manda cambiamenti à i so vicini più vicini, quelli à i so vicini, è cusì. Questu permette di aghjurnà u codice in decine è unità di seconde in tutta a flotta. Quandu u cambiamentu ghjunghje à a replica lucale, applicà questi patch à u so sistema di schedari locale. Rollback hè ancu realizatu secondu u listessu schema.

Avemu ancu implementatu kPHP assai è hà ancu u so propiu sviluppu andà secondu u schema sopra. Dapoi questu Servitore HTTP binariu, allura ùn pudemu micca pruduce diff - u binariu di liberazione pesa centinaie di MB. Dunque, ci hè una altra opzione quì - a versione hè scritta binlog copyfast. Cù ogni custruzzione aumenta, è durante u rollback aumenta ancu. Versione replicatu à i servitori. Copyfasts lucali vede chì una nova versione hè intrutu in u binlog, è da a stessa replicazione di gossip piglianu l'ultima versione di u binariu per elli, senza stancu u nostru servitore maestru, ma cun cura sparghje a carica in a reta. Ciò chì seguita rilanciu graziosu per a nova versione.

Per i nostri mutori, chì sò ancu essenzialmente binari, u schema hè assai simili:

  • git master branch;
  • binariu in .deb;
  • a versione hè scritta à binlog copyfast;
  • replicatu à i servitori;
  • u servitore tira fora un novu .dep;
  • dpkg -i;
  • rilanciamentu graziosu à a nova versione.

A diferenza hè chì u nostru binariu hè imballatu in archivi .deb, è quandu u pumping fora dpkg -i sò posti nantu à u sistema. Perchè kPHP hè implementatu cum'è binariu, è i mutori sò implementati cum'è dpkg? Hè accadutu cusì. Funziona - ùn tocca micca.

Ligami utili:

Alexey Akulovich hè unu di quelli chì, cum'è parte di u Cumitatu di u prugramma, aiuta PHP Russia u 17 di maghju diventerà u più grande avvenimentu per i sviluppatori PHP in i tempi recenti. Fighjate chì un PC cool avemu, chì parlanti (dui d'elli sò sviluppatu u core PHP!) - Pare chì qualcosa chì ùn pudete micca mancà sè scrive PHP.

Source: www.habr.com

Add a comment