Veelgestelde vragen over architectuur en werk van VKontakte

De geschiedenis van de oprichting van VKontakte staat op Wikipedia, het werd door Pavel zelf verteld. Het lijkt erop dat iedereen haar al kent. Over de binnenkant, architectuur en structuur van de site op HighLoad++ Pavel vertelde het mij in 2010. Sindsdien zijn veel servers gelekt, dus we zullen de informatie bijwerken: we zullen het ontleden, de binnenkant eruit halen, het wegen en het VK-apparaat vanuit technisch oogpunt bekijken.

Veelgestelde vragen over architectuur en werk van VKontakte

Alexey Akulovich (AterCattus) backend-ontwikkelaar in het VKontakte-team. De transcriptie van dit rapport is een gezamenlijk antwoord op veelgestelde vragen over de werking van het platform, infrastructuur, servers en interactie daartussen, maar niet over de ontwikkeling, namelijk over ijzer. Afzonderlijk over databases en wat VK in plaats daarvan heeft, over het verzamelen van logs en het monitoren van het hele project als geheel. Details onder de snit.



Al ruim vier jaar houd ik me bezig met allerlei taken die met de backend te maken hebben.

  • Uploaden, opslaan, verwerken, distribueren van media: video, livestreaming, audio, foto's, documenten.
  • Infrastructuur, platform, ontwikkelaarsmonitoring, logs, regionale caches, CDN, eigen RPC-protocol.
  • Integratie met externe diensten: pushmeldingen, parseren van externe links, RSS-feed.
  • Collega's helpen met diverse vragen, waarvan de antwoorden een duik in onbekende code vereisen.

Gedurende deze tijd heb ik aan veel onderdelen van de site meegewerkt. Ik wil deze ervaring delen.

Algemene architectuur

Alles begint, zoals gewoonlijk, met een server of een groep servers die verzoeken accepteren.

Front-server

De frontserver accepteert verzoeken via HTTPS, RTMP en WSS.

HTTPS - dit zijn verzoeken voor de hoofd- en mobiele webversies van de site: vk.com en m.vk.com, en andere officiële en niet-officiële clients van onze API: mobiele clients, messengers. Wij hebben een receptie RTMP-verkeer voor live-uitzendingen met aparte frontservers en WSS- verbindingen voor Streaming API.

Voor HTTPS en WSS op servers is het de moeite waard nginx. Voor RTMP uitzendingen zijn wij onlangs overgestapt op onze eigen oplossing steen, maar dit valt buiten de reikwijdte van het rapport. Voor fouttolerantie adverteren deze servers gemeenschappelijke IP-adressen en werken ze in groepen, zodat gebruikersverzoeken niet verloren gaan als er een probleem is op een van de servers. Voor HTTPS en WSS coderen dezelfde servers het verkeer om een ​​deel van de CPU-belasting op zich te nemen.

We zullen het verder niet hebben over WSS en RTMP, maar alleen over standaard HTTPS-verzoeken, die meestal bij een webproject horen.

backend

Achter de voorkant bevinden zich meestal backend-servers. Ze verwerken verzoeken die de frontserver van clients ontvangt.

Het kPHP-servers, waarop de HTTP-daemon draait, omdat HTTPS al is gedecodeerd. kPHP is een server die draait op modellen met voorvork: start een masterproces, een aantal onderliggende processen, geeft luistersockets aan hen door en zij verwerken hun verzoeken. In dit geval worden processen niet tussen elk verzoek van de gebruiker opnieuw opgestart, maar worden ze eenvoudigweg teruggezet naar de oorspronkelijke nulwaardestatus - verzoek na verzoek, in plaats van opnieuw te starten.

Lading distributie

Al onze backends zijn geen enorme verzameling machines die elk verzoek kunnen verwerken. Wij zij verdeeld in aparte groepen: algemeen, mobiel, api, video, staging... Het probleem op een afzonderlijke groep machines heeft geen invloed op alle andere. In geval van problemen met video zal de gebruiker die naar muziek luistert niet eens op de hoogte zijn van de problemen. Naar welke backend het verzoek moet worden verzonden, wordt bepaald door nginx aan de voorkant volgens de configuratie.

Metriek verzamelen en opnieuw in evenwicht brengen

Om te begrijpen hoeveel auto's we in elke groep nodig hebben, hebben we vertrouw niet op QPS. De backends zijn verschillend, ze hebben verschillende verzoeken, elk verzoek heeft een andere complexiteit bij het berekenen van QPS. Daarom wij we werken met het concept van belasting van de server als geheel - van de CPU en perf.

We hebben duizenden van dergelijke servers. Elke fysieke server draait een kPHP-groep om alle cores te recyclen (omdat kPHP single threaded is).

Inhoudsserver

CS of Content Server is een opslag. CS is een server die bestanden opslaat en ook geüploade bestanden en allerlei synchrone achtergrondtaken verwerkt die de hoofdwebfrontend eraan toewijst.

We hebben tienduizenden fysieke servers waarop bestanden worden opgeslagen. Gebruikers uploaden graag bestanden, en wij bewaren en delen ze graag. Sommige van deze servers zijn afgesloten door speciale pu/pp-servers.

pu/pp

Als je het netwerktabblad in VK opende, zag je pu/pp.

Veelgestelde vragen over architectuur en werk van VKontakte

Wat is pu/pp? Als we de ene server na de andere sluiten, zijn er twee opties voor het uploaden en downloaden van een bestand naar de gesloten server: direct door http://cs100500.userapi.com/path of via tussenserver - http://pu.vk.com/c100500/path.

Pu is de historische naam voor het uploaden van foto's en pp is foto-proxy. Dat wil zeggen, de ene server is voor het uploaden van foto's en de andere voor het uploaden. Nu worden niet alleen foto's geladen, maar is de naam behouden gebleven.

Deze servers HTTPS-sessies beëindigenom de processorbelasting uit de opslag te verwijderen. Omdat gebruikersbestanden op deze servers worden verwerkt, is het ook zo dat hoe minder gevoelige informatie op deze machines wordt opgeslagen, hoe beter. Bijvoorbeeld HTTPS-coderingssleutels.

Omdat de machines gesloten zijn door onze andere machines, kunnen we het ons veroorloven om ze geen ‘witte’ externe IP’s te geven, en geef "grijs". Op deze manier bespaarden we op de IP-pool en beschermden we de machines gegarandeerd tegen toegang van buitenaf - er is eenvoudigweg geen IP-adres om erin te komen.

Veerkracht over gedeelde IP's. In termen van fouttolerantie werkt het schema hetzelfde: verschillende fysieke servers hebben een gemeenschappelijk fysiek IP-adres en de hardware ervoor kiest waar het verzoek naartoe moet worden gestuurd. Ik zal later over andere opties praten.

Het controversiële punt is dat in dit geval de client behoudt minder verbindingen. Als er hetzelfde IP-adres is voor meerdere machines - met dezelfde host: pu.vk.com of pp.vk.com, heeft de clientbrowser een limiet op het aantal gelijktijdige verzoeken aan één host. Maar in de tijd van het alomtegenwoordige HTTP/2 geloof ik dat dit niet langer zo relevant is.

Het voor de hand liggende nadeel van het plan is dat het wel moet pomp al het verkeer, die via een andere server naar de opslag gaat. Omdat we verkeer door machines pompen, kunnen we met hetzelfde schema nog geen zwaar verkeer, bijvoorbeeld video, pompen. Wij zenden het rechtstreeks uit - een aparte directe verbinding voor aparte opslag speciaal voor video. We verzenden lichtere inhoud via een proxy.

Nog niet zo lang geleden kregen we een verbeterde versie van proxy. Nu zal ik je vertellen hoe ze verschillen van gewone en waarom dit nodig is.

Zon

In september 2017 kocht Oracle, dat eerder Sun had gekocht, heeft een groot aantal werknemers van Sun ontslagen. We kunnen zeggen dat het bedrijf op dit moment ophield te bestaan. Bij het kiezen van een naam voor het nieuwe systeem besloten onze beheerders hulde te brengen aan de nagedachtenis van dit bedrijf en noemden het nieuwe systeem Sun. Onder ons noemen we haar eenvoudigweg “zonnen”.

Veelgestelde vragen over architectuur en werk van VKontakte

pp had een paar problemen. Eén IP per groep - ineffectieve cache. Verschillende fysieke servers delen een gemeenschappelijk IP-adres en er is geen manier om te bepalen naar welke server het verzoek gaat. Als verschillende gebruikers voor hetzelfde bestand komen, komt het bestand dus in de cache van elke server terecht als er een cache op deze servers staat. Dit is een zeer inefficiënt plan, maar er kan niets aan worden gedaan.

Vervolgens - we kunnen de inhoud niet delen, omdat we geen specifieke server voor deze groep kunnen selecteren - ze hebben een gemeenschappelijk IP-adres. Ook om enkele interne redenen hebben we dat gedaan het was niet mogelijk om dergelijke servers in regio's te installeren. Ze stonden alleen in Sint-Petersburg.

Met de zonnen hebben we het selectiesysteem veranderd. Nu hebben we anycast-routering: dynamische routing, anycast, zelfcontrole-daemon. Elke server heeft zijn eigen individuele IP-adres, maar een gemeenschappelijk subnet. Alles is zo geconfigureerd dat als er één server uitvalt, het verkeer automatisch over de andere servers van dezelfde groep wordt verspreid. Nu is het mogelijk om een ​​specifieke server te selecteren, geen overbodige cachingen de betrouwbaarheid werd niet beïnvloed.

Gewicht ondersteuning. Nu kunnen we het ons veroorloven om indien nodig machines met een ander vermogen te installeren, en ook, in geval van tijdelijke problemen, de gewichten van de werkende ‘zonnen’ te veranderen om de belasting ervan te verminderen, zodat ze ‘rusten’ en weer gaan werken.

Sharding op inhouds-ID. Het grappige van sharding: we delen de inhoud meestal zodat verschillende gebruikers via dezelfde "zon" naar hetzelfde bestand gaan, zodat ze een gemeenschappelijke cache hebben.

We hebben onlangs de applicatie “Clover” gelanceerd. Dit is een online quiz in een live uitzending, waarbij de presentator vragen stelt en gebruikers in realtime antwoorden, waarbij ze opties kiezen. De app heeft een chat waar gebruikers kunnen chatten. Kan tegelijkertijd verbinding maken met de uitzending ruim 100 duizend mensen. Ze schrijven allemaal berichten die naar alle deelnemers worden gestuurd, en er komt een avatar bij het bericht. Als 100 mensen voor één avatar in één ‘zon’ komen, dan kan deze soms achter een wolk rollen.

Om uitbarstingen van verzoeken voor hetzelfde bestand te kunnen weerstaan, schakelen we voor een bepaald type inhoud een stom schema in dat bestanden verspreidt over alle beschikbare “zonnen” in de regio.

Zon van binnenuit

Omgekeerde proxy op nginx, cache in RAM of op snelle Optane/NVMe-schijven. Voorbeeld: http://sun4-2.userapi.com/c100500/path — een link naar de “zon”, die zich in de vierde regio, de tweede servergroep, bevindt. Het sluit het padbestand, dat fysiek op server 100500 ligt.

cache

We voegen nog een knooppunt toe aan ons architecturale schema: de cachingomgeving.

Veelgestelde vragen over architectuur en werk van VKontakte

Hieronder vindt u het lay-outdiagram regionale caches, er zijn er ongeveer 20. Dit zijn de plaatsen waar caches en “zonnen” zich bevinden, die het verkeer via zichzelf kunnen cachen.

Veelgestelde vragen over architectuur en werk van VKontakte

Dit is het cachen van multimedia-inhoud; hier worden geen gebruikersgegevens opgeslagen - alleen muziek, video, foto's.

Om de regio van de gebruiker te bepalen, gebruiken we we verzamelen BGP-netwerkvoorvoegsels die in de regio's zijn aangekondigd. In het geval van fallback moeten we ook de geoip-database parseren als we het IP-adres niet kunnen vinden aan de hand van voorvoegsels. We bepalen de regio aan de hand van het IP-adres van de gebruiker. In de code kunnen we naar een of meer regio's van de gebruiker kijken: de punten waar hij geografisch het dichtst bij is.

Hoe werkt het?

We tellen de populariteit van bestanden per regio. Er is een nummer van de regionale cache waar de gebruiker zich bevindt, en een bestandsidentificatie. We nemen dit paar en verhogen de beoordeling bij elke download.

Tegelijkertijd komen er van tijd tot tijd demonen - services in regio's - naar de API en zeggen: “Ik ben die en die cache, geef me een lijst met de meest populaire bestanden in mijn regio die nog niet bij mij staan. ” De API levert een aantal bestanden gesorteerd op beoordeling, de daemon downloadt ze, brengt ze naar de regio's en levert de bestanden van daaruit af. Dit is het fundamentele verschil tussen pu/pp en Sun vanuit caches: ze geven het bestand onmiddellijk door zichzelf, zelfs als dit bestand niet in de cache staat, en de cache downloadt het bestand eerst naar zichzelf en begint het vervolgens terug te geven.

In dit geval krijgen we inhoud dichter bij de gebruikers en het spreiden van de netwerkbelasting. Alleen vanuit de Moskou-cache distribueren we tijdens piekuren bijvoorbeeld meer dan 1 Tbit/s.

Maar er zijn problemen - cacheservers zijn niet van rubber. Voor superpopulaire inhoud is er soms niet genoeg netwerk voor een aparte server. Onze cacheservers zijn 40-50 Gbit/s, maar er is inhoud die zo'n kanaal volledig verstopt. We zijn op weg naar de implementatie van opslag van meer dan één kopie van populaire bestanden in de regio. Ik hoop dat we het tegen het einde van het jaar zullen implementeren.

We hebben gekeken naar de algemene architectuur.

  • Frontservers die verzoeken accepteren.
  • Backends die verzoeken verwerken.
  • Opslagplaatsen die worden gesloten door twee soorten proxy's.
  • Regionale caches.

Wat ontbreekt er in dit diagram? Uiteraard de databases waarin wij gegevens opslaan.

Databases of motoren

We noemen ze geen databases, maar motoren - Engines, omdat we praktisch geen databases hebben in de algemeen aanvaarde zin.

Veelgestelde vragen over architectuur en werk van VKontakte

Dit is een noodzakelijke maatregel.. Dit gebeurde omdat in 2008-2009, toen VK een explosieve groei in populariteit kende, het project volledig op MySQL en Memcache werkte en er problemen waren. MySQL hield ervan om bestanden te crashen en te corrumperen, waarna het niet meer kon herstellen, en Memcache ging geleidelijk achteruit in prestaties en moest opnieuw worden opgestart.

Het blijkt dat het steeds populairder wordende project permanente opslag had, die gegevens corrumpeert, en een cache die vertraagt. In dergelijke omstandigheden is het moeilijk om een ​​groeiend project te ontwikkelen. Er werd besloten om te proberen de cruciale zaken waarop het project zich richtte op onze eigen fietsen te herschrijven.

De oplossing was succesvol. Er was een mogelijkheid om dit te doen, maar ook een extreme noodzaak, omdat andere manieren van opschalen op dat moment nog niet bestonden. Er waren niet veel databases, NoSQL bestond nog niet, er waren alleen MySQL, Memcache, PostrgreSQL - en dat was alles.

Universele bediening. De ontwikkeling werd geleid door ons team van C-ontwikkelaars en alles gebeurde op een consistente manier. Ongeacht de engine hadden ze allemaal ongeveer hetzelfde bestandsformaat naar schijf geschreven, dezelfde opstartparameters, verwerkten ze signalen op dezelfde manier en gedroegen ze zich ongeveer hetzelfde in geval van randsituaties en problemen. Met de groei van de zoekmachines is het voor beheerders handiger om het systeem te bedienen - er is geen dierentuin die onderhouden hoeft te worden, en ze moeten opnieuw leren hoe ze elke nieuwe database van derden moeten bedienen, waardoor het mogelijk werd om snel en gemakkelijk de hun nummer.

Soorten motoren

Het team schreef een flink aantal motoren. Hier zijn er slechts enkele: vriend, hints, afbeelding, ipdb, brieven, lijsten, logs, memcached, meowdb, nieuws, nostradamus, foto, afspeellijsten, pmemcached, sandbox, zoeken, opslag, likes, taken, ...

Voor elke taak die een specifieke datastructuur vereist of atypische verzoeken verwerkt, schrijft het C-team een ​​nieuwe engine. Waarom niet.

Wij hebben een aparte motor memcached, die vergelijkbaar is met een gewone, maar met een heleboel lekkers, en die niet vertraagt. Niet ClickHouse, maar het werkt ook. Apart verkrijgbaar pmemcached - Is aanhoudende memcaching, die bovendien ook gegevens op schijf kan opslaan dan in het RAM-geheugen past, om geen gegevens te verliezen bij het opnieuw opstarten. Er zijn verschillende motoren voor individuele taken: wachtrijen, lijsten, sets - alles wat ons project nodig heeft.

Clusters

Vanuit codeperspectief is het niet nodig om engine of databases te zien als processen, entiteiten of instances. De code werkt specifiek met clusters, met groepen motoren - één type per cluster. Laten we zeggen dat er een memcached cluster is: het is gewoon een groep machines.

De code hoeft helemaal niet de fysieke locatie, grootte of aantal servers te kennen. Hij gaat naar het cluster met behulp van een bepaalde identificatie.

Om dit te laten werken, moet je nog een entiteit toevoegen die zich tussen de code en de motoren bevindt - volmacht.

RPC-proxy

Volmacht verbindende bus, waarop vrijwel de hele site draait. Tegelijkertijd hebben wij dat ook gedaan geen servicedetectie — in plaats daarvan is er een configuratie voor deze proxy, die de locatie van alle clusters en alle shards van dit cluster kent. Dit is wat beheerders doen.

Het maakt programmeurs helemaal niet uit hoeveel, waar en wat het kost; ze gaan gewoon naar het cluster. Dit maakt ons veel mogelijk. Bij ontvangst van een verzoek stuurt de proxy het verzoek door, wetende waarheen - hij bepaalt dit zelf.

Veelgestelde vragen over architectuur en werk van VKontakte

In dit geval is proxy een beschermingspunt tegen servicestoringen. Als een engine langzamer gaat of crasht, begrijpt de proxy dit en reageert dienovereenkomstig op de clientzijde. Hiermee kunt u de time-out verwijderen - de code wacht niet tot de engine reageert, maar begrijpt dat deze niet werkt en zich op de een of andere manier anders moet gedragen. De code moet voorbereid zijn op het feit dat de databases niet altijd werken.

Specifieke implementaties

Soms willen we toch echt een soort niet-standaard oplossing als motor hebben. Tegelijkertijd werd besloten om onze kant-en-klare rpc-proxy, speciaal gemaakt voor onze motoren, niet te gebruiken, maar een aparte proxy voor de taak te maken.

Voor MySQL, dat we hier en daar nog hebben, gebruiken we db-proxy, en voor ClickHouse - Kittenhuis.

Het werkt over het algemeen zo. Er is een bepaalde server, deze draait kPHP, Go, Python - in het algemeen elke code die ons RPC-protocol kan gebruiken. De code draait lokaal op een RPC-proxy. Elke server waarop de code zich bevindt, draait zijn eigen lokale proxy. Op verzoek begrijpt de proxy waar hij heen moet.

Veelgestelde vragen over architectuur en werk van VKontakte

Als de ene engine naar de andere wil gaan, ook al is het een buurman, gaat dat via een proxy, omdat de buurman zich mogelijk in een ander datacenter bevindt. De motor mag er niet op vertrouwen dat hij de locatie van iets anders dan zichzelf kent - dit is onze standaardoplossing. Maar er zijn natuurlijk uitzonderingen :)

Een voorbeeld van een TL-schema waarbij alle motoren werken.

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;

Dit is een binair protocol, waarvan de dichtstbijzijnde analoog is prototype. Het schema beschrijft vooraf optionele velden, complexe typen - uitbreidingen van ingebouwde scalairen en query's. Alles werkt volgens dit protocol.

RPC via TL via TCP/UDP… UDP?

We hebben een RPC-protocol voor het uitvoeren van engine-aanvragen dat bovenop het TL-schema draait. Dit werkt allemaal via een TCP/UDP-verbinding. TCP is begrijpelijk, maar waarom hebben we UDP vaak nodig?

UDP helpt vermijd het probleem van een groot aantal verbindingen tussen servers. Als elke server een RPC-proxy heeft en deze in het algemeen naar elke engine kan gaan, dan zijn er tienduizenden TCP-verbindingen per server. Er is een lading, maar het is nutteloos. In het geval van UDP bestaat dit probleem niet.

Geen redundante TCP-handshake. Dit is een typisch probleem: wanneer een nieuwe engine of een nieuwe server wordt gelanceerd, worden er veel TCP-verbindingen tegelijk tot stand gebracht. Voor kleine lichtgewicht verzoeken, bijvoorbeeld UDP-payload, is alle communicatie tussen de code en de engine twee UDP-pakketten: de één vliegt in de ene richting, de tweede in de andere. Eén retourrit - en de code kreeg een reactie van de motor zonder een handdruk.

Ja, het werkt allemaal gewoon met een zeer klein percentage pakketverlies. Het protocol biedt ondersteuning voor hertransmissies en time-outs, maar als we veel verliezen, krijgen we bijna TCP, wat niet gunstig is. We drijven UDP niet over de oceanen.

We hebben duizenden van dergelijke servers en het schema is hetzelfde: op elke fysieke server wordt een pakket met motoren geïnstalleerd. Ze zijn meestal single-threaded om zo snel mogelijk te werken zonder te blokkeren, en worden geshard als single-threaded oplossingen. Tegelijkertijd hebben we niets betrouwbaarder dan deze motoren en wordt er veel aandacht besteed aan persistente gegevensopslag.

Permanente gegevensopslag

Motoren schrijven binlogs. Een binlog is een bestand aan het einde waarvan een gebeurtenis voor een wijziging in de status of gegevens wordt toegevoegd. In verschillende oplossingen wordt het anders genoemd: binair log, WAL, AOF, maar het principe is hetzelfde.

Om te voorkomen dat de engine bij het herstarten jarenlang de hele binlog opnieuw leest, schrijven de engine momentopnamen - huidige status. Indien nodig lezen ze er eerst uit en lezen dan verder uit de binlog. Alle binlogs zijn geschreven in hetzelfde binaire formaat - volgens het TL-schema, zodat beheerders ze op dezelfde manier kunnen beheren met hun tools. Er zijn geen momentopnamen nodig. Er is een algemene header die aangeeft wiens momentopname int is, de magie van de engine en welk lichaam voor niemand belangrijk is. Dit is een probleem met de engine die de momentopname heeft vastgelegd.

Ik zal snel het werkingsprincipe beschrijven. Er is een server waarop de engine draait. Hij opent een nieuwe lege binlog om te schrijven en schrijft er een gebeurtenis voor verandering in.

Veelgestelde vragen over architectuur en werk van VKontakte

Op een gegeven moment besluit hij óf zelf een momentopname te maken, óf hij ontvangt een signaal. De server maakt een nieuw bestand aan, schrijft de volledige status erin, voegt de huidige binlog-grootte (offset) toe aan het einde van het bestand en gaat verder met schrijven. Er wordt geen nieuwe binlog aangemaakt.

Veelgestelde vragen over architectuur en werk van VKontakte

Op een gegeven moment, wanneer de motor opnieuw wordt opgestart, zal er zowel een binlog als een momentopname op de schijf staan. De engine leest de volledige momentopname en verhoogt de status op een bepaald punt.

Veelgestelde vragen over architectuur en werk van VKontakte

Leest de positie die was op het moment dat de momentopname werd gemaakt en de grootte van de binlog.

Veelgestelde vragen over architectuur en werk van VKontakte

Leest het einde van de binlog om de huidige status te krijgen en gaat door met het schrijven van verdere gebeurtenissen. Dit is een eenvoudig schema; al onze motoren werken volgens dit schema.

Gegevensreplicatie

Als gevolg hiervan is gegevensreplicatie in onze op verklaring gebaseerd — we schrijven in de binlog geen paginawijzigingen, maar namelijk verander verzoeken. Zeer vergelijkbaar met wat er via het netwerk komt, alleen licht gewijzigd.

Hetzelfde schema wordt niet alleen gebruikt voor replicatie, maar ook om back-ups te maken. We hebben een engine: een schrijfmaster die naar de binlog schrijft. Op elke andere plaats waar de beheerders het hebben ingesteld, wordt deze binlog gekopieerd, en dat is alles: we hebben een back-up.

Veelgestelde vragen over architectuur en werk van VKontakte

Indien nodig replica lezenOm de leesbelasting van de CPU te verminderen, wordt eenvoudigweg de leesengine gestart, die het einde van de binlog leest en deze opdrachten lokaal uitvoert.

De vertraging is hier erg klein en het is mogelijk om erachter te komen hoeveel de replica achterblijft bij de meester.

Gegevensschering in RPC-proxy

Hoe werkt sharden? Hoe begrijpt de proxy naar welke clusterscherf moet worden verzonden? De code zegt niet: "Stuur voor 15 scherven!" - nee, dit wordt gedaan door de proxy.

Het eenvoudigste schema is firstint — het eerste nummer in het verzoek.

get(photo100_500) => 100 % N.

Dit is een voorbeeld van een eenvoudig tekstprotocol in de memcache, maar zoekopdrachten kunnen uiteraard complex en gestructureerd zijn. In het voorbeeld wordt het eerste getal in de query gebruikt en de rest, gedeeld door de clustergrootte.

Dit is handig als we de gegevenslocatie van één enkele entiteit willen hebben. Stel dat 100 een gebruikers- of groeps-ID is en we willen dat alle gegevens van één entiteit op één Shard staan ​​voor complexe query's.

Als het ons niet uitmaakt hoe verzoeken over het cluster worden verspreid, is er nog een andere optie: de hele scherf hashen.

hash(photo100_500) => 3539886280 % N

We krijgen ook de hash, de rest van de divisie en het scherfnummer.

Beide opties werken alleen als we erop voorbereid zijn dat wanneer we de omvang van het cluster vergroten, we het zullen splitsen of meerdere keren zullen vergroten. We hadden bijvoorbeeld 16 scherven, we hebben niet genoeg, we willen meer - we kunnen er veilig 32 krijgen zonder downtime. Als we geen veelvouden willen verhogen, zal er downtime zijn, omdat we niet alles nauwkeurig kunnen opsplitsen zonder verliezen. Deze opties zijn nuttig, maar niet altijd.

Als we een willekeurig aantal servers moeten toevoegen of verwijderen, gebruiken we Consistente hashing op de ring a la Ketama. Maar tegelijkertijd verliezen we de locatie van de gegevens volledig; we moeten het verzoek samenvoegen met het cluster, zodat elk stuk zijn eigen kleine antwoord retourneert, en vervolgens de antwoorden samenvoegen met de proxy.

Er zijn superspecifieke verzoeken. Het ziet er als volgt uit: RPC-proxy ontvangt het verzoek, bepaalt naar welk cluster het moet gaan en bepaalt de shard. Dan zijn er schrijfmasters, of, als het cluster replica-ondersteuning heeft, wordt er op aanvraag naar een replica verzonden. De proxy doet dit allemaal.

Veelgestelde vragen over architectuur en werk van VKontakte

Logboeken

We schrijven logs op verschillende manieren. De meest voor de hand liggende en eenvoudige is schrijf logs naar geheugencache.

ring-buffer: prefix.idx = line

Er is een sleutelvoorvoegsel - de naam van het logbestand, een regel, en er is de grootte van dit logbestand - het aantal regels. We nemen een willekeurig getal van 0 tot het aantal regels minus 1. De sleutel in memcache is een voorvoegsel dat is samengevoegd met dit willekeurige getal. We slaan de logregel en de huidige tijd op in de waarde.

Wanneer het nodig is om logs te lezen, voeren wij dit uit Multi-krijg alle sleutels, gesorteerd op tijd, en krijg zo in realtime een productielogboek. Het schema wordt gebruikt wanneer u in realtime iets in de productie moet debuggen, zonder iets kapot te maken, zonder het verkeer naar andere machines te stoppen of toe te staan, maar dit logboek duurt niet lang.

Voor een betrouwbare opslag van boomstammen hebben wij een motor logs-engine. Dit is precies waarom het is gemaakt en op grote schaal wordt gebruikt in een groot aantal clusters. Het grootste cluster dat ik ken, slaat 600 TB aan verpakte logboeken op.

De motor is erg oud, er zijn clusters die al 6-7 jaar oud zijn. Er zijn problemen mee die we proberen op te lossen, we zijn ClickHouse bijvoorbeeld actief gaan gebruiken om logboeken op te slaan.

Logboeken verzamelen in ClickHouse

Dit diagram laat zien hoe wij onze motoren binnenlopen.

Veelgestelde vragen over architectuur en werk van VKontakte

Er is code die lokaal via RPC naar de RPC-proxy gaat, en deze begrijpt waar hij naar de engine moet gaan. Als we logs in ClickHouse willen schrijven, moeten we twee delen in dit schema wijzigen:

  • vervang een engine door ClickHouse;
  • vervang de RPC-proxy, die geen toegang heeft tot ClickHouse, door een oplossing die dat wel kan, en via RPC.

De engine is eenvoudig: we vervangen deze door een server of een cluster van servers met ClickHouse.

En om naar ClickHouse te gaan, dat hebben we gedaan KittenHuis. Als we rechtstreeks van KittenHouse naar ClickHouse gaan, gaat dat niet lukken. Zelfs zonder verzoeken komt het voort uit HTTP-verbindingen van een groot aantal machines. Om het schema te laten werken, op een server met ClickHouse lokale reverse proxy wordt verhoogd, dat zo is geschreven dat het de vereiste volumes aan verbindingen kan weerstaan. Het kan ook relatief betrouwbaar gegevens binnen zichzelf bufferen.

Veelgestelde vragen over architectuur en werk van VKontakte

Soms willen we het RPC-schema niet implementeren in niet-standaardoplossingen, bijvoorbeeld in nginx. Daarom heeft KittenHouse de mogelijkheid om logs via UDP te ontvangen.

Veelgestelde vragen over architectuur en werk van VKontakte

Als de afzender en de ontvanger van de logbestanden op dezelfde machine werken, is de kans dat een UDP-pakket binnen de lokale host verloren gaat vrij klein. Als compromis tussen de noodzaak om RPC te implementeren in een oplossing van derden en betrouwbaarheid, gebruiken we eenvoudigweg UDP-verzending. Op dit schema komen we later terug.

controle

We hebben twee soorten logboeken: logboeken die door beheerders op hun servers zijn verzameld en logboeken die door ontwikkelaars op basis van code zijn geschreven. Ze komen overeen met twee soorten statistieken: systeem en product.

Systeemstatistieken

Het werkt op al onze servers netdata, dat statistieken verzamelt en deze naar stuurt Grafiet Koolstof. Daarom wordt ClickHouse gebruikt als opslagsysteem en niet bijvoorbeeld Whisper. Indien nodig kunt u direct uit ClickHouse lezen of gebruiken grafana voor statistieken, grafieken en rapporten. Als ontwikkelaars hebben we voldoende toegang tot Netdata en Grafana.

Productstatistieken

Voor het gemak hebben we veel dingen opgeschreven. Er is bijvoorbeeld een reeks gewone functies waarmee u Counts en UniqueCounts-waarden in statistieken kunt schrijven, die ergens verder worden verzonden.

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

$stats = statlogsStatData($params)

Vervolgens kunnen we sorteer- en groeperingsfilters gebruiken en alles doen wat we willen op het gebied van statistieken: grafieken bouwen, Watchdogs configureren.

Wij schrijven heel veel statistieken het aantal evenementen varieert van 600 miljard tot 1 biljoen per dag. Wij willen ze echter behouden minstens een paar jaarom trends in statistieken te begrijpen. Het allemaal samenbrengen is een groot probleem dat we nog niet hebben opgelost. Ik zal je vertellen hoe het de afgelopen jaren heeft gewerkt.

We hebben functies die deze statistieken schrijven naar lokale geheugencacheom het aantal inzendingen te verminderen. Eens in de zoveel tijd lokaal gelanceerd statistieken-daemon verzamelt alle records. Vervolgens voegt de demon de statistieken samen in twee serverlagen logs-verzamelaars, dat statistieken van een aantal van onze machines verzamelt, zodat de laag erachter niet sterft.

Veelgestelde vragen over architectuur en werk van VKontakte

Indien nodig kunnen we rechtstreeks naar logboekverzamelaars schrijven.

Veelgestelde vragen over architectuur en werk van VKontakte

Maar het rechtstreeks schrijven van code naar verzamelaars, waarbij stas-daemom wordt omzeild, is een slecht schaalbare oplossing omdat het de belasting van de verzamelaar vergroot. De oplossing is alleen geschikt als we om de een of andere reden de memcache-statistieken-daemon op de machine niet kunnen verhogen, of als deze crasht en we direct zijn gegaan.

Vervolgens voegen logboekverzamelaars statistieken samen in miauwDB - dit is onze database, die ook statistieken kan opslaan.

Veelgestelde vragen over architectuur en werk van VKontakte

Vervolgens kunnen we binaire ‘near-SQL’-selecties maken op basis van de code.

Veelgestelde vragen over architectuur en werk van VKontakte

Experiment

In de zomer van 2018 hadden we een interne hackathon en het idee ontstond om te proberen het rode deel van het diagram te vervangen door iets dat statistieken in ClickHouse kon opslaan. We hebben logbestanden op ClickHouse - waarom probeert u het niet?

Veelgestelde vragen over architectuur en werk van VKontakte

We hadden een plan waarbij logs via KittenHouse werden geschreven.

Veelgestelde vragen over architectuur en werk van VKontakte

We besloten voeg nog een “*Huis” toe aan het diagram, die precies de statistieken ontvangt in het formaat zoals onze code ze via UDP schrijft. Vervolgens verandert dit *House ze in bijlagen, zoals boomstammen, die KittenHouse begrijpt. Deze logs kan hij perfect aanleveren bij ClickHouse, die ze moet kunnen lezen.

Veelgestelde vragen over architectuur en werk van VKontakte

Het schema met memcache, stats-daemon en logs-collectors database is vervangen door dit schema.

Veelgestelde vragen over architectuur en werk van VKontakte

Het schema met memcache, stats-daemon en logs-collectors database is vervangen door dit schema.

  • Er is hier een verzending van code, die lokaal in StatsHouse is geschreven.
  • StatsHouse schrijft UDP-statistieken, die al zijn omgezet in SQL-inserts, in batches naar KittenHouse.
  • KittenHouse stuurt ze naar ClickHouse.
  • Als we ze willen lezen, dan lezen we ze zonder StatsHouse - rechtstreeks vanuit ClickHouse met behulp van gewone SQL.

Is het nog steeds? experiment, maar we vinden het leuk hoe het afloopt. Als we de problemen met het schema oplossen, gaan we er misschien helemaal naar over. Persoonlijk hoop ik het.

Het schema bespaart geen ijzer. Er zijn minder servers nodig, lokale stats-daemons en logs-collectors zijn niet nodig, maar ClickHouse vereist een grotere server dan die in het huidige schema. Er zijn minder servers nodig, maar ze moeten duurder en krachtiger zijn.

Aanwenden

Laten we eerst eens kijken naar de PHP-implementatie. Wij ontwikkelen in git: gebruik GitLab и TeamCity voor inzet. Ontwikkelingstakken worden samengevoegd in de mastertak, van de master voor testen worden ze samengevoegd in staging en van staging naar productie.

Vóór de implementatie worden de huidige productietak en de vorige genomen en worden diff-bestanden daarin beschouwd - wijzigingen: gemaakt, verwijderd, gewijzigd. Deze wijziging wordt vastgelegd in de binlog van een speciale copyfast-engine, die wijzigingen snel kan repliceren naar ons gehele serverpark. Wat hier wordt gebruikt, is niet rechtstreeks kopiëren, maar replicatie van roddels, wanneer een server wijzigingen naar zijn dichtstbijzijnde buren verzendt, die naar hun buren, enzovoort. Hierdoor kunt u de code in tientallen en eenheden van seconden bijwerken voor het hele wagenpark. Wanneer de wijziging de lokale replica bereikt, worden deze patches op de lokale replica toegepast lokaal bestandssysteem. Het terugdraaien wordt ook volgens hetzelfde schema uitgevoerd.

Wij zetten kPHP ook veel in en het heeft ook een eigen ontwikkeling git volgens het bovenstaande diagram. Sinds dit HTTP-server binair, dan kunnen we geen diff produceren - het release-binaire bestand weegt honderden MB. Daarom is er hier nog een andere optie: er wordt naar de versie geschreven binlog copyfast. Bij elke build neemt het toe, en tijdens het terugdraaien neemt het ook toe. Versie gerepliceerd naar servers. Lokale copyfasts zien dat er een nieuwe versie in de binlog is gekomen, en door dezelfde roddelreplicatie nemen ze de nieuwste versie van het binaire bestand voor zichzelf, zonder onze hoofdserver te vermoeien, maar de belasting zorgvuldig over het netwerk te verdelen. Wat volgt sierlijke doorstart voor de nieuwe versie.

Voor onze motoren, die ook in wezen binair zijn, is het schema zeer vergelijkbaar:

  • git master branch;
  • binair in . Deb;
  • de versie is geschreven naar binlog copyfast;
  • gerepliceerd naar servers;
  • de server haalt een nieuwe .dep op;
  • dpkg -i;
  • sierlijke herlancering naar nieuwe versie.

Het verschil is dat ons binaire bestand is verpakt in archieven . Deb, en bij het wegpompen ervan dpkg -i worden op het systeem geplaatst. Waarom wordt kPHP ingezet als binair bestand, en worden motoren ingezet als dpkg? Het gebeurde op die manier. Het werkt - raak het niet aan.

Nuttige links:

Alexey Akulovich is een van degenen die, als onderdeel van de Programmacommissie, helpt PHP Rusland op 17 mei wordt het grootste evenement voor PHP-ontwikkelaars van de afgelopen tijd. Kijk eens wat een coole pc we hebben, wat luidsprekers (twee van hen ontwikkelen PHP-core!) - lijkt iets dat je niet mag missen als je PHP schrijft.

Bron: www.habr.com

Voeg een reactie