Vanlige spørsmål om arkitektur og arbeid med VKontakte

Historien om opprettelsen av VKontakte er på Wikipedia; den ble fortalt av Pavel selv. Det virker som om alle kjenner henne allerede. Om det indre, arkitekturen og strukturen til nettstedet på HighLoad++ Pavel fortalte meg tilbake i 2010. Mange servere har lekket siden den gang, så vi vil oppdatere informasjonen: vi vil dissekere den, ta ut innsiden, veie den og se på VK-enheten fra et teknisk synspunkt.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Alexey Akulovich (AterCattus) backend-utvikler i VKontakte-teamet. Transkripsjonen av denne rapporten er et samlet svar på ofte stilte spørsmål om driften av plattformen, infrastruktur, servere og interaksjon mellom dem, men ikke om utvikling, nemlig om jern. Hver for seg om databaser og hva VK har i stedet, om å samle logger og overvåke hele prosjektet som helhet. Detaljer under kuttet.



I mer enn fire år har jeg jobbet med alle slags oppgaver knyttet til backend.

  • Laste opp, lagre, behandle, distribuere media: video, live streaming, lyd, bilder, dokumenter.
  • Infrastruktur, plattform, utviklerovervåking, logger, regionale cacher, CDN, proprietær RPC-protokoll.
  • Integrasjon med eksterne tjenester: push-varsler, ekstern lenkeparsing, RSS-feed.
  • Hjelpe kolleger med ulike spørsmål, svarene på disse krever å dykke inn i ukjent kode.

I løpet av denne tiden hadde jeg en hånd med på mange komponenter på nettstedet. Jeg vil dele denne opplevelsen.

Generell arkitektur

Alt, som vanlig, starter med en server eller gruppe servere som godtar forespørsler.

Front server

Frontserveren aksepterer forespørsler via HTTPS, RTMP og WSS.

HTTPS - dette er forespørsler om hoved- og mobilnettversjonene av nettstedet: vk.com og m.vk.com, og andre offisielle og uoffisielle klienter til API-en vår: mobilklienter, messengers. Vi har resepsjon RTMP-trafikk for Live-sendinger med separate frontservere og WSS- tilkoblinger for Streaming API.

For HTTPS og WSS på servere er det verdt nginx. For RTMP-sendinger har vi nylig byttet til vår egen løsning hadde, men det er utenfor rammen av rapporten. For feiltoleranse annonserer disse serverne vanlige IP-adresser og fungerer i grupper slik at hvis det er et problem på en av serverne, går ikke brukerforespørsler tapt. For HTTPS og WSS krypterer de samme serverne trafikk for å ta en del av CPU-belastningen på seg selv.

Vi skal ikke snakke videre om WSS og RTMP, men kun om standard HTTPS-forespørsler, som vanligvis er knyttet til et webprosjekt.

Backend

Bak fronten er det vanligvis backend-servere. De behandler forespørsler som frontserveren mottar fra klienter.

Den kPHP-servere, som HTTP-daemonen kjører på, fordi HTTPS allerede er dekryptert. kPHP er en server som kjører på forgaffelmodeller: starter en masterprosess, en haug med barneprosesser, sender lyttekontakter til dem og de behandler forespørslene sine. I dette tilfellet startes ikke prosessene på nytt mellom hver forespørsel fra brukeren, men tilbakestiller ganske enkelt tilstanden til den opprinnelige nullverditilstanden - forespørsel etter forespørsel, i stedet for å starte på nytt.

Lastfordeling

Alle våre backends er ikke et stort utvalg maskiner som kan behandle enhver forespørsel. Vi dem delt inn i separate grupper: generelt, mobil, api, video, iscenesettelse... Problemet på en egen gruppe maskiner vil ikke påvirke alle andre. Ved problemer med video vil brukeren som hører på musikk ikke en gang vite om problemene. Hvilken backend å sende forespørselen til bestemmes av nginx på forsiden i henhold til konfigurasjonen.

Metrisk innsamling og rebalansering

For å forstå hvor mange biler vi må ha i hver gruppe, må vi ikke stol på QPS. Backends er forskjellige, de har forskjellige forespørsler, hver forespørsel har en annen kompleksitet for å beregne QPS. Det er derfor vi vi opererer med konseptet belastning på serveren som helhet - på CPU og perf.

Vi har tusenvis av slike servere. Hver fysisk server kjører en kPHP-gruppe for å resirkulere alle kjernene (fordi kPHP er enkelttråd).

Innholdsserver

CS eller Content Server er en lagring. CS er en server som lagrer filer og også behandler opplastede filer og alle slags synkrone oppgaver i bakgrunnen som hovednettgrensesnittet tildeler den.

Vi har titusenvis av fysiske servere som lagrer filer. Brukere elsker å laste opp filer, og vi elsker å lagre og dele dem. Noen av disse serverne er stengt av spesielle pu/pp-servere.

pu/pp

Hvis du åpnet nettverksfanen i VK, så du pu/pp.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Hva er pu/pp? Hvis vi lukker den ene serveren etter den andre, er det to alternativer for å laste opp og laste ned en fil til serveren som ble stengt: direkte gjennom http://cs100500.userapi.com/path eller via mellomserver - http://pu.vk.com/c100500/path.

Pu er det historiske navnet for bildeopplasting, og pp er bildeproxy. Det vil si at én server er for opplasting av bilder, og en annen er for opplasting. Nå er ikke bare bilder lastet inn, men navnet er bevart.

Disse serverne avslutte HTTPS-økterfor å fjerne prosessorbelastningen fra lagringen. Siden brukerfiler behandles på disse serverne, jo mindre sensitiv informasjon lagret på disse maskinene, jo bedre. For eksempel HTTPS-krypteringsnøkler.

Siden maskinene er stengt av våre andre maskiner, har vi råd til å ikke gi dem «hvite» eksterne IP-er, og gi "grå". På denne måten har vi spart på IP-poolen og garantert beskyttet maskinene mot tilgang utenfor - det er rett og slett ingen IP å komme inn i den.

Motstandsdyktighet over delte IP-er. Når det gjelder feiltoleranse fungerer ordningen likt – flere fysiske servere har en felles fysisk IP, og maskinvaren foran velger hvor forespørselen skal sendes. Jeg skal snakke om andre alternativer senere.

Det kontroversielle poenget er at i dette tilfellet klienten beholder færre forbindelser. Hvis det er samme IP for flere maskiner - med samme vert: pu.vk.com eller pp.vk.com, har klientnettleseren en grense på antall samtidige forespørsler til én vert. Men i tiden med allestedsnærværende HTTP/2, tror jeg at dette ikke lenger er så relevant.

Den åpenbare ulempen med ordningen er at den må pumpe all trafikk, som går til lagringen, gjennom en annen server. Siden vi pumper trafikk gjennom maskiner, kan vi ennå ikke pumpe tung trafikk, for eksempel video, ved hjelp av samme ordning. Vi overfører det direkte - en separat direkte tilkobling for separate lagringer spesielt for video. Vi overfører lettere innhold gjennom en proxy.

For ikke lenge siden fikk vi en forbedret versjon av proxy. Nå skal jeg fortelle deg hvordan de skiller seg fra vanlige og hvorfor dette er nødvendig.

Sol

I september 2017 kjøpte Oracle, som tidligere hadde kjøpt Sun, sparket et stort antall Sun-ansatte. Vi kan si at selskapet i dette øyeblikket opphørte å eksistere. Da de valgte et navn for det nye systemet, bestemte administratorene våre seg for å hylle minnet om dette selskapet og kalte det nye systemet Sun. Blant oss kaller vi henne ganske enkelt "soler".

Vanlige spørsmål om arkitektur og arbeid med VKontakte

pp hadde noen problemer. Én IP per gruppe - ineffektiv cache. Flere fysiske servere deler en felles IP-adresse, og det er ingen måte å kontrollere hvilken server forespørselen går til. Derfor, hvis forskjellige brukere kommer for den samme filen, så hvis det er en cache på disse serverne, havner filen i cachen til hver server. Dette er en veldig ineffektiv ordning, men ingenting kunne gjøres.

Følgelig - vi kan ikke sønderdele innhold, fordi vi ikke kan velge en spesifikk server for denne gruppen - de har en felles IP. Også av noen interne årsaker vi har det var ikke mulig å installere slike servere i regioner. De sto bare i St. Petersburg.

Med solen endret vi utvalgssystemet. Nå har vi anycast-ruting: dynamisk ruting, anycast, selvsjekk-demon. Hver server har sin egen individuelle IP, men et felles subnett. Alt er konfigurert på en slik måte at hvis en server svikter, blir trafikken automatisk spredt over de andre serverne i samme gruppe. Nå er det mulig å velge en bestemt server, ingen redundant caching, og påliteligheten ble ikke påvirket.

Vektstøtte. Nå har vi råd til å installere maskiner med forskjellig kraft etter behov, og også, i tilfelle midlertidige problemer, endre vektene til de arbeidende "solene" for å redusere belastningen på dem, slik at de "hviler" og begynner å jobbe igjen.

Deling etter innholds-ID. En morsom ting med sharding: vi sønderdeler vanligvis innhold slik at forskjellige brukere går til den samme filen gjennom den samme "solen", slik at de har en felles cache.

Vi lanserte nylig "Clover"-applikasjonen. Dette er en nettquiz i en direktesending, der verten stiller spørsmål og brukerne svarer i sanntid, og velger alternativer. Appen har en chat hvor brukere kan chatte. Kan koble til sendingen samtidig mer enn 100 tusen mennesker. De skriver alle meldinger som sendes til alle deltakerne, og en avatar følger med meldingen. Hvis 100 tusen mennesker kommer for en avatar i en "sol", kan den noen ganger rulle bak en sky.

For å motstå serieutbrudd av forespørsler om den samme filen, er det for en viss type innhold vi slår på et dumt opplegg som sprer filer over alle tilgjengelige «soler» i regionen.

Sol fra innsiden

Omvendt proxy på nginx, cache enten i RAM eller på raske Optane/NVMe-disker. Eksempel: http://sun4-2.userapi.com/c100500/path — en lenke til "solen", som ligger i den fjerde regionen, den andre servergruppen. Den lukker banefilen, som fysisk ligger på server 100500.

Cache

Vi legger til en node til i vårt arkitektoniske opplegg - caching-miljøet.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Nedenfor er layoutdiagrammet regionale cacher, det er rundt 20 av dem. Dette er stedene hvor cacher og "soler" befinner seg, som kan cache trafikk gjennom seg selv.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Dette er caching av multimedieinnhold; ingen brukerdata lagres her - bare musikk, video, bilder.

For å bestemme brukerens region, må vi vi samler inn BGP-nettverksprefikser annonsert i regionene. I tilfelle av fallback, må vi også analysere geoip-databasen hvis vi ikke kunne finne IP-en med prefikser. Vi bestemmer regionen ut fra brukerens IP. I koden kan vi se på en eller flere regioner av brukeren - de punktene han er nærmest geografisk.

Hvordan virker det?

Vi teller populariteten til filer etter region. Det er en rekke av den regionale cachen der brukeren befinner seg, og en filidentifikator - vi tar dette paret og øker vurderingen med hver nedlasting.

Samtidig kommer demoner - tjenester i regioner - fra tid til annen til APIen og sier: "Jeg er en slik og slik cache, gi meg en liste over de mest populære filene i regionen min som ikke er på meg ennå. ” API-en leverer en haug med filer sortert etter vurdering, demonen laster dem ned, tar dem til regionene og leverer filene derfra. Dette er den grunnleggende forskjellen mellom pu/pp og Sun fra cacher: de gir filen gjennom seg selv umiddelbart, selv om denne filen ikke er i cachen, og cachen laster først ned filen til seg selv, og begynner deretter å gi den tilbake.

I dette tilfellet får vi innhold nærmere brukerne og spre nettverksbelastningen. For eksempel, bare fra Moskva-cachen distribuerer vi mer enn 1 Tbit/s i rushtiden.

Men det er problemer - cache-servere er ikke gummi. For superpopulært innhold er det noen ganger ikke nok nettverk for en separat server. Våre cache-servere er på 40-50 Gbit/s, men det er innhold som tetter en slik kanal fullstendig. Vi går mot å implementere lagring av mer enn én kopi av populære filer i regionen. Jeg håper at vi vil implementere det innen utgangen av året.

Vi så på den generelle arkitekturen.

  • Frontservere som godtar forespørsler.
  • Backends som behandler forespørsler.
  • Lagre som er lukket av to typer fullmakter.
  • Regionale cacher.

Hva mangler i dette diagrammet? Selvfølgelig databasene der vi lagrer data.

Databaser eller motorer

Vi kaller dem ikke databaser, men motorer - motorer, fordi vi praktisk talt ikke har databaser i allment akseptert forstand.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Dette er et nødvendig tiltak. Dette skjedde fordi i 2008-2009, da VK hadde en eksplosiv vekst i popularitet, fungerte prosjektet utelukkende på MySQL og Memcache, og det var problemer. MySQL elsket å krasje og korrupte filer, hvoretter den ikke ville gjenopprette seg, og Memcache ble gradvis redusert i ytelse og måtte startes på nytt.

Det viser seg at det stadig mer populære prosjektet hadde vedvarende lagring, som ødelegger data, og en cache, som bremser ned. Under slike forhold er det vanskelig å utvikle et prosjekt i vekst. Det ble besluttet å prøve å omskrive de kritiske tingene som prosjektet var fokusert på på våre egne sykler.

Løsningen var vellykket. Det var en mulighet til å gjøre dette, så vel som en ekstrem nødvendighet, fordi andre måter å skalere ikke fantes på den tiden. Det var ikke en haug med databaser, NoSQL eksisterte ikke ennå, det var bare MySQL, Memcache, PostrgreSQL - og det er det.

Universell drift. Utviklingen ble ledet av vårt team av C-utviklere og alt ble gjort på en konsistent måte. Uavhengig av motor, hadde de alle omtrent samme filformat skrevet til disk, samme lanseringsparametere, behandlet signaler på samme måte, og oppførte seg omtrent likt i tilfelle kantsituasjoner og problemer. Med veksten av motorer er det praktisk for administratorer å betjene systemet - det er ingen dyrehage som må vedlikeholdes, og de må lære på nytt hvordan de skal betjene hver nye tredjepartsdatabase, noe som gjorde det mulig å øke raskt og enkelt. nummeret deres.

Typer motorer

Teamet skrev ganske mange motorer. Her er bare noen av dem: venn, hint, bilde, ipdb, bokstaver, lister, logger, memcached, meowdb, nyheter, nostradamus, foto, spillelister, pmemcached, sandkasse, søk, lagring, liker, oppgaver, …

For hver oppgave som krever en spesifikk datastruktur eller behandler atypiske forespørsler, skriver C-teamet en ny motor. Hvorfor ikke.

Vi har en egen motor memcached, som ligner på en vanlig, men med en haug med godsaker, og som ikke bremser. Ikke ClickHouse, men det fungerer også. Tilgjengelig separat pmembufret - Er vedvarende memcached, som også kan lagre data på disk, dessuten passer det inn i RAM, for ikke å miste data ved omstart. Det finnes ulike motorer for individuelle oppgaver: køer, lister, sett - alt som prosjektet vårt krever.

Klynger

Fra et kodeperspektiv er det ikke nødvendig å tenke på motorer eller databaser som prosesser, enheter eller instanser. Koden fungerer spesifikt med klynger, med grupper av motorer - én type per klynge. La oss si at det er en minnebuffret klynge - det er bare en gruppe maskiner.

Koden trenger ikke å vite den fysiske plasseringen, størrelsen eller antallet servere i det hele tatt. Han går til klyngen ved hjelp av en bestemt identifikator.

For at dette skal fungere, må du legge til en enhet til som er plassert mellom koden og motorene - proxy.

RPC-proxy

Fullmakt koble buss, som nesten hele nettstedet kjører på. Samtidig har vi ingen tjenestefunn - i stedet er det en konfigurasjon for denne proxyen, som kjenner plasseringen til alle klynger og alle shards av denne klyngen. Dette er hva administratorer gjør.

Programmerere bryr seg ikke i det hele tatt hvor mye, hvor og hva det koster - de går bare til klyngen. Dette tillater oss mye. Når du mottar en forespørsel, omdirigerer proxyen forespørselen, vel vitende om hvor - den bestemmer dette selv.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

I dette tilfellet er proxy et beskyttelsespunkt mot tjenestefeil. Hvis en motor bremser ned eller krasjer, forstår proxyen dette og svarer deretter til klientsiden. Dette lar deg fjerne tidsavbruddet - koden venter ikke på at motoren skal svare, men forstår at den ikke fungerer og må oppføre seg annerledes. Koden må være forberedt på at databasene ikke alltid fungerer.

Spesifikke implementeringer

Noen ganger ønsker vi fortsatt å ha en slags ikke-standard løsning som motor. Samtidig ble det besluttet å ikke bruke vår ferdiglagde rpc-proxy, laget spesielt for våre motorer, men å lage en egen proxy for oppgaven.

For MySQL, som vi fortsatt har her og der, bruker vi db-proxy, og for ClickHouse - Kattungehus.

Det fungerer generelt slik. Det er en viss server, den kjører kPHP, Go, Python - generelt sett hvilken som helst kode som kan bruke RPC-protokollen vår. Koden kjører lokalt på en RPC-proxy - hver server hvor koden er plassert kjører sin egen lokale proxy. På forespørsel forstår fullmektigen hvor han skal gå.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Hvis en motor vil gå til en annen, selv om det er en nabo, går den gjennom en proxy, fordi naboen kan være i et annet datasenter. Motoren skal ikke stole på å vite plasseringen til noe annet enn seg selv – dette er vår standardløsning. Men det finnes selvfølgelig unntak :)

Et eksempel på en TL-ordning som alle motorer fungerer etter.

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;

Dette er en binær protokoll, den nærmeste analogen er protobuf. Skjemaet beskriver valgfrie felt, komplekse typer - utvidelser av innebygde skalarer og spørringer. Alt fungerer i henhold til denne protokollen.

RPC over TL over TCP/UDP... UDP?

Vi har en RPC-protokoll for å utføre motorforespørsler som kjører på toppen av TL-skjemaet. Alt dette fungerer over en TCP/UDP-tilkobling. TCP er forståelig, men hvorfor trenger vi UDP ofte?

UDP hjelper unngå problemet med et stort antall tilkoblinger mellom servere. Hvis hver server har en RPC-proxy, og den generelt kan gå til en hvilken som helst motor, er det titusenvis av TCP-tilkoblinger per server. Det er en belastning, men den er ubrukelig. I tilfelle av UDP eksisterer ikke dette problemet.

Ingen overflødig TCP-håndtrykk. Dette er et typisk problem: når en ny motor eller en ny server startes, etableres mange TCP-tilkoblinger samtidig. For små lette forespørsler, for eksempel UDP-nyttelast, er all kommunikasjon mellom koden og motoren to UDP-pakker: den ene flyr i en retning, den andre i den andre. En rundtur – og koden fikk svar fra motoren uten et håndtrykk.

Ja, alt bare fungerer med en svært liten prosentandel av pakketap. Protokollen har støtte for retransmits og timeouts, men taper vi mye vil vi få tilnærmet TCP, noe som ikke er lønnsomt. Vi kjører ikke UDP over hav.

Vi har tusenvis av slike servere, og opplegget er det samme: en pakke med motorer er installert på hver fysisk server. De er for det meste entrådede for å kjøre så raskt som mulig uten å blokkere, og er sønderdelt som entrådede løsninger. Samtidig har vi ikke noe mer pålitelig enn disse motorene, og det legges mye vekt på vedvarende datalagring.

Vedvarende datalagring

Motorer skriver binlogs. En binlog er en fil på slutten som en hendelse for en endring i tilstand eller data legges til. I forskjellige løsninger kalles det annerledes: binær logg, WAL, AOF, men prinsippet er det samme.

For å hindre at motoren leser hele binloggen på nytt i mange år ved omstart, skriver motorene øyeblikksbilder - nåværende tilstand. Om nødvendig leser de fra den først, og leser deretter ferdig fra binloggen. Alle binlogs er skrevet i samme binære format - i henhold til TL-skjemaet, slik at administratorer kan administrere dem likt med verktøyene sine. Det er ikke noe slikt behov for øyeblikksbilder. Det er en generell overskrift som indikerer hvem sitt øyeblikksbilde er int, magien til motoren, og hvilken kropp som ikke er viktig for noen. Dette er et problem med motoren som registrerte øyeblikksbildet.

Jeg vil raskt beskrive operasjonsprinsippet. Det er en server som motoren kjører på. Han åpner en ny tom binlogg for skriving og skriver en hendelse for endring av den.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

På et tidspunkt bestemmer han seg enten for å ta et øyeblikksbilde selv, eller så mottar han et signal. Serveren oppretter en ny fil, skriver hele tilstanden inn i den, legger til gjeldende binlogstørrelse - offset - til slutten av filen, og fortsetter å skrive videre. En ny binlogg opprettes ikke.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

På et tidspunkt, når motoren starter på nytt, vil det være både en binlog og et øyeblikksbilde på disken. Motoren leser hele øyeblikksbildet og hever tilstanden på et visst tidspunkt.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Leser posisjonen som var på tidspunktet øyeblikksbildet ble opprettet og størrelsen på binloggen.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Leser slutten av binloggen for å få gjeldende status og fortsetter å skrive ytterligere hendelser. Dette er et enkelt opplegg; alle våre motorer fungerer i henhold til det.

Data replikering

Som et resultat, datareplikering i vår uttalelsesbasert — vi skriver i binlog ikke noen sideendringer, men nemlig endringsforespørsler. Svært lik det som kommer over nettverket, bare litt modifisert.

Det samme opplegget brukes ikke bare for replikering, men også å lage sikkerhetskopier. Vi har en motor - en skrivemester som skriver til binloggen. På et hvilket som helst annet sted der administratorene konfigurerer det, blir denne binloggen kopiert, og det er det - vi har en sikkerhetskopi.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Hvis nødvendig lese replikaFor å redusere CPU-lesebelastningen, startes lesemotoren ganske enkelt, som leser slutten av binloggen og utfører disse kommandoene lokalt.

Etterslepet her er veldig lite, og det er mulig å finne ut hvor mye kopien henger etter mesteren.

Datadeling i RPC-proxy

Hvordan fungerer skjæring? Hvordan forstår proxyen hvilken cluster shard som skal sendes til? Koden sier ikke: "Send for 15 shards!" - nei, dette gjøres av fullmektig.

Den enkleste ordningen er firstint — det første tallet i forespørselen.

get(photo100_500) => 100 % N.

Dette er et eksempel på en enkel memcached tekstprotokoll, men spørringer kan selvfølgelig være komplekse og strukturerte. Eksemplet tar det første tallet i spørringen og resten når de er delt på klyngestørrelsen.

Dette er nyttig når vi ønsker å ha datalokalitet for en enkelt enhet. La oss si at 100 er en bruker- eller gruppe-ID, og ​​vi vil at alle dataene til én enhet skal være på ett shard for komplekse søk.

Hvis vi ikke bryr oss om hvordan forespørsler er spredt over klyngen, er det et annet alternativ - hash hele skjæret.

hash(photo100_500) => 3539886280 % N

Vi får også hashen, resten av divisjonen og shardnummeret.

Begge disse alternativene fungerer bare hvis vi er forberedt på at når vi øker størrelsen på klyngen, vil vi dele den eller øke den flere ganger. For eksempel hadde vi 16 skår, vi har ikke nok, vi vil ha flere - vi kan trygt få 32 uten nedetid. Hvis vi ønsker å øke ikke multipler, vil det være nedetid, fordi vi ikke vil kunne dele opp alt nøyaktig uten tap. Disse alternativene er nyttige, men ikke alltid.

Hvis vi trenger å legge til eller fjerne et vilkårlig antall servere, bruker vi Konsekvent hashing på ringen a la Ketama. Men samtidig mister vi fullstendig lokaliteten til dataene; vi må slå sammen forespørselen til klyngen slik at hver del returnerer sitt eget lille svar, og deretter slå sammen svarene til proxyen.

Det er superspesifikke forespørsler. Det ser slik ut: RPC-proxy mottar forespørselen, bestemmer hvilken klynge som skal gå til og bestemmer shard. Så er det enten skrivemestere, eller hvis klyngen har replikastøtte, sender den til en replika på forespørsel. Proxyen gjør alt dette.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Tømmerstokker

Vi skriver logger på flere måter. Den mest åpenbare og enkle er skrive logger til memcache.

ring-buffer: prefix.idx = line

Det er et nøkkelprefiks - navnet på loggen, en linje, og det er størrelsen på denne loggen - antall linjer. Vi tar et tilfeldig tall fra 0 til antall linjer minus 1. Nøkkelen i memcache er et prefiks sammenkoblet med dette tilfeldige tallet. Vi lagrer logglinjen og gjeldende tid til verdien.

Når det er nødvendig å lese logger, utfører vi Multi Get alle nøkler, sortert etter tid, og får dermed en produksjonslogg i sanntid. Opplegget brukes når du skal feilsøke noe i produksjon i sanntid, uten å ødelegge noe, uten å stoppe eller tillate trafikk til andre maskiner, men denne loggen varer ikke lenge.

For pålitelig lagring av tømmerstokker har vi en motor logger-motor. Det er nettopp derfor den ble opprettet og er mye brukt i et stort antall klynger. Den største klyngen jeg vet av lagrer 600 TB med pakket logger.

Motoren er veldig gammel, det er klynger som allerede er 6-7 år gamle. Det er problemer med det som vi prøver å løse, for eksempel begynte vi aktivt å bruke ClickHouse til å lagre logger.

Samle logger i ClickHouse

Dette diagrammet viser hvordan vi går inn i motorene våre.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Det er kode som går lokalt via RPC til RPC-proxyen, og den forstår hvor den skal gå til motoren. Hvis vi vil skrive logger i ClickHouse, må vi endre to deler i denne ordningen:

  • erstatte noen motor med ClickHouse;
  • erstatte RPC-proxyen, som ikke får tilgang til ClickHouse, med en løsning som kan, og via RPC.

Motoren er enkel - vi erstatter den med en server eller en klynge av servere med ClickHouse.

Og for å gå til ClickHouse, gjorde vi det KittenHouse. Hvis vi går direkte fra KittenHouse til ClickHouse, vil det ikke takle det. Selv uten forespørsler, legger det seg opp fra HTTP-tilkoblinger til et stort antall maskiner. For at ordningen skal fungere, på en server med ClickHouse lokal omvendt proxy heves, som er skrevet på en slik måte at den tåler de nødvendige volumene av tilkoblinger. Den kan også bufre data i seg selv relativt pålitelig.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Noen ganger ønsker vi ikke å implementere RPC-ordningen i ikke-standardiserte løsninger, for eksempel i nginx. Derfor har KittenHouse muligheten til å motta logger via UDP.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Hvis avsender og mottaker av loggene fungerer på samme maskin, er sannsynligheten for å miste en UDP-pakke i den lokale verten ganske lav. Som et kompromiss mellom behovet for å implementere RPC i en tredjepartsløsning og pålitelighet, bruker vi ganske enkelt UDP-sending. Vi kommer tilbake til denne ordningen senere.

overvåking

Vi har to typer logger: de som samles inn av administratorer på deres servere og de som er skrevet av utviklere fra kode. De tilsvarer to typer beregninger: system og produkt.

Systemmålinger

Det fungerer på alle våre servere Nettdata, som samler inn statistikk og sender den til Grafitt karbon. Derfor brukes ClickHouse som et oppbevaringssystem, og ikke Whisper, for eksempel. Om nødvendig kan du lese direkte fra ClickHouse, eller bruke grafana for beregninger, grafer og rapporter. Som utviklere har vi nok tilgang til Netdata og Grafana.

Produktberegninger

For enkelhets skyld har vi skrevet mange ting. For eksempel er det et sett med vanlige funksjoner som lar deg skrive Counts, UniqueCounts-verdier inn i statistikk, som sendes et sted videre.

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

$stats = statlogsStatData($params)

Deretter kan vi bruke sorterings- og grupperingsfiltre og gjøre alt vi vil av statistikk – bygge grafer, konfigurere Watchdogs.

Vi skriver veldig mange beregninger antall hendelser er fra 600 milliarder til 1 billion per dag. Vi ønsker imidlertid å beholde dem minst et par årfor å forstå trender i beregninger. Å sette alt sammen er et stort problem som vi ikke har løst ennå. Jeg skal fortelle deg hvordan det har fungert de siste årene.

Vi har funksjoner som skriver disse beregningene til lokal memcachefor å redusere antall oppføringer. En gang i løpet av kort tid lokalt lansert statistikk-daemon samler alle poster. Deretter slår demonen sammen beregningene til to lag med servere tømmer-samlere, som samler statistikk fra en haug av maskinene våre slik at laget bak dem ikke dør.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Om nødvendig kan vi skrive direkte til logger-samlere.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Men å skrive fra kode direkte til samlere, utenom stas-daemom, er en dårlig skalerbar løsning fordi det øker belastningen på samleren. Løsningen er egnet bare hvis vi av en eller annen grunn ikke kan øke memcache stats-daemonen på maskinen, eller den krasjet og vi gikk direkte.

Deretter slår logger-samlere sammen statistikk til mjauDB - dette er vår database, som også kan lagre beregninger.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Deretter kan vi gjøre binære "near-SQL"-valg fra koden.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Eksperiment

Sommeren 2018 hadde vi et internt hackathon, og ideen kom opp om å prøve å erstatte den røde delen av diagrammet med noe som kunne lagre metrikk i ClickHouse. Vi har logger på ClickHouse - hvorfor ikke prøve det?

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Vi hadde et opplegg som skrev logger gjennom KittenHouse.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Vi bestemte legg til et annet "*hus" til diagrammet, som vil motta nøyaktig beregningene i formatet slik koden vår skriver dem via UDP. Så gjør dette *Huset dem til innlegg, som tømmerstokker, som KittenHouse forstår. Han kan perfekt levere disse loggene til ClickHouse, som skal kunne lese dem.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Opplegget med memcache, stats-daemon og logs-collectors database er erstattet med denne.

Vanlige spørsmål om arkitektur og arbeid med VKontakte

Opplegget med memcache, stats-daemon og logs-collectors database er erstattet med denne.

  • Det er en utsendelse fra kode her, som er skrevet lokalt i StatsHouse.
  • StatsHouse skriver UDP-beregninger, allerede konvertert til SQL-innlegg, til KittenHouse i batcher.
  • KittenHouse sender dem til ClickHouse.
  • Hvis vi vil lese dem, så leser vi dem forbi StatsHouse - direkte fra ClickHouse ved hjelp av vanlig SQL.

Er det fortsatt eksperiment, men vi liker hvordan det blir. Hvis vi fikser problemene med ordningen, så går vi kanskje helt over til den. Personlig håper jeg det.

Ordningen sparer ikke jern. Færre servere er nødvendig, lokale statistikk-demoner og logger-samlere er ikke nødvendig, men ClickHouse krever en større server enn de i gjeldende opplegg. Færre servere er nødvendig, men de må være dyrere og kraftigere.

Utplassere

La oss først se på PHP-distribusjonen. Vi utvikler oss i git: bruk GitLab и TeamCity for utplassering. Utviklingsgrener slås sammen til mastergrenen, fra masteren for testing slås de sammen til iscenesettelse, og fra iscenesettelse til produksjon.

Før distribusjon tas den nåværende produksjonsgrenen og den forrige, og diff-filer vurderes i dem - endringer: opprettet, slettet, endret. Denne endringen registreres i binloggen til en spesiell copyfast-motor, som raskt kan replikere endringer til hele serverflåten vår. Det som brukes her er ikke kopiering direkte, men replikering av sladder, når en server sender endringer til sine nærmeste naboer, de til sine naboer, og så videre. Dette lar deg oppdatere koden på flere titalls og enheter av sekunder over hele flåten. Når endringen når den lokale kopien, bruker den disse oppdateringene på sin lokalt filsystem. Tilbakerulling utføres også etter samme ordning.

Vi distribuerer også kPHP mye, og det har også sin egen utvikling på git i henhold til diagrammet ovenfor. Siden dette HTTP-server binær, da kan vi ikke produsere diff - utgivelsesbinæren veier hundrevis av MB. Derfor er det et annet alternativ her - versjonen er skrevet til binlog copyfast. For hvert bygg øker det, og under tilbakerulling øker det også. Versjon replikert til servere. Lokale copyfasts ser at en ny versjon har kommet inn i binloggen, og ved samme sladderreplikering tar de siste versjon av binæren for seg selv, uten å slite vår masterserver, men forsiktig spre belastningen over nettverket. Det som følger grasiøs relansering for den nye versjonen.

For motorene våre, som også i hovedsak er binære filer, er ordningen veldig lik:

  • git master gren;
  • binær i . Deb;
  • versjonen er skrevet til binlog copyfast;
  • replikert til servere;
  • serveren trekker ut en ny .dep;
  • dpkg -i;
  • grasiøs relansering til ny versjon.

Forskjellen er at vår binære er pakket i arkiver . Deb, og når de pumper ut de dpkg -i er plassert på systemet. Hvorfor er kPHP distribuert som en binær, og motorer er distribuert som dpkg? Det ble sånn. Det fungerer – ikke rør det.

Nyttige lenker:

Alexey Akulovich er en av dem som, som en del av programkomiteen, hjelper til PHP Russland 17. mai vil bli den største begivenheten for PHP-utviklere i nyere tid. Se for en kul PC vi har, hva høyttalere (to av dem utvikler PHP-kjerne!) - virker som noe du ikke kan gå glipp av hvis du skriver PHP.

Kilde: www.habr.com

Legg til en kommentar