Preguntes freqüents sobre arquitectura i treball de VKontakte

La història de la creació de VKontakte es troba a la Viquipèdia; la va explicar el mateix Pavel. Sembla que tothom ja la coneix. Sobre l'interior, l'arquitectura i l'estructura del lloc a HighLoad++ Pavel em va dir l'any 2010. Des d'aleshores s'han filtrat molts servidors, així que actualitzarem la informació: la disseccionarem, en traurem l'interior, la pesarem i mirarem el dispositiu VK des d'un punt de vista tècnic.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Alexei Akulovich (AterCattus) desenvolupador de backend a l'equip de VKontakte. La transcripció d'aquest informe és una resposta col·lectiva a les preguntes més freqüents sobre el funcionament de la plataforma, la infraestructura, els servidors i la interacció entre ells, però no sobre el desenvolupament, és a dir sobre el ferro. Per separat, sobre les bases de dades i el que té VK, sobre la recollida de registres i el seguiment de tot el projecte en conjunt. Detalls sota el tall.



Des de fa més de quatre anys m'he ocupat de tot tipus de tasques relacionades amb el backend.

  • Càrrega, emmagatzematge, processament, distribució de mitjans: vídeo, streaming en directe, àudio, fotos, documents.
  • Infraestructura, plataforma, monitorització de desenvolupadors, registres, memòria cau regional, CDN, protocol RPC propietari.
  • Integració amb serveis externs: notificacions push, anàlisi d'enllaços externs, canal RSS.
  • Ajudar els companys amb diverses preguntes, les respostes a les quals requereixen submergir-se en codi desconegut.

Durant aquest temps, vaig tenir una mà en molts components del lloc. Vull compartir aquesta experiència.

Arquitectura general

Tot, com és habitual, comença amb un servidor o grup de servidors que accepten peticions.

Servidor frontal

El servidor frontal accepta peticions mitjançant HTTPS, RTMP i WSS.

HTTPS - Es tracta de sol·licituds per a les versions web principal i mòbil del lloc: vk.com i m.vk.com, i altres clients oficials i no oficials de la nostra API: clients mòbils, missatgers. Tenim recepció RTMP-trànsit per a emissions en directe amb servidors frontals separats i WSS- connexions per a l'API de streaming.

Per HTTPS i WSS en servidors val la pena nginx. Per a les emissions RTMP, recentment hem canviat a la nostra pròpia solució kive, però està fora de l'abast de l'informe. Per a la tolerància a errors, aquests servidors anuncien adreces IP comunes i actuen en grups de manera que si hi ha algun problema en un dels servidors, no es perdin les sol·licituds dels usuaris. Per a HTTPS i WSS, aquests mateixos servidors xifren el trànsit per tal de prendre part de la càrrega de la CPU sobre ells mateixos.

No parlarem més de WSS i RTMP, sinó només de sol·licituds HTTPS estàndard, que solen estar associades a un projecte web.

Backend

Darrere de la part frontal normalment hi ha servidors backend. Processen les peticions que el servidor frontal rep dels clients.

El servidors kPHP, on s'executa el dimoni HTTP, perquè HTTPS ja està desxifrat. kPHP és un servidor que s'executa models de preforca: inicia un procés mestre, un munt de processos fills, els passa sockets d'escolta i processen les seves peticions. En aquest cas, els processos no es reinicien entre cada sol·licitud de l'usuari, sinó que simplement restableixen el seu estat a l'estat original de valor zero: sol·licitud rere petició, en lloc de reiniciar-se.

Distribució de càrrega

Tots els nostres backends no són un gran conjunt de màquines que puguin processar qualsevol sol·licitud. Nosaltres ells dividit en grups separats: general, mòbil, API, vídeo, posada en escena... El problema en un grup separat de màquines no afectarà a tots els altres. En cas de problemes amb el vídeo, l'usuari que escolta música ni tan sols sabrà dels problemes. A quin backend enviar la sol·licitud el decideix nginx a la part davantera segons la configuració.

Recollida de mètriques i reequilibri

Per entendre quants cotxes hem de tenir a cada grup, nosaltres no confieu en QPS. Els backends són diferents, tenen peticions diferents, cada sol·licitud té una complexitat diferent de càlcul de QPS. Per això nosaltres operem amb el concepte de càrrega al servidor en conjunt: a la CPU i al rendiment.

Tenim milers d'aquests servidors. Cada servidor físic executa un grup kPHP per reciclar tots els nuclis (perquè kPHP és d'un sol fil).

Servidor de continguts

CS o Content Server és un emmagatzematge. CS és un servidor que emmagatzema fitxers i també processa fitxers penjats i tot tipus de tasques sincròniques en segon pla que li assigna la interfície web principal.

Tenim desenes de milers de servidors físics que emmagatzemen fitxers. Als usuaris els encanta penjar fitxers i ens encanta emmagatzemar-los i compartir-los. Alguns d'aquests servidors estan tancats per servidors especials pu/pp.

pu/pp

Si vau obrir la pestanya de xarxa a VK, vau veure pu/pp.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Què és pu/pp? Si tanquem un servidor rere l'altre, hi ha dues opcions per carregar i descarregar un fitxer al servidor que estava tancat: directament a través d' http://cs100500.userapi.com/path o mitjançant un servidor intermedi - http://pu.vk.com/c100500/path.

Pu és el nom històric per a la càrrega de fotos i pp és el proxy de fotos. És a dir, un servidor serveix per pujar fotos i un altre per pujar. Ara no només es carreguen fotos, sinó que s'ha conservat el nom.

Aquests servidors finalitzar les sessions HTTPSper eliminar la càrrega del processador de l'emmagatzematge. A més, com que els fitxers d'usuari es processen en aquests servidors, com menys informació sensible s'emmagatzemi en aquestes màquines, millor. Per exemple, claus de xifratge HTTPS.

Com que les màquines estan tancades per les nostres altres màquines, ens podem permetre el luxe de no donar-los IP externes "blanques" i donar "gris". D'aquesta manera, hem estalviat al grup d'IP i hem garantit la protecció de les màquines de l'accés exterior; simplement no hi ha cap IP per entrar-hi.

Resiliència sobre IP compartides. Pel que fa a la tolerància a errors, l'esquema funciona igual: diversos servidors físics tenen una IP física comuna i el maquinari que tenen davant tria on enviar la sol·licitud. Més endavant parlaré d'altres opcions.

El punt controvertit és que en aquest cas el client manté menys connexions. Si hi ha la mateixa IP per a diverses màquines, amb el mateix host: pu.vk.com o pp.vk.com, el navegador client té un límit en el nombre de sol·licituds simultànies a un amfitrió. Però en l'època de l'omnipresent HTTP/2, crec que això ja no és tan rellevant.

El desavantatge evident de l'esquema és que ha de fer-ho bomba tot el trànsit, que va a l'emmagatzematge, a través d'un altre servidor. Com que bombem trànsit a través de màquines, encara no podem bombejar trànsit pesat, per exemple, vídeo, utilitzant el mateix esquema. El transmetem directament: una connexió directa independent per a emmagatzematges separats específicament per a vídeo. Transmetem contingut més lleuger mitjançant un proxy.

No fa molt, vam tenir una versió millorada del proxy. Ara us explicaré en què es diferencien dels normals i per què és necessari.

Sun

El setembre de 2017, Oracle, que abans havia comprat Sun, va acomiadar un gran nombre d'empleats de Sun. Podem dir que en aquest moment l'empresa va deixar d'existir. En triar un nom per al nou sistema, els nostres administradors van decidir retre homenatge a la memòria d'aquesta empresa i van batejar el nou sistema Sun. Entre nosaltres simplement l'anomenem "sols".

Preguntes freqüents sobre arquitectura i treball de VKontakte

pp va tenir alguns problemes. Una IP per grup: memòria cau ineficaç. Diversos servidors físics comparteixen una adreça IP comuna i no hi ha manera de controlar a quin servidor anirà la sol·licitud. Per tant, si diferents usuaris vénen pel mateix fitxer, aleshores si hi ha una memòria cau en aquests servidors, el fitxer acaba a la memòria cau de cada servidor. Aquest és un esquema molt ineficient, però no es podria fer res.

Conseqüentment - no podem dividir el contingut, perquè no podem seleccionar un servidor específic per a aquest grup: tenen una IP comuna. També per alguns motius interns que tenim no era possible instal·lar aquests servidors a les regions. Només es trobaven a Sant Petersburg.

Amb els sols, vam canviar el sistema de selecció. Ara tenim enrutament anycast: encaminament dinàmic, anycast, dimoni d'autocomprovació. Cada servidor té la seva pròpia IP individual, però una subxarxa comuna. Tot està configurat de manera que si un servidor falla, el trànsit es distribueix automàticament entre els altres servidors del mateix grup. Ara és possible seleccionar un servidor específic, sense memòria cau redundant, i la fiabilitat no es va veure afectada.

Suport de pes. Ara ens podem permetre instal·lar màquines de diferent potència segons sigui necessari, i també, en cas de problemes temporals, canviar els pesos dels "sols" de treball per reduir-ne la càrrega, de manera que "descansin" i comencin a treballar de nou.

Compartiment per identificador de contingut. Una cosa curiosa del sharding: normalment fem fragments de contingut perquè diferents usuaris van al mateix fitxer a través del mateix “sol” perquè tinguin una memòria cau comú.

Recentment hem llançat l'aplicació "Trèvol". Es tracta d'un qüestionari en línia en una retransmissió en directe, on l'amfitrió fa preguntes i els usuaris responen en temps real, escollint opcions. L'aplicació té un xat on els usuaris poden xatejar. Es pot connectar simultàniament a l'emissió més de 100 mil persones. Tots escriuen missatges que s'envien a tots els participants i acompanya el missatge un avatar. Si 100 mil persones vénen a buscar un avatar en un sol "sol", de vegades pot rodar darrere d'un núvol.

Per tal de suportar ràfegues de sol·licituds per al mateix fitxer, és per a un determinat tipus de contingut que activem un estúpid esquema que distribueix fitxers per tots els "sols" disponibles a la regió.

Sol des de dins

Proxy invers a nginx, memòria cau a la memòria RAM o als discs ràpids Optane/NVMe. Exemple: http://sun4-2.userapi.com/c100500/path — un enllaç al "sol", que es troba a la quarta regió, el segon grup de servidors. Tanca el fitxer de ruta, que es troba físicament al servidor 100500.

Cache

Afegim un node més al nostre esquema arquitectònic: l'entorn de memòria cau.

Preguntes freqüents sobre arquitectura i treball de VKontakte

A continuació es mostra el diagrama de disposició cachés regionals, n'hi ha uns 20. Aquests són els llocs on es troben els cachés i els "sols", que poden guardar el trànsit a la memòria cau per ells mateixos.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Es tracta d'emmagatzemar contingut multimèdia a la memòria cau; aquí no s'emmagatzemen dades d'usuari: només música, vídeo, fotos.

Per determinar la regió de l'usuari, nosaltres recollim els prefixos de xarxa BGP anunciats a les regions. En el cas del fallback, també hem d'analitzar la base de dades geoip si no hem pogut trobar la IP per prefixos. Determinem la regió per la IP de l'usuari. Al codi, podem mirar una o més regions de l'usuari, aquells punts als quals està més a prop geogràficament.

Com funciona?

Comptem la popularitat dels fitxers per regió. Hi ha un nombre de la memòria cau regional on es troba l'usuari i un identificador de fitxer: prenem aquest parell i augmentem la qualificació amb cada descàrrega.

Al mateix temps, els dimonis -serveis a les regions- de tant en tant vénen a l'API i diuen: "Sóc tal i tal caché, doneu-me una llista dels fitxers més populars de la meva regió que encara no són a mi. ” L'API ofereix un munt de fitxers ordenats per classificació, el dimoni els descarrega, els porta a les regions i lliura els fitxers des d'allà. Aquesta és la diferència fonamental entre pu/pp i Sun des de la memòria cau: donen el fitxer per ells mateixos immediatament, fins i tot si aquest fitxer no es troba a la memòria cau, i la memòria cau primer baixa el fitxer a si mateix i després comença a retornar-lo.

En aquest cas aconseguim contingut més proper als usuaris i repartint la càrrega de la xarxa. Per exemple, només des de la memòria cau de Moscou distribuïm més d'1 Tbit/s durant les hores punta.

Però hi ha problemes - els servidors de memòria cau no són de goma. Per al contingut súper popular, de vegades no hi ha prou xarxa per a un servidor separat. Els nostres servidors de memòria cau són de 40-50 Gbit/s, però hi ha contingut que obstrueix completament aquest canal. Estem avançant cap a la implementació d'emmagatzematge de més d'una còpia de fitxers populars a la regió. Espero que la implementem a finals d'any.

Hem observat l'arquitectura general.

  • Servidors frontals que accepten peticions.
  • Backends que processen les sol·licituds.
  • Emmagatzems que estan tancats per dos tipus de proxies.
  • Cachés regionals.

Què li falta a aquest diagrama? Per descomptat, les bases de dades en les quals emmagatzemem les dades.

Bases de dades o motors

Els anomenem no bases de dades, sinó motors - Motors, perquè pràcticament no disposem de bases de dades en el sentit generalment acceptat.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Aquesta és una mesura necessària. Això va passar perquè el 2008-2009, quan VK va tenir un creixement explosiu de popularitat, el projecte va funcionar completament en MySQL i Memcache i hi va haver problemes. A MySQL li encantava bloquejar i corrompre els fitxers, després del qual no es recuperaria, i Memcache va anar degradant gradualment el rendiment i es va haver de reiniciar.

Resulta que el projecte cada cop més popular tenia un emmagatzematge persistent, que corromp les dades, i una memòria cau, que s'alenteix. En aquestes condicions, és difícil desenvolupar un projecte en creixement. Es va decidir intentar reescriure en les nostres pròpies bicicletes les coses crítiques en què es va centrar el projecte.

La solució va tenir èxit. Hi havia una oportunitat de fer-ho, així com una necessitat extrema, perquè en aquell moment no existien altres maneres d'escalar. No hi havia un munt de bases de dades, NoSQL encara no existia, només hi havia MySQL, Memcache, PostrgreSQL, i això és tot.

Funcionament universal. El desenvolupament va ser liderat pel nostre equip de desenvolupadors C i tot es va fer de manera coherent. Independentment del motor, tots tenien aproximadament el mateix format de fitxer escrit al disc, els mateixos paràmetres de llançament, processaven els senyals de la mateixa manera i es comportaven aproximadament igual en cas de situacions i problemes de punta. Amb el creixement dels motors, és convenient que els administradors facin funcionar el sistema: no hi ha cap zoològic que s'hagi de mantenir, i han de tornar a aprendre com operar cada nova base de dades de tercers, cosa que va permetre augmentar ràpidament i còmodament. el seu nombre.

Tipus de motors

L'equip va escriure uns quants motors. Aquests són només alguns d'ells: amic, pistes, imatge, ipdb, cartes, llistes, registres, memcached, meowdb, notícies, nostradamus, foto, llistes de reproducció, pmemcached, sandbox, cerca, emmagatzematge, m'agrada, tasques, …

Per a cada tasca que requereix una estructura de dades específica o processa sol·licituds atípiques, l'equip C escriu un nou motor. Perquè no.

Tenim un motor separat memcached, que és semblant a un de normal, però amb un munt de llaminadures, i que no frena. No ClickHouse, però també funciona. Disponible per separat pmemcached - És persistent memcached, que també pot emmagatzemar dades al disc, a més, que encaixa a la memòria RAM, per no perdre dades en reiniciar. Hi ha diversos motors per a tasques individuals: cues, llistes, conjunts, tot el que requereix el nostre projecte.

Clústers

Des de la perspectiva del codi, no cal pensar en els motors o bases de dades com a processos, entitats o instàncies. El codi funciona específicament amb clústers, amb grups de motors - un tipus per clúster. Suposem que hi ha un clúster Memcached: només és un grup de màquines.

El codi no necessita saber la ubicació física, la mida o el nombre de servidors. Va al clúster utilitzant un determinat identificador.

Perquè això funcioni, heu d'afegir una entitat més que es troba entre el codi i els motors: proxy.

Proxy RPC

Proxy bus de connexió, on s'executa gairebé tot el lloc. Al mateix temps tenim cap descobriment de serveis — En canvi, hi ha una configuració per a aquest servidor intermediari, que coneix la ubicació de tots els clústers i tots els fragments d'aquest clúster. Això és el que fan els administradors.

Als programadors no els importa gens quant, on i què costa: només van al clúster. Això ens permet molt. Quan rep una sol·licitud, el servidor intermediari redirigeix ​​la sol·licitud, sabent on, ho determina ell mateix.

Preguntes freqüents sobre arquitectura i treball de VKontakte

En aquest cas, el proxy és un punt de protecció contra la fallada del servei. Si algun motor s'alenteix o es bloqueja, el servidor intermediari ho entén i respon en conseqüència al costat del client. Això us permet eliminar el temps d'espera: el codi no espera que el motor respongui, però entén que no funciona i s'ha de comportar d'una manera diferent. El codi ha d'estar preparat pel fet que les bases de dades no sempre funcionen.

Implementacions específiques

De vegades encara volem tenir algun tipus de solució no estàndard com a motor. Al mateix temps, es va decidir no utilitzar el nostre proxy rpc preparat, creat específicament per als nostres motors, sinó fer un proxy independent per a la tasca.

Per a MySQL, que encara tenim aquí i allà, fem servir db-proxy, i per a ClickHouse - Casa de gatets.

En general funciona així. Hi ha un servidor determinat, executa kPHP, Go, Python; en general, qualsevol codi que pugui utilitzar el nostre protocol RPC. El codi s'executa localment en un servidor intermediari RPC: cada servidor on es troba el codi executa el seu propi servidor intermediari local. Quan ho sol·liciti, el proxy entén a on anar.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Si un motor vol anar a un altre, encara que sigui un veí, passa per un proxy, perquè el veí pot estar en un altre centre de dades. El motor no hauria de dependre de conèixer la ubicació de res que no sigui ell mateix: aquesta és la nostra solució estàndard. Però és clar que hi ha excepcions :)

Un exemple d'un esquema TL segons el qual funcionen tots els motors.

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;

Aquest és un protocol binari, l'anàleg més proper del qual és protobuf. L'esquema prescriu camps opcionals, tipus complexos: extensions d'escalars integrats i consultes. Tot funciona segons aquest protocol.

RPC sobre TL sobre TCP/UDP... UDP?

Tenim un protocol RPC per executar sol·licituds de motor que s'executa a la part superior de l'esquema TL. Tot això funciona mitjançant una connexió TCP/UDP. TCP és comprensible, però per què necessitem UDP sovint?

UDP ajuda evitar el problema d'un gran nombre de connexions entre servidors. Si cada servidor té un proxy RPC i, en general, pot anar a qualsevol motor, hi ha desenes de milers de connexions TCP per servidor. Hi ha una càrrega, però és inútil. En el cas de l'UDP aquest problema no existeix.

No hi ha encaix de mans TCP redundant. Aquest és un problema típic: quan s'inicia un nou motor o un nou servidor, s'estableixen moltes connexions TCP alhora. Per a sol·licituds petites i lleugeres, per exemple, càrrega útil UDP, tota la comunicació entre el codi i el motor és dos paquets UDP: un vola en una direcció, el segon en l'altra. Un viatge d'anada i tornada, i el codi va rebre una resposta del motor sense encaixada de mans.

Sí, tot funciona amb un percentatge molt petit de pèrdua de paquets. El protocol té suport per a retransmissions i temps d'espera, però si perdem molt, obtindrem gairebé TCP, cosa que no és beneficiosa. No conduïm UDP a través dels oceans.

Tenim milers d'aquests servidors, i l'esquema és el mateix: s'instal·la un paquet de motors a cada servidor físic. La majoria són d'un sol fil per executar-se el més ràpid possible sense bloquejar-los, i es divideixen com a solucions d'un sol fil. Al mateix temps, no tenim res més fiable que aquests motors, i es presta molta atenció a l'emmagatzematge de dades persistent.

Emmagatzematge de dades persistent

Els motors escriuen binlogs. Un binlog és un fitxer al final del qual s'afegeix un esdeveniment per a un canvi d'estat o de dades. En diferents solucions s'anomena de manera diferent: registre binari, PASSEIG, Aof, però el principi és el mateix.

Per evitar que el motor torni a llegir el binlog sencer durant molts anys quan es reinicia, els motors escriuen instantànies - estat actual. Si cal, primer llegeixen d'ell, i després acaben de llegir des del binlog. Tots els binlogs s'escriuen en el mateix format binari, segons l'esquema TL, de manera que els administradors els puguin administrar de la mateixa manera amb les seves eines. No hi ha cap necessitat de fer instantànies. Hi ha una capçalera general que indica la instantània de qui és int, la màgia del motor i quin cos no és important per a ningú. Aquest és un problema amb el motor que va gravar la instantània.

Descriuré ràpidament el principi de funcionament. Hi ha un servidor on funciona el motor. Obre un nou binlog buit per escriure i escriu un esdeveniment per canviar-lo.

Preguntes freqüents sobre arquitectura i treball de VKontakte

En algun moment, decideix fer una instantània ell mateix o rep un senyal. El servidor crea un fitxer nou, hi escriu tot el seu estat, afegeix la mida de binlog actual - desplaçament - al final del fitxer i continua escrivint més. No s'ha creat un binlog nou.

Preguntes freqüents sobre arquitectura i treball de VKontakte

En algun moment, quan es reiniciï el motor, hi haurà tant un binlog com una instantània al disc. El motor llegeix tota la instantània i augmenta el seu estat en un moment determinat.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Llegeix la posició que es trobava en el moment en què es va crear la instantània i la mida del binlog.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Llegeix el final del binlog per obtenir l'estat actual i continua escrivint més esdeveniments. Aquest és un esquema senzill; tots els nostres motors funcionen segons ell.

Replicació de dades

Com a resultat, la replicació de dades al nostre basat en declaracions — escrivim al binlog no cap canvi de pàgina, però és a dir sol·licituds de canvi. Molt semblant al que ve per la xarxa, només lleugerament modificat.

El mateix esquema s'utilitza no només per a la replicació, sinó també per crear còpies de seguretat. Tenim un motor: un mestre d'escriptura que escriu al binlog. En qualsevol altre lloc on els administradors el configuren, aquest binlog es copia, i ja està: tenim una còpia de seguretat.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Si és necessari rèplica de lecturaPer reduir la càrrega de lectura de la CPU, simplement s'inicia el motor de lectura, que llegeix el final del binlog i executa aquestes ordres localment.

El retard aquí és molt petit, i és possible esbrinar fins a quin punt la rèplica queda enrere del mestre.

Compartiment de dades al proxy RPC

Com funciona el sharding? Com entén el servidor intermediari a quin fragment de clúster s'ha d'enviar? El codi no diu: "Envia per 15 fragments!" - No, això ho fa el proxy.

L'esquema més senzill és firstint — el primer número de la sol·licitud.

get(photo100_500) => 100 % N.

Aquest és un exemple d'un protocol de text Memcache senzill, però, per descomptat, les consultes poden ser complexes i estructurades. L'exemple pren el primer número de la consulta i la resta quan es divideix per la mida del clúster.

Això és útil quan volem tenir la localitat de dades d'una sola entitat. Suposem que 100 és un identificador d'usuari o grup i volem que totes les dades d'una entitat estiguin en un fragment per a consultes complexes.

Si no ens importa com es distribueixen les sol·licituds pel clúster, hi ha una altra opció: triturant tot el fragment.

hash(photo100_500) => 3539886280 % N

També obtenim el hash, la resta de la divisió i el número de fragment.

Ambdues opcions només funcionen si estem preparats per al fet que quan augmentem la mida del clúster, el dividirem o l'augmentarem múltiples vegades. Per exemple, teníem 16 fragments, no en tenim prou, en volem més: podem obtenir-ne 32 sense temps d'inactivitat. Si volem augmentar no múltiples, hi haurà temps d'inactivitat, perquè no podrem dividir-ho tot amb precisió sense pèrdues. Aquestes opcions són útils, però no sempre.

Si necessitem afegir o eliminar un nombre arbitrari de servidors, fem servir Hashing consistent al ring a la Ketama. Però, al mateix temps, perdem completament la localitat de les dades; hem de combinar la sol·licitud al clúster perquè cada peça torni la seva pròpia petita resposta i després fusionar les respostes al proxy.

Hi ha peticions súper específiques. Sembla així: el proxy RPC rep la sol·licitud, determina a quin clúster ha d'anar i determina el fragment. A continuació, hi ha mestres d'escriptura o, si el clúster té suport per a rèpliques, s'envia a una rèplica sota demanda. El proxy fa tot això.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Registres

Escrivim registres de diverses maneres. El més evident i senzill és escriure registres a memcache.

ring-buffer: prefix.idx = line

Hi ha un prefix clau: el nom del registre, una línia i la mida d'aquest registre: el nombre de línies. Prenem un nombre aleatori de 0 al nombre de línies menys 1. La clau de memcache és un prefix concatenat amb aquest nombre aleatori. Desem la línia de registre i l'hora actual al valor.

Quan cal llegir els registres, ho fem Multi Get totes les claus, ordenades per temps, i així obtenir un registre de producció en temps real. L'esquema s'utilitza quan cal depurar alguna cosa en producció en temps real, sense trencar res, sense aturar ni permetre el trànsit a altres màquines, però aquest registre no dura gaire.

Per a un emmagatzematge fiable de troncs disposem d'un motor logs-motor. Precisament per això es va crear i s'utilitza àmpliament en un gran nombre de clústers. El clúster més gran que conec emmagatzema 600 TB de registres empaquetats.

El motor és molt antic, hi ha grups que ja tenen 6-7 anys. Hi ha problemes amb ell que estem intentant resoldre, per exemple, vam començar a utilitzar ClickHouse activament per emmagatzemar registres.

Recollida de registres a ClickHouse

Aquest diagrama mostra com entrem als nostres motors.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Hi ha codi que va localment via RPC al proxy RPC i entén on anar al motor. Si volem escriure registres a ClickHouse, hem de canviar dues parts en aquest esquema:

  • substituir algun motor amb ClickHouse;
  • substituïu el proxy RPC, que no pot accedir a ClickHouse, amb alguna solució que pugui, i mitjançant RPC.

El motor és senzill: el substituïm per un servidor o un clúster de servidors amb ClickHouse.

I per anar a ClickHouse, ho vam fer Casa dels gatets. Si anem directament de KittenHouse a ClickHouse, no ho farà. Fins i tot sense sol·licituds, s'acumula a partir de connexions HTTP d'un gran nombre de màquines. Perquè l'esquema funcioni, en un servidor amb ClickHouse s'aixeca el proxy invers local, que està escrit de manera que pugui suportar els volums de connexions requerits. També pot emmagatzemar dades dins de si mateix de manera relativament fiable.

Preguntes freqüents sobre arquitectura i treball de VKontakte

De vegades no volem implementar l'esquema RPC en solucions no estàndard, per exemple, a nginx. Per tant, KittenHouse té la capacitat de rebre registres mitjançant UDP.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Si el remitent i el destinatari dels registres treballen a la mateixa màquina, la probabilitat de perdre un paquet UDP dins de l'amfitrió local és força baixa. Com a compromís entre la necessitat d'implementar RPC en una solució de tercers i la fiabilitat, només fem servir l'enviament UDP. Tornarem a aquest esquema més endavant.

Seguiment

Tenim dos tipus de registres: els que recullen els administradors als seus servidors i els escrits pels desenvolupadors a partir del codi. Corresponen a dos tipus de mètriques: sistema i producte.

Mètriques del sistema

Funciona en tots els nostres servidors Dades de la xarxa, que recull estadístiques i les envia a Carboni de grafit. Per tant, ClickHouse s'utilitza com a sistema d'emmagatzematge, i no Whisper, per exemple. Si cal, podeu llegir directament des de ClickHouse o utilitzar Grafana per a mètriques, gràfics i informes. Com a desenvolupadors, tenim prou accés a Netdata i Grafana.

Mètriques del producte

Per comoditat, hem escrit moltes coses. Per exemple, hi ha un conjunt de funcions ordinàries que us permeten escriure comptes, valors de comptes únics en estadístiques, que s'envien a algun lloc més llunyà.

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

$stats = statlogsStatData($params)

Posteriorment, podem utilitzar filtres d'ordenació i agrupació i fer tot el que vulguem d'estadístiques: crear gràfics, configurar Watchdogs.

Escrivim molt moltes mètriques el nombre d'esdeveniments és de 600 mil milions a 1 bilió per dia. Tanmateix, volem mantenir-los almenys un parell d'anysper entendre les tendències en mètriques. Ajuntar-ho tot és un gran problema que encara no hem resolt. Us explicaré com ha anat funcionant els darrers anys.

Tenim funcions que escriuen aquestes mètriques al memcache localper reduir el nombre d'entrades. Una vegada en un curt període de temps llançat localment dimoni d'estadístiques recull tots els registres. A continuació, el dimoni fusiona les mètriques en dues capes de servidors col·lectors de troncs, que agrega les estadístiques d'un munt de les nostres màquines perquè la capa que hi ha darrere no mori.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Si cal, podem escriure directament als col·lectors de registres.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Però escriure des del codi directament als col·leccionistes, sense passar per stas-daemom, és una solució poc escalable perquè augmenta la càrrega del col·lector. La solució només és adequada si per algun motiu no podem augmentar el dimoni d'estadístiques de memcache a la màquina, o s'ha estavellat i hem anat directament.

A continuació, els col·lectors de registres fusionen les estadístiques en meowDB - aquesta és la nostra base de dades, que també pot emmagatzemar mètriques.

Preguntes freqüents sobre arquitectura i treball de VKontakte

A continuació, podem fer seleccions binàries "quasi SQL" del codi.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Experiment

A l'estiu del 2018 vam tenir un hackathon intern i va sorgir la idea d'intentar substituir la part vermella del diagrama per alguna cosa que pogués emmagatzemar mètriques a ClickHouse. Tenim registres a ClickHouse, per què no provar-ho?

Preguntes freqüents sobre arquitectura i treball de VKontakte

Teníem un esquema que escrivia registres a través de KittenHouse.

Preguntes freqüents sobre arquitectura i treball de VKontakte

Vam decidir afegiu un altre "*Casa" al diagrama, que rebrà exactament les mètriques en el format tal com el nostre codi les escrigui mitjançant UDP. Aleshores, aquesta *House els converteix en insercions, com els troncs, que KittenHouse entén. Pot lliurar perfectament aquests registres a ClickHouse, que hauria de poder llegir-los.

Preguntes freqüents sobre arquitectura i treball de VKontakte

L'esquema amb memcache, stats-daemon i logs-collectors base de dades es substitueix per aquest.

Preguntes freqüents sobre arquitectura i treball de VKontakte

L'esquema amb memcache, stats-daemon i logs-collectors base de dades es substitueix per aquest.

  • Aquí hi ha un enviament del codi, que està escrit localment a StatsHouse.
  • StatsHouse escriu mètriques UDP, ja convertides en insercions SQL, a KittenHouse per lots.
  • KittenHouse els envia a ClickHouse.
  • Si els volem llegir, els llegim sense passar StatsHouse, directament des de ClickHouse mitjançant SQL normal.

És encara experiment, però ens agrada com queda. Si solucionem els problemes amb l'esquema, potser el canviarem completament. Personalment, espero que sí.

L'esquema no estalvia ferro. Es necessiten menys servidors, no calen els dimonis d'estadístiques i els col·lectors de registres locals, però ClickHouse requereix un servidor més gran que els de l'esquema actual. Es necessiten menys servidors, però han de ser més cars i més potents.

Desplega

Primer, mirem el desplegament de PHP. Estem desenvolupant en git: utilitzar GitLab и TeamCity per al desplegament. Les branques de desenvolupament es fusionen a la branca mestra, des de la branca mestra per a la prova es fusionen a la posada en escena i de la posada en escena a la producció.

Abans del desplegament, es prenen la branca de producció actual i l'anterior, i s'hi tenen en compte els fitxers diff - canvis: creats, suprimits, canviats. Aquest canvi es registra al binlog d'un motor especial de copyfast, que pot replicar ràpidament els canvis a tota la nostra flota de servidors. El que s'utilitza aquí no és copiar directament, sinó replicació de xafarderies, quan un servidor envia canvis als seus veïns més propers, aquells als seus veïns, etc. Això us permet actualitzar el codi en desenes i unitats de segons a tota la flota. Quan el canvi arriba a la rèplica local, li aplica aquests pedaços sistema de fitxers local. La recuperació també es realitza segons el mateix esquema.

També implementem kPHP molt i també té el seu propi desenvolupament git segons el diagrama anterior. Des d'això Servidor HTTP binari, llavors no podem produir diff: el binari de llançament pesa centenars de MB. Per tant, aquí hi ha una altra opció: la versió està escrita binlog copyfast. Amb cada construcció augmenta, i durant el rollback també augmenta. Versió replicat als servidors. Els copyfasts locals veuen que una nova versió ha entrat al binlog i, mitjançant la mateixa replicació de xafarderies, prenen l'última versió del binari per ells mateixos, sense cansar el nostre servidor mestre, però repartint acuradament la càrrega per la xarxa. El que segueix rellançament graciós per a la nova versió.

Per als nostres motors, que també són essencialment binaris, l'esquema és molt similar:

  • branca mestra de git;
  • binari a .deb;
  • la versió s'escriu a binlog copyfast;
  • replicat als servidors;
  • el servidor treu un nou .dep;
  • dpkg -i;
  • rellançament elegant a la nova versió.

La diferència és que el nostre binari està empaquetat en arxius .deb, i en treure'ls dpkg -i es col·loquen al sistema. Per què es desplega kPHP com a binari i els motors es despleguen com a dpkg? Va passar així. Funciona, no el toqueu.

Enllaços útils:

Alexey Akulovich és un dels que, com a part del Comitè del Programa, ajuda PHP Rússia el 17 de maig es convertirà en l'esdeveniment més gran per als desenvolupadors de PHP dels últims temps. Mireu quin ordinador més xulo tenim, què altaveus (dos d'ells estan desenvolupant el nucli de PHP!) - sembla una cosa que no us podeu perdre si escriviu PHP.

Font: www.habr.com

Afegeix comentari