FAQ mbi arkitekturën dhe punën e VKontakte

Historia e krijimit të VKontakte është në Wikipedia; ajo u tha nga vetë Pavel. Duket se tashmë të gjithë e njohin. Rreth brendësisë, arkitekturës dhe strukturës së faqes në HighLoad++ Pavel më tha në vitin 2010. Shumë serverë kanë rrjedhur që atëherë, kështu që ne do të përditësojmë informacionin: do ta zbërthejmë, do të nxjerrim të brendshmet, do ta peshojmë dhe do ta shikojmë pajisjen VK nga pikëpamja teknike.

FAQ mbi arkitekturën dhe punën e VKontakte

Alexey Akulovich (AterCattus) zhvilluesi i backend-it në ekipin VKontakte. Transkripti i këtij raporti është një përgjigje kolektive për pyetjet e bëra shpesh në lidhje me funksionimin e platformës, infrastrukturës, serverëve dhe ndërveprimit ndërmjet tyre, por jo për zhvillimin, përkatësisht rreth hekurit. Më vete, në lidhje me bazat e të dhënave dhe atë që VK ka në vend të kësaj, për mbledhjen e regjistrave dhe monitorimin e të gjithë projektit në tërësi. Detajet nën prerje.



Për më shumë se katër vjet jam marrë me të gjitha llojet e detyrave që lidhen me backend-in.

  • Ngarkimi, ruajtja, përpunimi, shpërndarja e mediave: video, transmetimi i drejtpërdrejtë, audio, foto, dokumente.
  • Infrastruktura, platforma, monitorimi i zhvilluesve, regjistrat, cache rajonale, CDN, protokolli i pronarit RPC.
  • Integrimi me shërbimet e jashtme: njoftimet shtytëse, analizimi i lidhjeve të jashtme, furnizimi RSS.
  • Ndihma e kolegëve me pyetje të ndryshme, përgjigjet e të cilave kërkojnë zhytje në kod të panjohur.

Gjatë kësaj kohe, unë kisha një dorë në shumë komponentë të faqes. Unë dua të ndaj këtë përvojë.

Arkitektura e përgjithshme

Gjithçka, si zakonisht, fillon me një server ose grup serverësh që pranojnë kërkesa.

Serveri i përparmë

Serveri i përparmë pranon kërkesa nëpërmjet HTTPS, RTMP dhe WSS.

HTTPS - këto janë kërkesa për versionet kryesore dhe celulare të faqes në internet: vk.com dhe m.vk.com, dhe klientë të tjerë zyrtarë dhe jozyrtarë të API-së sonë: klientët celularë, lajmëtarët. Ne kemi një pritje RTMP-trafiku për transmetime të drejtpërdrejta me serverë të veçantë të përparmë dhe UK- lidhjet për API të transmetimit.

Për HTTPS dhe WSS në serverë ia vlen nginx. Për transmetimet RTMP, kohët e fundit kaluam në zgjidhjen tonë kive, por është përtej qëllimit të raportit. Për tolerancën e gabimeve, këta serverë reklamojnë adresa IP të zakonshme dhe veprojnë në grup, në mënyrë që nëse ka një problem në një nga serverët, kërkesat e përdoruesve të mos humbasin. Për HTTPS dhe WSS, të njëjtët serverë kodojnë trafikun në mënyrë që të marrin një pjesë të ngarkesës së CPU mbi vete.

Nuk do të flasim më tej për WSS dhe RTMP, por vetëm për kërkesat standarde HTTPS, të cilat zakonisht shoqërohen me një projekt ueb.

backend

Prapa pjesës së përparme zakonisht ka serverë backend. Ata përpunojnë kërkesat që serveri i përparmë merr nga klientët.

Ajo serverët kPHP, në të cilin po funksionon demon HTTP, sepse HTTPS tashmë është deshifruar. kPHP është një server që funksionon modele prefork: fillon një proces master, një sërë procesesh fëmijësh, u kalon priza dëgjimi dhe ata përpunojnë kërkesat e tyre. Në këtë rast, proceset nuk rifillojnë ndërmjet çdo kërkese nga përdoruesi, por thjesht rivendosin gjendjen e tyre në gjendjen origjinale me vlerë zero - kërkesë pas kërkese, në vend që të rifillojnë.

Shpërndarja e ngarkesës

Të gjitha backend-et tona nuk janë një grup i madh makinerish që mund të përpunojnë çdo kërkesë. Ne ata të ndarë në grupe të veçanta: gjeneral, celular, api, video, skenë... Problemi në një grup të veçantë makinerish nuk do të prekë të gjithë të tjerët. Në rast të problemeve me video, përdoruesi që dëgjon muzikë nuk do të dijë as për problemet. Në cilin backend të dërgohet kërkesa vendoset nga nginx në pjesën e përparme sipas konfigurimit.

Mbledhja metrike dhe ribalancimi

Për të kuptuar se sa makina duhet të kemi në secilin grup, ne mos u mbështetni në QPS. Backend-et janë të ndryshme, kanë kërkesa të ndryshme, secila kërkesë ka një kompleksitet të ndryshëm të llogaritjes së QPS. Kjo është arsyeja pse ne ne operojmë me konceptin e ngarkesës në server në tërësi - në CPU dhe perf.

Ne kemi mijëra serverë të tillë. Çdo server fizik drejton një grup kPHP për të ricikluar të gjitha bërthamat (sepse kPHP është me një filetim të vetëm).

Serveri i përmbajtjes

CS ose Content Server është një ruajtje. CS është një server që ruan skedarët dhe gjithashtu përpunon skedarët e ngarkuar dhe të gjitha llojet e detyrave sinkrone në sfond që frontendi kryesor i uebit i cakton atij.

Ne kemi dhjetëra mijëra serverë fizikë që ruajnë skedarë. Përdoruesve u pëlqen të ngarkojnë skedarë, dhe neve na pëlqen t'i ruajmë dhe t'i ndajmë ato. Disa nga këta serverë mbyllen nga serverë të veçantë pu/pp.

pu/pp

Nëse keni hapur skedën e rrjetit në VK, keni parë pu/pp.

FAQ mbi arkitekturën dhe punën e VKontakte

Çfarë është pu/pp? Nëse mbyllim një server pas tjetrit, atëherë ekzistojnë dy mundësi për ngarkimin dhe shkarkimin e një skedari në serverin që është mbyllur: drejtpërdrejt përmes http://cs100500.userapi.com/path ose nëpërmjet serverit të ndërmjetëm - http://pu.vk.com/c100500/path.

Pu është emri historik për ngarkimin e fotografive dhe pp është përfaqësues i fotografisë. Kjo do të thotë, një server është për ngarkimin e fotove dhe një tjetër është për ngarkimin. Tani jo vetëm fotot janë ngarkuar, por emri është ruajtur.

Këta serverë mbyllni seancat HTTPSpër të hequr ngarkesën e procesorit nga ruajtja. Gjithashtu, duke qenë se skedarët e përdoruesve përpunohen në këta serverë, sa më pak të ndjeshme të ruhen informacione në këto makina, aq më mirë. Për shembull, çelësat e enkriptimit HTTPS.

Meqenëse makinat mbyllen nga makineritë tona të tjera, ne mund të lejojmë që të mos u japim atyre IP të jashtme "të bardha", dhe jep "gri". Në këtë mënyrë kemi kursyer në grupin IP dhe garantuam mbrojtjen e makinave nga aksesi i jashtëm - thjesht nuk ka IP për të hyrë në të.

Rezistenca ndaj IP-ve të përbashkëta. Për sa i përket tolerancës së gabimeve, skema funksionon njësoj - disa serverë fizikë kanë një IP të përbashkët fizike, dhe pajisja përballë tyre zgjedh se ku do të dërgojë kërkesën. Do të flas për opsionet e tjera më vonë.

Pika e diskutueshme është se në këtë rast klienti mban më pak lidhje. Nëse ka të njëjtën IP për disa makina - me të njëjtin host: pu.vk.com ose pp.vk.com, shfletuesi i klientit ka një kufi në numrin e kërkesave të njëkohshme për një host. Por në kohën e HTTP/2 kudo, unë besoj se kjo nuk është më aq e rëndësishme.

Disavantazhi i dukshëm i skemës është se duhet pomponi të gjithë trafikun, i cili shkon në ruajtje, përmes një serveri tjetër. Meqenëse ne pompojmë trafikun përmes makinerive, ne ende nuk mund të pompojmë trafik të rëndë, për shembull, video, duke përdorur të njëjtën skemë. Ne e transmetojmë atë drejtpërdrejt - një lidhje e veçantë direkte për depo të veçanta posaçërisht për video. Ne transmetojmë përmbajtje më të lehtë përmes një përfaqësuesi.

Jo shumë kohë më parë ne morëm një version të përmirësuar të proxy. Tani do t'ju tregoj se si ndryshojnë nga ato të zakonshmet dhe pse është e nevojshme.

Diell

Në shtator 2017, Oracle, e cila kishte blerë më parë Sun, pushoi një numër të madh punonjësish të Sun. Mund të themi se në këtë moment kompania pushoi së ekzistuari. Kur zgjodhën një emër për sistemin e ri, administratorët tanë vendosën t'i bëjnë homazhe kujtimit të kësaj kompanie dhe e quajtën sistemin e ri Sun. Mes nesh ne e quajmë atë thjesht "diej".

FAQ mbi arkitekturën dhe punën e VKontakte

pp kishte disa probleme. Një IP për grup - cache joefektive. Disa serverë fizikë ndajnë një adresë IP të përbashkët dhe nuk ka asnjë mënyrë për të kontrolluar se në cilin server do të shkojë kërkesa. Prandaj, nëse përdorues të ndryshëm vijnë për të njëjtin skedar, atëherë nëse ka një cache në këta serverë, skedari përfundon në cache-in e secilit server. Kjo është një skemë shumë joefikase, por asgjë nuk mund të bëhej.

Rrjedhimisht - ne nuk mund të ndajmë përmbajtjen, sepse ne nuk mund të zgjedhim një server specifik për këtë grup - ata kanë një IP të përbashkët. Gjithashtu për disa arsye të brendshme kemi nuk ishte e mundur të instaloheshin serverë të tillë në rajone. Ata qëndruan vetëm në Shën Petersburg.

Me diellet ndryshuam sistemin e përzgjedhjes. Tani kemi rrugëtim anycast: drejtim dinamik, anycast, demon i vetëkontrollit. Çdo server ka IP-në e vet individuale, por një nënrrjet të përbashkët. Gjithçka është konfiguruar në atë mënyrë që nëse një server dështon, trafiku shpërndahet automatikisht nëpër serverët e tjerë të të njëjtit grup. Tani është e mundur të zgjidhni një server specifik, nuk ka memorie të tepërt, dhe besueshmëria nuk u ndikua.

Mbështetja e peshës. Tani mund të përballojmë instalimin e makinerive me fuqi të ndryshme sipas nevojës dhe gjithashtu, në rast problemesh të përkohshme, të ndryshojmë peshat e "diellëve" të punës për të ulur ngarkesën mbi to, në mënyrë që ato të "pushojnë" dhe të fillojnë të punojnë përsëri.

Ndarja sipas ID-së së përmbajtjes. Një gjë qesharake në lidhje me ndarjen: ne zakonisht copëtojmë përmbajtjen në mënyrë që përdorues të ndryshëm të shkojnë në të njëjtin skedar përmes të njëjtit "diell" në mënyrë që të kenë një memorie të përbashkët.

Së fundmi kemi lançuar aplikacionin “Tërfili”. Ky është një kuiz online në një transmetim të drejtpërdrejtë, ku hosti bën pyetje dhe përdoruesit përgjigjen në kohë reale, duke zgjedhur opsionet. Aplikacioni ka një bisedë ku përdoruesit mund të bisedojnë. Mund të lidhet njëkohësisht me transmetimin më shumë se 100 mijë njerëz. Ata të gjithë shkruajnë mesazhe që u dërgohen të gjithë pjesëmarrësve dhe një avatar vjen së bashku me mesazhin. Nëse 100 mijë njerëz vijnë për një avatar në një "diell", atëherë ai ndonjëherë mund të rrokulliset pas një reje.

Për t'i bërë ballë shpërthimeve të kërkesave për të njëjtin skedar, është pikërisht për një lloj të caktuar përmbajtjeje që ne aktivizojmë një skemë budallaqe që shpërndan skedarë në të gjithë "diejtë" e disponueshëm në rajon.

Dielli nga brenda

Proxy i kundërt në nginx, cache ose në RAM ose në disqe të shpejtë Optane/NVMe. Shembull: http://sun4-2.userapi.com/c100500/path — një lidhje me "diellin", i cili ndodhet në rajonin e katërt, grupi i dytë i serverëve. Ai mbyll skedarin e rrugës, i cili ndodhet fizikisht në serverin 100500.

Vend i fshehtë

Ne i shtojmë një nyje tjetër skemës sonë arkitekturore - mjedisin e memorizimit.

FAQ mbi arkitekturën dhe punën e VKontakte

Më poshtë është diagrami i paraqitjes arkat rajonale, janë rreth 20 të tilla. Këto janë vendet ku ndodhen cache dhe "suns", të cilat mund të ruajnë trafikun përmes vetes.

FAQ mbi arkitekturën dhe punën e VKontakte

Ky është ruajtja e përmbajtjes multimediale; këtu nuk ruhen të dhëna të përdoruesit - vetëm muzikë, video, foto.

Për të përcaktuar rajonin e përdoruesit, ne ne mbledhim prefikset e rrjetit BGP të shpallura në rajone. Në rastin e rikthimit, ne gjithashtu duhet të analizojmë bazën e të dhënave geoip nëse nuk mund ta gjejmë IP-në sipas prefikseve. Ne përcaktojmë rajonin me IP-në e përdoruesit. Në kod, ne mund të shikojmë një ose më shumë rajone të përdoruesit - ato pika me të cilat ai është më afër gjeografikisht.

Si funksionon kjo gjë?

Ne llogarisim popullaritetin e skedarëve sipas rajonit. Ekziston një numër i memories rajonale ku ndodhet përdoruesi dhe një identifikues skedari - ne e marrim këtë çift dhe e rrisim vlerësimin me çdo shkarkim.

Në të njëjtën kohë, demonët - shërbimet në rajone - herë pas here vijnë në API dhe thonë: "Unë jam cache e tillë dhe e tillë, më jep një listë të skedarëve më të njohur në rajonin tim që nuk janë ende në mua. ” API jep një sërë skedarësh të renditur sipas vlerësimit, daemon i shkarkon ato, i çon në rajone dhe i dorëzon skedarët prej andej. Ky është ndryshimi themelor midis pu/pp dhe Sun nga cache: ata e japin skedarin përmes vetes së tyre menjëherë, edhe nëse ky skedar nuk është në cache, dhe cache fillimisht shkarkon skedarin në vetvete dhe më pas fillon ta kthejë atë.

Në këtë rast marrim përmbajtje më afër përdoruesve dhe shpërndarja e ngarkesës së rrjetit. Për shembull, vetëm nga cache e Moskës shpërndajmë më shumë se 1 Tbit/s gjatë orëve të pikut.

Por ka probleme - serverët cache nuk janë gome. Për përmbajtje super të njohura, ndonjëherë nuk ka rrjet të mjaftueshëm për një server të veçantë. Serverët tanë cache janë 40-50 Gbit/s, por ka përmbajtje që bllokon plotësisht një kanal të tillë. Ne po shkojmë drejt zbatimit të ruajtjes së më shumë se një kopje të skedarëve të njohur në rajon. Shpresoj se do ta zbatojmë deri në fund të vitit.

Ne shikuam arkitekturën e përgjithshme.

  • Serverë të përparmë që pranojnë kërkesa.
  • Backend që përpunojnë kërkesat.
  • Magazinimet që mbyllen nga dy lloje proxy.
  • Memoriet rajonale.

Çfarë i mungon këtij diagrami? Sigurisht, bazat e të dhënave në të cilat ne ruajmë të dhënat.

Bazat e të dhënave ose motorët

Ne i quajmë ato jo baza të të dhënave, por motorë - Motorë, sepse praktikisht nuk kemi baza të të dhënave në kuptimin e pranuar përgjithësisht.

FAQ mbi arkitekturën dhe punën e VKontakte

Kjo është një masë e nevojshme. Kjo ndodhi sepse në 2008-2009, kur VK pati një rritje shpërthyese në popullaritet, projekti funksionoi tërësisht në MySQL dhe Memcache dhe pati probleme. MySQL pëlqente të prishte dhe të korruptonte skedarët, pas së cilës nuk do të rikuperohej dhe Memcache gradualisht u degradua në performancë dhe duhej të rifillohej.

Rezulton se projekti gjithnjë e më popullor kishte ruajtje të vazhdueshme, e cila korrupton të dhënat dhe një cache, e cila ngadalësohet. Në kushte të tilla, është e vështirë të zhvillohet një projekt në rritje. U vendos që të përpiqemi të rishkruajmë gjërat kritike në të cilat projekti ishte fokusuar në biçikletat tona.

Zgjidhja ishte e suksesshme. Kishte një mundësi për ta bërë këtë, si dhe një domosdoshmëri ekstreme, sepse mënyra të tjera të shkallëzimit nuk ekzistonin në atë kohë. Nuk kishte një bandë bazash të dhënash, NoSQL nuk ekzistonte ende, kishte vetëm MySQL, Memcache, PostrgreSQL - dhe kaq.

Operacion universal. Zhvillimi u drejtua nga ekipi ynë i zhvilluesve C dhe gjithçka u bë në një mënyrë të qëndrueshme. Pavarësisht nga motori, të gjithë kishin afërsisht të njëjtin format skedari të shkruar në disk, të njëjtat parametra nisjeje, përpunonin sinjalet në të njëjtën mënyrë dhe silleshin afërsisht njësoj në rast të situatave dhe problemeve në skaj. Me rritjen e motorëve, është i përshtatshëm për administratorët që të operojnë sistemin - nuk ka kopsht zoologjik që duhet të mirëmbahet, dhe ata duhet të rimësojnë se si të përdorin çdo bazë të dhënash të re të palëve të treta, gjë që bëri të mundur që shpejt dhe shpejt rrisin me lehtësi numrin e tyre.

Llojet e motorëve

Ekipi shkroi mjaft motorë. Këtu janë vetëm disa prej tyre: mik, sugjerime, imazh, ipdb, letra, lista, regjistra, memcached, meowdb, lajme, nostradamus, foto, lista dëgjimi, pmemcached, sandbox, kërkim, hapësirë ​​ruajtëse, pëlqime, detyra,…

Për çdo detyrë që kërkon një strukturë specifike të dhënash ose përpunon kërkesa atipike, ekipi C shkruan një motor të ri. Pse jo.

Ne kemi një motor të veçantë e memorizuar, e cila është e ngjashme me një të zakonshme, por me një tufë të mirash dhe që nuk ngadalëson. Jo ClickHouse, por gjithashtu funksionon. E disponueshme veçmas pmemcached - A memcached këmbëngulës, i cili gjithashtu mund të ruajë të dhënat në disk, për më tepër, sesa të përshtatet në RAM, në mënyrë që të mos humbasin të dhënat kur riniset. Ka motorë të ndryshëm për detyra individuale: radhë, lista, grupe - gjithçka që kërkon projekti ynë.

Grupimet

Nga perspektiva e kodit, nuk ka nevojë të mendohen motorët ose bazat e të dhënave si procese, entitete ose instanca. Kodi funksionon posaçërisht me grupe, me grupe motorësh - një lloj për grup. Le të themi se ekziston një grup memcached - është thjesht një grup makinerish.

Kodi nuk ka nevojë të dijë fare vendndodhjen fizike, madhësinë ose numrin e serverëve. Ai shkon në grup duke përdorur një identifikues të caktuar.

Që kjo të funksionojë, duhet të shtoni një ent tjetër që ndodhet midis kodit dhe motorëve - prokurë.

Proxy RPC

Përfaqësues autobus lidhës, në të cilin funksionon pothuajse i gjithë faqja. Në të njëjtën kohë kemi asnjë zbulim shërbimi — në vend të kësaj, ekziston një konfigurim për këtë përfaqësues, i cili njeh vendndodhjen e të gjitha grupimeve dhe të gjitha pjesët e këtij grupi. Kjo është ajo që bëjnë administratorët.

Programuesit nuk u interesojnë fare se sa, ku dhe sa kushton - ata thjesht shkojnë në grup. Kjo na lejon shumë. Kur merr një kërkesë, përfaqësuesi e ridrejton kërkesën, duke ditur se ku - e përcakton vetë këtë.

FAQ mbi arkitekturën dhe punën e VKontakte

Në këtë rast, proxy është një pikë mbrojtjeje kundër dështimit të shërbimit. Nëse një motor ngadalësohet ose rrëzohet, atëherë përfaqësuesi e kupton këtë dhe i përgjigjet në përputhje me rrethanat anës së klientit. Kjo ju lejon të hiqni kohëzgjatjen - kodi nuk pret që motori të përgjigjet, por kupton që nuk po funksionon dhe duhet të sillet disi ndryshe. Kodi duhet të përgatitet për faktin se bazat e të dhënave nuk funksionojnë gjithmonë.

Implementime specifike

Ndonjëherë ne ende dëshirojmë vërtet të kemi një lloj zgjidhjeje jo standarde si motor. Në të njëjtën kohë, u vendos që të mos përdornim përfaqësuesin tonë të gatshëm rpc, të krijuar posaçërisht për motorët tanë, por të bënim një përfaqësues të veçantë për detyrën.

Për MySQL, të cilën e kemi ende aty-këtu, përdorim db-proxy, dhe për ClickHouse - Shtëpi kotelesh.

Në përgjithësi funksionon kështu. Ekziston një server i caktuar, ai drejton kPHP, Go, Python - në përgjithësi, çdo kod që mund të përdorë protokollin tonë RPC. Kodi ekzekutohet në nivel lokal në një përfaqësues RPC - çdo server ku ndodhet kodi ekzekuton përfaqësuesin e tij lokal. Me kërkesë, përfaqësuesi e kupton se ku të shkojë.

FAQ mbi arkitekturën dhe punën e VKontakte

Nëse një motor dëshiron të shkojë në një tjetër, edhe nëse është një fqinj, ai kalon përmes një proxy, sepse fqinji mund të jetë në një qendër tjetër të dhënash. Motori nuk duhet të mbështetet në njohjen e vendndodhjes së ndonjë gjëje tjetër përveç vetvetes - kjo është zgjidhja jonë standarde. Por sigurisht që ka përjashtime :)

Një shembull i një skeme TL sipas së cilës funksionojnë të gjithë motorët.

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;

Ky është një protokoll binar, analogu më i afërt i të cilit është protobuf. Skema parashkruan fusha opsionale, lloje komplekse - zgjerime të skalarëve të integruar dhe pyetje. Gjithçka funksionon sipas këtij protokolli.

RPC mbi TL mbi TCP/UDP… UDP?

Ne kemi një protokoll RPC për ekzekutimin e kërkesave të motorit që funksionon në krye të skemës TL. E gjithë kjo funksionon përmes një lidhjeje TCP/UDP. TCP është e kuptueshme, por pse na duhet shpesh UDP?

UDP ndihmon shmangni problemin e një numri të madh lidhjesh midis serverëve. Nëse çdo server ka një përfaqësues RPC dhe, në përgjithësi, mund të shkojë në çdo motor, atëherë ka dhjetëra mijëra lidhje TCP për server. Ka një ngarkesë, por është e kotë. Në rastin e UDP-së ky problem nuk ekziston.

Asnjë shtrëngim duarsh i tepërt TCP. Ky është një problem tipik: kur lëshohet një motor i ri ose një server i ri, shumë lidhje TCP krijohen menjëherë. Për kërkesa të vogla me peshë të lehtë, për shembull, ngarkesa UDP, i gjithë komunikimi midis kodit dhe motorit është dy pako UDP: njëri fluturon në një drejtim, i dyti në tjetrin. Një udhëtim vajtje-ardhje - dhe kodi mori një përgjigje nga motori pa shtrëngim duarsh.

Po, gjithçka funksionon me një përqindje shumë të vogël të humbjes së paketave. Protokolli ka mbështetje për ritransmetime dhe ndërprerje, por nëse humbim shumë, do të marrim pothuajse TCP, gjë që nuk është fitimprurëse. Ne nuk e drejtojmë UDP nëpër oqeane.

Ne kemi mijëra serverë të tillë, dhe skema është e njëjtë: një paketë motorësh është instaluar në çdo server fizik. Ato janë kryesisht me një fillesë për të ekzekutuar sa më shpejt që të jetë e mundur pa bllokuar dhe janë të copëtuara si zgjidhje me një fije. Në të njëjtën kohë, ne nuk kemi asgjë më të besueshme se këta motorë, dhe shumë vëmendje i kushtohet ruajtjes së vazhdueshme të të dhënave.

Ruajtja e vazhdueshme e të dhënave

Motorët shkruajnë bilogje. Një binlog është një skedar në fund të të cilit shtohet një ngjarje për një ndryshim në gjendje ose të dhëna. Në zgjidhje të ndryshme quhet ndryshe: log binar, WAL, AOF, por parimi është i njëjtë.

Për të parandaluar që motori të rilexojë të gjithë binlogun për shumë vite kur rindizet, motorët shkruajnë fotografitë - gjendja aktuale. Nëse është e nevojshme, ata lexojnë fillimisht prej tij dhe më pas përfundojnë leximin nga binlogu. Të gjitha binlogët janë shkruar në të njëjtin format binar - sipas skemës TL, në mënyrë që administratorët të mund t'i administrojnë ato në mënyrë të barabartë me mjetet e tyre. Nuk ka nevojë të tillë për fotografi. Ekziston një titull i përgjithshëm që tregon se fotografia e kujt është int, magjia e motorit dhe cili trup nuk është i rëndësishëm për askënd. Ky është një problem me motorin që regjistroi fotografinë.

Unë do të përshkruaj shpejt parimin e funksionimit. Ekziston një server në të cilin motori funksionon. Ai hap një binlog të ri bosh për të shkruar dhe shkruan një ngjarje për ndryshim në të.

FAQ mbi arkitekturën dhe punën e VKontakte

Në një moment, ai ose vendos të bëjë një fotografi vetë, ose merr një sinjal. Serveri krijon një skedar të ri, shkruan të gjithë gjendjen e tij në të, shton madhësinë aktuale të binlogut - offset - në fund të skedarit dhe vazhdon të shkruajë më tej. Një binlog i ri nuk është krijuar.

FAQ mbi arkitekturën dhe punën e VKontakte

Në një moment, kur motori të rindizet, do të ketë edhe një bilog dhe një fotografi në disk. Motori lexon të gjithë fotografinë dhe ngre gjendjen e tij në një pikë të caktuar.

FAQ mbi arkitekturën dhe punën e VKontakte

Lexon pozicionin që ishte në kohën kur u krijua fotografia dhe madhësia e binlogut.

FAQ mbi arkitekturën dhe punën e VKontakte

Lexon fundin e binlogut për të marrë gjendjen aktuale dhe vazhdon të shkruajë ngjarje të mëtejshme. Kjo është një skemë e thjeshtë; të gjithë motorët tanë punojnë sipas saj.

Replikimi i të dhënave

Si rezultat, përsëritja e të dhënave në tonë të bazuara në deklarata — ne shkruajmë në binlog jo ndonjë ndryshim faqeje, por domethënë kërkesat për ndryshim. Shumë e ngjashme me atë që vjen në rrjet, vetëm pak e modifikuar.

E njëjta skemë përdoret jo vetëm për përsëritje, por edhe për të krijuar kopje rezervë. Ne kemi një motor - një mjeshtër shkrimi që shkruan në binlog. Në çdo vend tjetër ku administratorët e kanë vendosur, ky binlog kopjohet dhe kaq - ne kemi një kopje rezervë.

FAQ mbi arkitekturën dhe punën e VKontakte

Nëse është e nevojshme duke lexuar kopjePër të reduktuar ngarkesën e leximit të CPU-së, thjesht lëshohet motori i leximit, i cili lexon fundin e binlogut dhe i ekzekuton këto komanda në nivel lokal.

Vonesa këtu është shumë e vogël dhe është e mundur të zbulohet se sa kopja mbetet pas mjeshtrit.

Ndarja e të dhënave në përfaqësuesin RPC

Si funksionon ndarja? Si e kupton përfaqësuesi se cilit fragment grupi duhet t'i dërgojë? Kodi nuk thotë: "Dërgo për 15 copë!" - jo, kjo bëhet nga përfaqësuesi.

Skema më e thjeshtë është firstint - numri i parë në kërkesë.

get(photo100_500) => 100 % N.

Ky është një shembull për një protokoll teksti të thjeshtë memcached, por, sigurisht, pyetjet mund të jenë komplekse dhe të strukturuara. Shembulli merr numrin e parë në pyetje dhe pjesën e mbetur kur ndahet me madhësinë e grupit.

Kjo është e dobishme kur duam të kemi lokalitet të të dhënave të një entiteti të vetëm. Le të themi se 100 është një ID përdoruesi ose grupi, dhe ne duam që të gjitha të dhënat e një entiteti të jenë në një copëz për pyetje komplekse.

Nëse nuk na intereson se si kërkesat shpërndahen në të gjithë grupin, ekziston një mundësi tjetër - duke hash të gjithë copëzën.

hash(photo100_500) => 3539886280 % N

Ne marrim gjithashtu hash-in, pjesën e mbetur të pjesëtimit dhe numrin e copëzave.

Të dyja këto opsione funksionojnë vetëm nëse jemi të përgatitur për faktin se kur të rrisim madhësinë e grupit, do ta ndajmë ose do ta rrisim me shumëfish. Për shembull, ne kishim 16 copëza, nuk kemi mjaft, duam më shumë - mund të marrim me siguri 32 pa ndërprerje. Nëse duam të rrisim jo shumëfish, do të ketë kohë joproduktive, sepse nuk do të jemi në gjendje të ndajmë me saktësi gjithçka pa humbje. Këto opsione janë të dobishme, por jo gjithmonë.

Nëse na duhet të shtojmë ose heqim një numër arbitrar serverësh, ne përdorim Hashimi i vazhdueshëm në ring a la Ketama. Por në të njëjtën kohë, ne humbasim plotësisht lokalitetin e të dhënave; ne duhet të bashkojmë kërkesën në grup në mënyrë që çdo pjesë të kthejë përgjigjen e saj të vogël dhe më pas t'i bashkojmë përgjigjet me përfaqësuesin.

Ka kërkesa super specifike. Duket kështu: përfaqësuesi RPC merr kërkesën, përcakton se në cilin grup të shkojë dhe përcakton copëzën. Pastaj ka ose mjeshtra shkrimi, ose, nëse grupi ka mbështetje për kopje, ai dërgon në një kopje sipas kërkesës. Proxy i bën të gjitha këto.

FAQ mbi arkitekturën dhe punën e VKontakte

Regjistrat

Ne shkruajmë regjistrat në disa mënyra. Më e dukshme dhe më e thjeshta është shkruani regjistrat në memcache.

ring-buffer: prefix.idx = line

Ekziston një parashtesë kryesore - emri i regjistrit, një rresht, dhe ka madhësinë e këtij regjistri - numrin e rreshtave. Marrim një numër të rastësishëm nga 0 në numrin e rreshtave minus 1. Çelësi në memcache është një parashtesë e lidhur me këtë numër të rastësishëm. Ne ruajmë linjën e regjistrit dhe kohën aktuale në vlerë.

Kur është e nevojshme të lexohen regjistrat, ne kryejmë Multi Get të gjithë çelësat, të renditur sipas kohës, dhe kështu merrni një regjistër prodhimi në kohë reale. Skema përdoret kur duhet të korrigjoni diçka në prodhim në kohë reale, pa prishur asgjë, pa ndalur ose lejuar trafikun në makina të tjera, por ky regjistër nuk zgjat shumë.

Për ruajtjen e besueshme të trungjeve ne kemi një motor trungje-motor. Kjo është pikërisht arsyeja pse u krijua dhe përdoret gjerësisht në një numër të madh grupimesh. Grupi më i madh që unë njoh, ruan 600 TB trungje të paketuara.

Motori është shumë i vjetër, ka grupime që janë tashmë 6-7 vjeç. Ka probleme me të që ne po përpiqemi t'i zgjidhim, për shembull, filluam të përdorim në mënyrë aktive ClickHouse për të ruajtur regjistrat.

Mbledhja e regjistrave në ClickHouse

Ky diagram tregon se si ne hyjmë në motorët tanë.

FAQ mbi arkitekturën dhe punën e VKontakte

Ekziston një kod që shkon lokalisht përmes RPC në përfaqësuesin RPC dhe kupton se ku duhet të shkojë te motori. Nëse duam të shkruajmë regjistra në ClickHouse, duhet të ndryshojmë dy pjesë në këtë skemë:

  • zëvendësoni disa motorë me ClickHouse;
  • zëvendësoni përfaqësuesin RPC, i cili nuk mund të hyjë në ClickHouse, me një zgjidhje që mundet, dhe nëpërmjet RPC.

Motori është i thjeshtë - ne e zëvendësojmë atë me një server ose një grup serverësh me ClickHouse.

Dhe për të shkuar në ClickHouse, ne e bëmë Shtëpia e Koteleve. Nëse shkojmë drejtpërdrejt nga KittenHouse në ClickHouse, nuk do të përballet. Edhe pa kërkesa, ai shtohet nga lidhjet HTTP të një numri të madh makinash. Që skema të funksionojë, në një server me ClickHouse përfaqësuesi i kundërt lokal është ngritur, i cili është shkruar në mënyrë të tillë që të përballojë vëllimet e nevojshme të lidhjeve. Ai gjithashtu mund të ruajë të dhënat brenda vetes në mënyrë relativisht të besueshme.

FAQ mbi arkitekturën dhe punën e VKontakte

Ndonjëherë ne nuk duam të zbatojmë skemën RPC në zgjidhje jo standarde, për shembull, në nginx. Prandaj, KittenHouse ka aftësinë për të marrë regjistrat përmes UDP.

FAQ mbi arkitekturën dhe punën e VKontakte

Nëse dërguesi dhe marrësi i regjistrave punojnë në të njëjtën makinë, atëherë probabiliteti për të humbur një paketë UDP brenda hostit lokal është mjaft i ulët. Si një kompromis midis nevojës për të zbatuar RPC në një zgjidhje të palëve të treta dhe besueshmërisë, ne thjesht përdorim dërgimin UDP. Ne do t'i kthehemi kësaj skeme më vonë.

Monitorimi

Ne kemi dy lloje regjistrash: ato të mbledhura nga administratorët në serverët e tyre dhe ato të shkruara nga zhvilluesit nga kodi. Ato korrespondojnë me dy lloje metrike: sistemi dhe produkti.

Metrika e sistemit

Ajo funksionon në të gjithë serverët tanë Të dhënat Net, i cili mbledh statistika dhe i dërgon ato në Karboni grafit. Prandaj, ClickHouse përdoret si një sistem ruajtjeje, dhe jo Whisper, për shembull. Nëse është e nevojshme, mund të lexoni drejtpërdrejt nga ClickHouse, ose të përdorni grafana për metrikat, grafikët dhe raportet. Si zhvillues, ne kemi akses të mjaftueshëm në Netdata dhe Grafana.

Metrikat e produktit

Për lehtësi, ne kemi shkruar shumë gjëra. Për shembull, ekziston një grup funksionesh të zakonshme që ju lejojnë të shkruani vlerat Counts, UniqueCounts në statistika, të cilat dërgohen diku më tej.

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

$stats = statlogsStatData($params)

Më pas, ne mund të përdorim filtrat e renditjes dhe grupimit dhe të bëjmë gjithçka që duam nga statistikat - të ndërtojmë grafikë, të konfigurojmë Watchdogs.

Ne shkruajmë shumë shumë metrika numri i ngjarjeve është nga 600 miliardë në 1 trilion në ditë. Megjithatë, ne duam t'i mbajmë ato të paktën nja dy vjetpër të kuptuar tendencat në metrikë. Duke i bashkuar të gjitha është një problem i madh që nuk e kemi zgjidhur ende. Unë do t'ju tregoj se si ka funksionuar vitet e fundit.

Ne kemi funksione që shkruajnë këto metrika në memcache lokalepër të reduktuar numrin e hyrjeve. Një herë në një periudhë të shkurtër kohore lançohet në nivel lokal statistika-demon mbledh të gjitha të dhënat. Më pas, demoni bashkon matjet në dy shtresa të serverëve trungje-mbledhës, i cili grumbullon statistika nga një grup makinerish tona në mënyrë që shtresa pas tyre të mos vdesë.

FAQ mbi arkitekturën dhe punën e VKontakte

Nëse është e nevojshme, ne mund t'u shkruajmë drejtpërdrejt mbledhësve të regjistrave.

FAQ mbi arkitekturën dhe punën e VKontakte

Por shkrimi nga kodi direkt te mbledhësit, duke anashkaluar stas-daemom, është një zgjidhje e dobët e shkallëzueshme sepse rrit ngarkesën në kolektor. Zgjidhja është e përshtatshme vetëm nëse për ndonjë arsye nuk mund të ngremë stats-daemon memcache në makinë, ose ai u rrëzua dhe ne shkuam drejtpërdrejt.

Më pas, regjistrat-mbledhës bashkojnë statistikat në meowDB - kjo është databaza jonë, e cila gjithashtu mund të ruajë metrikat.

FAQ mbi arkitekturën dhe punën e VKontakte

Pastaj ne mund të bëjmë zgjedhje binare "afër-SQL" nga kodi.

FAQ mbi arkitekturën dhe punën e VKontakte

eksperiment

Në verën e vitit 2018, ne patëm një hackathon të brendshëm dhe lindi ideja që të përpiqeshim të zëvendësonim pjesën e kuqe të diagramit me diçka që mund të ruante metrikat në ClickHouse. Ne kemi regjistra në ClickHouse - pse të mos e provoni?

FAQ mbi arkitekturën dhe punën e VKontakte

Ne kishim një skemë që shkruante regjistrat përmes KittenHouse.

FAQ mbi arkitekturën dhe punën e VKontakte

Ne vendosëm shtoni një tjetër "*House" në diagram, të cilat do të marrin saktësisht metrikat në formatin siç i shkruan kodi ynë nëpërmjet UDP. Pastaj kjo *House i kthen ato në inserte, si trungje, të cilat KittenHouse i kupton. Ai mund t'i dorëzojë në mënyrë të përsosur këto regjistra në ClickHouse, i cili duhet të jetë në gjendje t'i lexojë ato.

FAQ mbi arkitekturën dhe punën e VKontakte

Skema me bazën e të dhënave memcache, stats-daemon dhe logs-collectors është zëvendësuar me këtë.

FAQ mbi arkitekturën dhe punën e VKontakte

Skema me bazën e të dhënave memcache, stats-daemon dhe logs-collectors është zëvendësuar me këtë.

  • Këtu ka një dërgim nga kodi, i cili shkruhet lokalisht në StatsHouse.
  • StatsHouse shkruan metrikat UDP, tashmë të konvertuara në inserte SQL, në KittenHouse në grupe.
  • KittenHouse i dërgon ato në ClickHouse.
  • Nëse duam t'i lexojmë, atëherë i lexojmë duke anashkaluar StatsHouse - direkt nga ClickHouse duke përdorur SQL të rregullt.

Është akoma një eksperiment, por na pëlqen si rezulton. Nëse i rregullojmë problemet me skemën, atëherë ndoshta do të kalojmë plotësisht në të. Personalisht, shpresoj se po.

skemë nuk kursen hekurin. Nevojiten më pak serverë, nuk nevojiten statistika-demonë dhe log-mbledhës lokalë, por ClickHouse kërkon një server më të madh se ata në skemën aktuale. Nevojiten më pak serverë, por ata duhet të jenë më të shtrenjtë dhe më të fuqishëm.

Vendosni

Së pari, le të shohim vendosjen e PHP. Ne jemi duke u zhvilluar në git: përdorim GitLab и TeamCity për vendosje. Degët e zhvillimit shkrihen në degën master, nga masteri për testim shkrihen në inskenim dhe nga inskenimi në prodhim.

Para vendosjes, merret dega aktuale e prodhimit dhe ajo e mëparshmja, dhe skedarët e ndryshëm konsiderohen në to - ndryshime: krijuar, fshirë, ndryshuar. Ky ndryshim regjistrohet në binlogun e një motori special copyfast, i cili mund të përsërisë shpejt ndryshimet në të gjithë flotën tonë të serverëve. Ajo që përdoret këtu nuk është kopjimi direkt, por përsëritje e thashethemeve, kur një server dërgon ndryshime tek fqinjët e tij më të afërt, ato tek fqinjët e tyre, e kështu me radhë. Kjo ju lejon të përditësoni kodin në dhjetëra dhe njësi sekondash në të gjithë flotën. Kur ndryshimi arrin në kopjen lokale, ai i zbaton këto arna në të sistemi lokal i skedarëve. Rikthimi kryhet gjithashtu sipas të njëjtës skemë.

Ne gjithashtu vendosim shumë kPHP dhe ai gjithashtu ka zhvillimin e vet git sipas diagramit të mësipërm. Që nga kjo Binar i serverit HTTP, atëherë nuk mund të prodhojmë ndryshim - binarja e lëshimit peshon qindra MB. Prandaj, ekziston një mundësi tjetër këtu - versioni është shkruar në binlog copyfast. Me çdo ndërtim rritet, dhe gjatë rikthimit rritet gjithashtu. Version replikuar në serverë. Kopjuesit lokalë shohin që një version i ri ka hyrë në binlog dhe me të njëjtin përsëritje thashetheme ata marrin për vete versionin më të fundit të binarit, pa e lodhur serverin tonë master, por duke shpërndarë me kujdes ngarkesën në të gjithë rrjetin. Çfarë vijon rinisje e këndshme për versionin e ri.

Për motorët tanë, të cilët janë gjithashtu në thelb binare, skema është shumë e ngjashme:

  • git master dega;
  • binare në .deb;
  • versioni është shkruar në binlog copyfast;
  • replikuar në serverë;
  • serveri nxjerr një .dep të ri;
  • dpkg -i;
  • rinisje e këndshme në versionin e ri.

Dallimi është se binari ynë është i paketuar në arkiva .deb, dhe kur pompohen ato dpkg -i vendosen në sistem. Pse kPHP vendoset si binar, dhe motorët vendosen si dpkg? Kështu ndodhi. Ajo funksionon - mos e prekni.

Lidhje të dobishme:

Alexey Akulovich është një nga ata që, si pjesë e Komitetit të Programit, ndihmon PHP Rusia më 17 maj do të bëhet ngjarja më e madhe për zhvilluesit e PHP në kohët e fundit. Shikoni çfarë PC të lezetshëm kemi, çfarë folësit (dy prej tyre po zhvillojnë bërthamën PHP!) - duket si diçka që nuk mund ta humbisni nëse shkruani PHP.

Burimi: www.habr.com

Shto një koment