FAQ sa arkitektura at gawain ng VKontakte

Ang kasaysayan ng paglikha ng VKontakte ay nasa Wikipedia; sinabi ito ni Pavel mismo. Parang kilala na siya ng lahat. Tungkol sa mga panloob, arkitektura at istraktura ng site sa HighLoad++ Pavel Sinabi sa akin noong 2010. Maraming mga server ang nag-leak mula noon, kaya i-update namin ang impormasyon: i-dissect namin ito, aalisin ang loob, timbangin ito, at tingnan ang VK device mula sa teknikal na pananaw.

FAQ sa arkitektura at gawain ng VKontakte

Alexey Akulovich (AterCattus) backend developer sa VKontakte team. Ang transcript ng ulat na ito ay isang kolektibong sagot sa mga madalas itanong tungkol sa pagpapatakbo ng platform, imprastraktura, mga server at pakikipag-ugnayan sa pagitan nila, ngunit hindi tungkol sa pag-unlad, lalo na. tungkol sa bakal. Hiwalay, tungkol sa mga database at kung ano ang mayroon ang VK, tungkol sa pagkolekta ng mga log at pagsubaybay sa buong proyekto sa kabuuan. Mga detalye sa ilalim ng hiwa.



Sa loob ng higit sa apat na taon ay nakikitungo ako sa lahat ng uri ng mga gawain na may kaugnayan sa backend.

  • Pag-upload, pag-iimbak, pagproseso, pamamahagi ng media: video, live streaming, audio, mga larawan, mga dokumento.
  • Imprastraktura, platform, pagsubaybay ng developer, mga log, mga cache ng rehiyon, CDN, proprietary RPC protocol.
  • Pagsasama sa mga panlabas na serbisyo: push notification, external link parsing, RSS feed.
  • Pagtulong sa mga kasamahan sa iba't ibang mga katanungan, ang mga sagot na nangangailangan ng pagsisid sa hindi kilalang code.

Sa panahong ito, nagkaroon ako ng kamay sa maraming bahagi ng site. Gusto kong ibahagi ang karanasang ito.

Pangkalahatang arkitektura

Lahat, gaya ng dati, ay nagsisimula sa isang server o grupo ng mga server na tumatanggap ng mga kahilingan.

Front server

Tumatanggap ang front server ng mga kahilingan sa pamamagitan ng HTTPS, RTMP at WSS.

HTTPS - ito ay mga kahilingan para sa mga pangunahing at mobile na bersyon ng web ng site: vk.com at m.vk.com, at iba pang opisyal at hindi opisyal na mga kliyente ng aming API: mga mobile na kliyente, mga mensahero. May reception kami RTMP-trapiko para sa mga Live na broadcast na may hiwalay na front server at WSS- mga koneksyon para sa Streaming API.

Para sa HTTPS at WSS sa mga server ito ay nagkakahalaga nginx. Para sa mga RTMP broadcast, lumipat kami kamakailan sa sarili naming solusyon kive, ngunit ito ay lampas sa saklaw ng ulat. Para sa fault tolerance, ang mga server na ito ay nag-a-advertise ng mga karaniwang IP address at kumikilos sa mga grupo upang kung may problema sa isa sa mga server, ang mga kahilingan ng user ay hindi mawawala. Para sa HTTPS at WSS, ang parehong mga server na ito ay nag-e-encrypt ng trapiko upang makakuha ng bahagi ng pag-load ng CPU sa kanilang sarili.

Hindi na kami mag-uusap pa tungkol sa WSS at RTMP, ngunit tungkol lamang sa mga karaniwang kahilingan sa HTTPS, na karaniwang nauugnay sa isang web project.

backend

Sa likod ng harap ay karaniwang may mga backend server. Pinoproseso nila ang mga kahilingan na natatanggap ng front server mula sa mga kliyente.

Ito mga kPHP server, kung saan tumatakbo ang HTTP daemon, dahil ang HTTPS ay na-decrypt na. Ang kPHP ay isang server na tumatakbo mga modelo ng prefork: nagsisimula ng master na proseso, isang grupo ng mga bata na nagpoproseso, nagpapasa ng mga socket sa pakikinig sa kanila at pinoproseso nila ang kanilang mga kahilingan. Sa kasong ito, hindi na-restart ang mga proseso sa pagitan ng bawat kahilingan mula sa user, ngunit i-reset lang ang kanilang estado sa orihinal na zero-value na estado - kahilingan pagkatapos ng kahilingan, sa halip na i-restart.

Pamamahagi ng load

Ang lahat ng aming mga backend ay hindi isang malaking pool ng mga machine na maaaring magproseso ng anumang kahilingan. Tayo sila nahahati sa magkakahiwalay na grupo: pangkalahatan, mobile, api, video, pagtatanghal ng dula... Ang problema sa isang hiwalay na pangkat ng mga makina ay hindi makakaapekto sa lahat ng iba pa. Sa kaso ng mga problema sa video, ang gumagamit na nakikinig sa musika ay hindi malalaman ang tungkol sa mga problema. Aling backend ang pagpapadala ng kahilingan ay napagpasyahan ng nginx sa harap ayon sa config.

Pagkolekta ng sukatan at muling pagbabalanse

Upang maunawaan kung gaano karaming mga kotse ang kailangan namin sa bawat grupo, kami huwag umasa sa QPS. Ang mga backend ay iba, mayroon silang iba't ibang mga kahilingan, ang bawat kahilingan ay may iba't ibang kumplikado ng pagkalkula ng QPS. Kaya naman tayo nagpapatakbo kami sa konsepto ng pag-load sa server sa kabuuan - sa CPU at perf.

Mayroon kaming libu-libong ganoong mga server. Ang bawat pisikal na server ay nagpapatakbo ng pangkat ng kPHP upang i-recycle ang lahat ng mga core (dahil ang kPHP ay single threaded).

Server ng Nilalaman

Ang CS o Content Server ay isang storage. Ang CS ay isang server na nag-iimbak ng mga file at nagpoproseso din ng mga na-upload na file at lahat ng uri ng background synchronous na gawain na itinatalaga ng pangunahing web frontend dito.

Mayroon kaming libu-libong pisikal na server na nag-iimbak ng mga file. Gustung-gusto ng mga user na mag-upload ng mga file, at gusto naming iimbak at ibahagi ang mga ito. Ang ilan sa mga server na ito ay sarado ng mga espesyal na pu/pp server.

pu/pp

Kung binuksan mo ang tab ng network sa VK, nakita mo ang pu/pp.

FAQ sa arkitektura at gawain ng VKontakte

Ano ang pu/pp? Kung isasara namin ang isang server pagkatapos ng isa pa, mayroong dalawang pagpipilian para sa pag-upload at pag-download ng isang file sa server na sarado: direkta sa pamamagitan ng http://cs100500.userapi.com/path o sa pamamagitan ng intermediate server - http://pu.vk.com/c100500/path.

Ang Pu ay ang makasaysayang pangalan para sa pag-upload ng larawan, at ang pp ay photo proxy. Ibig sabihin, ang isang server ay para sa pag-upload ng mga larawan, at ang isa pa ay para sa pag-upload. Ngayon hindi lamang mga larawan ang na-load, ngunit ang pangalan ay napanatili.

Ang mga server na ito wakasan ang mga sesyon ng HTTPSpara alisin ang processor load mula sa storage. Gayundin, dahil ang mga file ng user ay pinoproseso sa mga server na ito, ang hindi gaanong sensitibong impormasyon na nakaimbak sa mga makinang ito, mas mabuti. Halimbawa, ang mga HTTPS encryption key.

Dahil ang mga makina ay isinara ng aming iba pang mga makina, hindi namin kayang bigyan sila ng "puting" panlabas na mga IP, at bigyan ng "grey". Sa ganitong paraan, naka-save kami sa IP pool at ginagarantiyahan na protektahan ang mga makina mula sa labas ng access - walang IP na makapasok dito.

Katatagan sa mga nakabahaging IP. Sa mga tuntunin ng fault tolerance, gumagana ang scheme - ang ilang mga pisikal na server ay may isang karaniwang pisikal na IP, at ang hardware sa harap nila ay pipili kung saan ipapadala ang kahilingan. Pag-uusapan ko ang iba pang mga pagpipilian sa ibang pagkakataon.

Ang kontrobersyal na punto ay na sa kasong ito ang kliyente ay nagpapanatili ng mas kaunting mga koneksyon. Kung mayroong parehong IP para sa ilang mga makina - na may parehong host: pu.vk.com o pp.vk.com, ang client browser ay may limitasyon sa bilang ng mga sabay-sabay na kahilingan sa isang host. Ngunit sa panahon ng ubiquitous HTTP/2, naniniwala ako na hindi na ito masyadong nauugnay.

Ang halatang kawalan ng scheme ay kailangan nito bomba ang lahat ng trapiko, na papunta sa storage, sa pamamagitan ng isa pang server. Dahil nag-pump kami ng trapiko sa pamamagitan ng mga makina, hindi pa kami makakapag-pump ng mabigat na trapiko, halimbawa, video, gamit ang parehong scheme. Direktang ipinapadala namin ito - isang hiwalay na direktang koneksyon para sa hiwalay na mga storage partikular para sa video. Nagpapadala kami ng mas magaan na nilalaman sa pamamagitan ng isang proxy.

Hindi pa nagtagal, nakakuha kami ng pinahusay na bersyon ng proxy. Ngayon sasabihin ko sa iyo kung paano sila naiiba sa mga ordinaryong at kung bakit ito kinakailangan.

araw

Noong Setyembre 2017, ang Oracle, na dati nang bumili ng Sun, nagtanggal ng malaking bilang ng mga empleyado ng Sun. Masasabi natin na sa sandaling ito ay tumigil na ang kumpanya. Kapag pumipili ng pangalan para sa bagong system, nagpasya ang aming mga administrator na magbigay pugay sa memorya ng kumpanyang ito at pinangalanan ang bagong system na Sun. Sa ating mga sarili, tinatawag lang natin siyang "suns".

FAQ sa arkitektura at gawain ng VKontakte

pp ay nagkaroon ng ilang mga problema. Isang IP bawat grupo - hindi epektibong cache. Maraming mga pisikal na server ang nagbabahagi ng isang karaniwang IP address, at walang paraan upang makontrol kung saang server mapupunta ang kahilingan. Samakatuwid, kung ang iba't ibang mga gumagamit ay dumating para sa parehong file, kung gayon kung mayroong isang cache sa mga server na ito, ang file ay napupunta sa cache ng bawat server. Ito ay isang napaka-hindi mahusay na pamamaraan, ngunit walang magagawa.

Dahil dito - hindi namin maaaring paghati-hatiin ang nilalaman, dahil hindi kami makakapili ng partikular na server para sa pangkat na ito - mayroon silang karaniwang IP. Para din sa ilang panloob na kadahilanan na mayroon tayo hindi posibleng mag-install ng mga naturang server sa mga rehiyon. Sila ay nakatayo lamang sa St. Petersburg.

Sa pamamagitan ng mga araw, binago namin ang sistema ng pagpili. Ngayon meron na tayo anycast routing: dynamic na pagruruta, anycast, self-check daemon. Ang bawat server ay may sariling indibidwal na IP, ngunit isang karaniwang subnet. Ang lahat ay na-configure sa paraang kung ang isang server ay nabigo, ang trapiko ay awtomatikong kumalat sa iba pang mga server ng parehong grupo. Ngayon ay posible na pumili ng isang partikular na server, walang kalabisan caching, at hindi naapektuhan ang pagiging maaasahan.

Suporta sa timbang. Ngayon ay kayang-kaya nating mag-install ng mga makina ng iba't ibang kapangyarihan kung kinakailangan, at gayundin, sa kaso ng mga pansamantalang problema, baguhin ang mga bigat ng gumaganang "araw" upang mabawasan ang pagkarga sa kanila, upang sila ay "magpahinga" at magsimulang magtrabaho muli.

Pagbabahagi ng content id. Isang nakakatawang bagay tungkol sa sharding: karaniwan naming pinaghati-hatian ang nilalaman upang ang iba't ibang mga gumagamit ay pumunta sa parehong file sa pamamagitan ng parehong "araw" upang magkaroon sila ng isang karaniwang cache.

Inilunsad namin kamakailan ang application na "Clover". Ito ay isang online na pagsusulit sa isang live na broadcast, kung saan ang host ay nagtatanong at ang mga user ay sumasagot sa real time, na pumipili ng mga opsyon. Ang app ay may chat kung saan makakapag-chat ang mga user. Maaaring sabay na kumonekta sa broadcast higit sa 100 libong tao. Lahat sila ay nagsusulat ng mga mensahe na ipinadala sa lahat ng mga kalahok, at isang avatar ang kasama ng mensahe. Kung 100 libong tao ang dumating para sa isang avatar sa isang "araw", kung minsan ay maaari itong gumulong sa likod ng isang ulap.

Upang makayanan ang mga pagsabog ng mga kahilingan para sa parehong file, ito ay para sa isang partikular na uri ng nilalaman na i-on namin ang isang hangal na pamamaraan na nagkakalat ng mga file sa lahat ng magagamit na "mga araw" sa rehiyon.

Araw mula sa loob

Reverse proxy sa nginx, cache alinman sa RAM o sa mabilis na Optane/NVMe disks. Halimbawa: http://sun4-2.userapi.com/c100500/path β€” isang link sa "araw", na matatagpuan sa ikaapat na rehiyon, ang pangalawang pangkat ng server. Isinasara nito ang path file, na pisikal na namamalagi sa server 100500.

Cache

Nagdagdag kami ng isa pang node sa aming architectural scheme - ang caching environment.

FAQ sa arkitektura at gawain ng VKontakte

Nasa ibaba ang layout diagram mga panrehiyong cache, may mga 20 sa kanila. Ito ang mga lugar kung saan matatagpuan ang mga cache at "suns", na maaaring mag-cache ng trapiko sa kanilang mga sarili.

FAQ sa arkitektura at gawain ng VKontakte

Ito ay pag-cache ng nilalamang multimedia; walang data ng gumagamit ang naka-imbak dito - musika, video, mga larawan lamang.

Upang matukoy ang rehiyon ng gumagamit, kami kinokolekta namin ang mga prefix ng BGP network na inihayag sa mga rehiyon. Sa kaso ng fallback, kailangan din naming i-parse ang geoip database kung hindi namin mahanap ang IP sa pamamagitan ng mga prefix. Tinutukoy namin ang rehiyon sa pamamagitan ng IP ng user. Sa code, maaari naming tingnan ang isa o higit pang mga rehiyon ng user - ang mga punto kung saan siya ay pinakamalapit sa heograpiya.

Paano ito gumagana?

Binibilang namin ang katanyagan ng mga file ayon sa rehiyon. Mayroong isang bilang ng rehiyonal na cache kung saan matatagpuan ang user, at isang file identifier - kinukuha namin ang pares na ito at dinadagdagan ang rating sa bawat pag-download.

Kasabay nito, ang mga demonyo - mga serbisyo sa mga rehiyon - paminsan-minsan ay pumupunta sa API at nagsasabi: "Ako ay ganoon at ganoong cache, bigyan ako ng isang listahan ng mga pinakasikat na file sa aking rehiyon na wala pa sa akin. ” Ang API ay naghahatid ng isang bungkos ng mga file na pinagsunod-sunod ayon sa rating, dina-download ng daemon ang mga ito, dinadala ang mga ito sa mga rehiyon at naghahatid ng mga file mula doon. Ito ang pangunahing pagkakaiba sa pagitan ng pu/pp at Sun mula sa mga cache: ibinibigay nila kaagad ang file sa pamamagitan ng kanilang mga sarili, kahit na ang file na ito ay wala sa cache, at unang dina-download ng cache ang file sa sarili nito, at pagkatapos ay sisimulan itong ibalik.

Sa kasong ito nakukuha namin nilalaman na mas malapit sa mga gumagamit at pagkalat ng network load. Halimbawa, mula lamang sa Moscow cache, namamahagi kami ng higit sa 1 Tbit/s sa mga oras ng peak.

Ngunit may mga problema - Ang mga cache server ay hindi goma. Para sa sobrang sikat na nilalaman, kung minsan ay walang sapat na network para sa isang hiwalay na server. Ang aming mga cache server ay 40-50 Gbit/s, ngunit may nilalaman na ganap na bumabara sa naturang channel. Sumusulong kami patungo sa pagpapatupad ng storage ng higit sa isang kopya ng mga sikat na file sa rehiyon. Sana ay maipatupad natin ito sa pagtatapos ng taon.

Tiningnan namin ang pangkalahatang arkitektura.

  • Mga front server na tumatanggap ng mga kahilingan.
  • Bina-backend ang pagpoproseso ng mga kahilingan.
  • Mga imbakan na isinara ng dalawang uri ng mga proxy.
  • Mga panrehiyong cache.

Ano ang kulang sa diagram na ito? Siyempre, ang mga database kung saan kami nag-iimbak ng data.

Mga database o makina

Tinatawag namin silang hindi mga database, ngunit mga makina - Mga Engine, dahil halos wala kaming mga database sa pangkalahatang tinatanggap na kahulugan.

FAQ sa arkitektura at gawain ng VKontakte

Ito ay isang kinakailangang panukala. Nangyari ito dahil noong 2008-2009, nang ang VK ay nagkaroon ng isang paputok na paglaki sa katanyagan, ang proyekto ay ganap na nagtrabaho sa MySQL at Memcache at nagkaroon ng mga problema. Gustung-gusto ng MySQL na mag-crash at masira ang mga file, pagkatapos nito ay hindi na ito mababawi, at ang Memcache ay unti-unting bumagsak sa pagganap at kailangang i-restart.

Lumalabas na ang lalong sikat na proyekto ay may patuloy na imbakan, na sumisira sa data, at isang cache, na bumabagal. Sa ganitong mga kondisyon, mahirap bumuo ng isang lumalagong proyekto. Napagpasyahan na subukang muling isulat ang mga kritikal na bagay na pinagtuunan ng pansin ng proyekto sa aming sariling mga bisikleta.

Naging matagumpay ang solusyon. Nagkaroon ng pagkakataong gawin ito, pati na rin ang matinding pangangailangan, dahil wala pang ibang paraan ng pag-scale noong panahong iyon. Wala pang isang grupo ng mga database, wala pang NoSQL, mayroon lamang MySQL, Memcache, PostrgreSQL - at iyon na.

Pangkalahatang operasyon. Ang pag-unlad ay pinangunahan ng aming koponan ng mga C developer at lahat ay ginawa sa isang pare-parehong paraan. Anuman ang makina, lahat sila ay may humigit-kumulang na parehong format ng file na isinulat sa disk, ang parehong mga parameter ng paglulunsad, naproseso ang mga signal sa parehong paraan, at kumilos nang halos pareho sa kaso ng mga sitwasyon at problema sa gilid. Sa paglaki ng mga makina, maginhawa para sa mga administrator na patakbuhin ang system - walang zoo na kailangang mapanatili, at kailangan nilang muling matutunan kung paano patakbuhin ang bawat bagong database ng third-party, na naging posible upang mabilis at maginhawang madagdagan. kanilang numero.

Mga uri ng makina

Ang koponan ay nagsulat ng ilang mga makina. Narito ang ilan lamang sa mga ito: kaibigan, mga pahiwatig, larawan, ipdb, mga titik, listahan, mga log, memcached, meowdb, balita, nostradamus, larawan, mga playlist, pmemcached, sandbox, paghahanap, storage, mga gusto, mga gawain, …

Para sa bawat gawain na nangangailangan ng partikular na istraktura ng data o nagpoproseso ng mga hindi tipikal na kahilingan, nagsusulat ang C team ng bagong makina. Bakit hindi.

May hiwalay kaming makina memcached, na katulad ng isang regular, ngunit may isang grupo ng mga goodies, at kung saan ay hindi nagpapabagal. Hindi ClickHouse, ngunit gumagana din ito. Available nang hiwalay pmemcached - Ay paulit-ulit na memcached, na maaari ring mag-imbak ng data sa disk, bukod dito, kaysa sa magkasya sa RAM, upang hindi mawalan ng data kapag nag-restart. Mayroong iba't ibang mga makina para sa mga indibidwal na gawain: mga pila, listahan, set - lahat ng kailangan ng aming proyekto.

Mga kumpol

Mula sa pananaw ng code, hindi na kailangang isipin ang mga engine o database bilang mga proseso, entity, o mga pagkakataon. Ang code ay partikular na gumagana sa mga kumpol, na may mga pangkat ng mga makina - isang uri bawat kumpol. Sabihin nating mayroong isang memcached cluster - ito ay isang grupo lamang ng mga makina.

Hindi kailangang malaman ng code ang pisikal na lokasyon, laki, o bilang ng mga server. Pumunta siya sa cluster gamit ang isang tiyak na identifier.

Para gumana ito, kailangan mong magdagdag ng isa pang entity na matatagpuan sa pagitan ng code at ng mga makina - proxy.

RPC proxy

Proxy kumukonektang bus, kung saan tumatakbo ang halos buong site. At the same time meron tayo walang pagtuklas ng serbisyo β€” sa halip, mayroong isang config para sa proxy na ito, na nakakaalam ng lokasyon ng lahat ng mga cluster at lahat ng mga shards ng cluster na ito. Ito ang ginagawa ng mga admin.

Walang pakialam ang mga programmer kung magkano, saan at kung ano ang halaga nito - pumunta lang sila sa cluster. Ito ay nagpapahintulot sa amin ng marami. Kapag tumatanggap ng kahilingan, nire-redirect ng proxy ang kahilingan, alam kung saan - ito mismo ang nagpapasiya nito.

FAQ sa arkitektura at gawain ng VKontakte

Sa kasong ito, ang proxy ay isang punto ng proteksyon laban sa pagkabigo ng serbisyo. Kung bumagal o bumagsak ang ilang makina, naiintindihan ito ng proxy at tumutugon nang naaayon sa panig ng kliyente. Ito ay nagpapahintulot sa iyo na alisin ang timeout - ang code ay hindi naghihintay para sa engine upang tumugon, ngunit nauunawaan na ito ay hindi gumagana at kailangan upang kumilos kahit papaano naiiba. Ang code ay dapat na handa para sa katotohanan na ang mga database ay hindi palaging gumagana.

Mga partikular na pagpapatupad

Minsan gusto pa rin nating magkaroon ng ilang uri ng hindi karaniwang solusyon bilang isang makina. Kasabay nito, napagpasyahan na huwag gamitin ang aming handa na rpc-proxy, na partikular na nilikha para sa aming mga makina, ngunit upang gumawa ng isang hiwalay na proxy para sa gawain.

Para sa MySQL, na mayroon pa rin kami dito at doon, gumagamit kami ng db-proxy, at para sa ClickHouse - Kuting bahay.

Ito ay gumagana sa pangkalahatan tulad nito. Mayroong isang tiyak na server, ito ay nagpapatakbo ng kPHP, Go, Python - sa pangkalahatan, anumang code na maaaring gumamit ng aming RPC protocol. Lokal na tumatakbo ang code sa isang RPC proxy - ang bawat server kung saan matatagpuan ang code ay nagpapatakbo ng sarili nitong lokal na proxy. Kapag hiniling, naiintindihan ng proxy kung saan pupunta.

FAQ sa arkitektura at gawain ng VKontakte

Kung ang isang makina ay gustong pumunta sa isa pa, kahit na ito ay isang kapitbahay, ito ay dumaan sa isang proxy, dahil ang kapitbahay ay maaaring nasa ibang data center. Ang makina ay hindi dapat umasa sa pag-alam sa lokasyon ng anumang bagay maliban sa sarili nito - ito ang aming karaniwang solusyon. Pero syempre may mga exception :)

Isang halimbawa ng isang TL-scheme ayon sa kung saan gumagana ang lahat ng makina.

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;

Ito ay isang binary protocol, ang pinakamalapit na analogue ay protobuf. Inilarawan ng schema ang mga opsyonal na field, kumplikadong uri - mga extension ng mga built-in na scalar, at mga query. Gumagana ang lahat ayon sa protocol na ito.

RPC sa TL sa TCP/UDP... UDP?

Mayroon kaming RPC protocol para sa pagpapatupad ng mga kahilingan sa engine na tumatakbo sa itaas ng TL scheme. Gumagana ang lahat sa isang koneksyong TCP/UDP. Naiintindihan ang TCP, ngunit bakit kailangan natin ng UDP nang madalas?

Tumutulong ang UDP maiwasan ang problema ng isang malaking bilang ng mga koneksyon sa pagitan ng mga server. Kung ang bawat server ay may isang RPC proxy at, sa pangkalahatan, maaari itong pumunta sa anumang engine, kung gayon mayroong libu-libong mga koneksyon sa TCP bawat server. May karga, pero walang silbi. Sa kaso ng UDP ang problemang ito ay hindi umiiral.

Walang kalabisan TCP handshake. Ito ay isang karaniwang problema: kapag ang isang bagong engine o isang bagong server ay inilunsad, maraming mga koneksyon sa TCP ang naitatag nang sabay-sabay. Para sa maliit na magaan na kahilingan, halimbawa, UDP payload, lahat ng komunikasyon sa pagitan ng code at ng engine ay dalawang UDP packet: ang isa ay lumilipad sa isang direksyon, ang pangalawa sa kabilang direksyon. Isang round trip - at nakatanggap ang code ng tugon mula sa makina nang walang pakikipagkamay.

Oo, gumagana lang ang lahat na may napakaliit na porsyento ng pagkawala ng packet. Ang protocol ay may suporta para sa mga retransmit at timeout, ngunit kung marami tayong mawawala, makakakuha tayo ng halos TCP, na hindi kapaki-pakinabang. Hindi kami nagtutulak ng UDP sa mga karagatan.

Mayroon kaming libu-libong ganoong mga server, at pareho ang scheme: isang pakete ng mga makina ang naka-install sa bawat pisikal na server. Karamihan sa mga ito ay single-threaded upang tumakbo nang mabilis hangga't maaari nang hindi nakaharang, at hinahati bilang mga single-threaded na solusyon. Kasabay nito, wala kaming mas maaasahan kaysa sa mga makinang ito, at maraming pansin ang binabayaran sa patuloy na pag-iimbak ng data.

Patuloy na pag-iimbak ng data

Ang mga makina ay nagsusulat ng mga binlog. Ang binlog ay isang file sa dulo kung saan ang isang kaganapan para sa pagbabago sa estado o data ay idinagdag. Sa iba't ibang mga solusyon ito ay tinatawag na naiiba: binary log, Wal, AOF, ngunit ang prinsipyo ay pareho.

Upang pigilan ang makina na muling basahin ang buong binlog sa loob ng maraming taon kapag nag-restart, sumulat ang mga makina mga snapshot - kasalukuyang estado. Kung kinakailangan, basahin muna nila ito, at pagkatapos ay tapusin ang pagbabasa mula sa binlog. Ang lahat ng binlog ay nakasulat sa parehong binary na format - ayon sa TL scheme, upang ang mga admin ay mapangasiwaan ang mga ito nang pantay-pantay sa kanilang mga tool. Walang ganoong pangangailangan para sa mga snapshot. Mayroong pangkalahatang header na nagsasaad kung kaninong snapshot ang int, magic ng makina, at kung aling katawan ang hindi mahalaga sa sinuman. Ito ay isang problema sa makina na nagtala ng snapshot.

Mabilis kong ilalarawan ang prinsipyo ng pagpapatakbo. Mayroong isang server kung saan tumatakbo ang makina. Nagbukas siya ng bagong walang laman na binlog para sa pagsusulat at nagsusulat ng kaganapan para sa pagbabago nito.

FAQ sa arkitektura at gawain ng VKontakte

Sa ilang mga punto, nagpasya siyang kumuha ng snapshot sa kanyang sarili, o makakatanggap siya ng signal. Lumilikha ang server ng bagong file, isusulat ang buong estado nito dito, idinadagdag ang kasalukuyang laki ng binlog - offset - sa dulo ng file, at patuloy na sumulat. Ang isang bagong binlog ay hindi nalikha.

FAQ sa arkitektura at gawain ng VKontakte

Sa ilang mga punto, kapag nag-restart ang makina, magkakaroon ng parehong binlog at snapshot sa disk. Binabasa ng makina ang buong snapshot at itinataas ang estado nito sa isang tiyak na punto.

FAQ sa arkitektura at gawain ng VKontakte

Binabasa ang posisyon sa oras na ginawa ang snapshot at ang laki ng binlog.

FAQ sa arkitektura at gawain ng VKontakte

Binabasa ang dulo ng binlog upang makuha ang kasalukuyang estado at magpatuloy sa pagsusulat ng mga karagdagang kaganapan. Ito ay isang simpleng pamamaraan; lahat ng aming mga makina ay gumagana ayon dito.

Pagtitiklop ng data

Bilang resulta, ang pagtitiklop ng data sa aming nakabatay sa pahayag β€” sumusulat kami sa binlog walang anumang pagbabago sa pahina, ngunit lalo baguhin ang mga kahilingan. Katulad sa kung ano ang dumarating sa network, bahagyang binago.

Ang parehong pamamaraan ay ginagamit hindi lamang para sa pagtitiklop, kundi pati na rin upang lumikha ng mga backup. Mayroon kaming makina - isang master ng pagsulat na nagsusulat sa binlog. Sa anumang iba pang lugar kung saan itinatakda ito ng mga admin, kinopya ang binlog na ito, at iyon lang - mayroon kaming backup.

FAQ sa arkitektura at gawain ng VKontakte

Kung kailangan replika ng pagbabasaUpang bawasan ang pag-load ng pagbabasa ng CPU, inilulunsad lamang ang makina ng pagbabasa, na nagbabasa sa dulo ng binlog at lokal na isinasagawa ang mga utos na ito.

Ang lag dito ay napakaliit, at posibleng malaman kung gaano katagal ang replica sa likod ng master.

Pagbabahagi ng data sa RPC proxy

Paano gumagana ang sharding? Paano naiintindihan ng proxy kung saang cluster shard ipapadala? Ang code ay hindi nagsasabi: "Ipadala para sa 15 shards!" - hindi, ginagawa ito ng proxy.

Ang pinakasimpleng scheme ay firstint β€” ang unang numero sa kahilingan.

get(photo100_500) => 100 % N.

Ito ay isang halimbawa para sa isang simpleng memcached text protocol, ngunit, siyempre, ang mga query ay maaaring maging kumplikado at structured. Kinukuha ng halimbawa ang unang numero sa query at ang natitira kapag hinati sa laki ng cluster.

Ito ay kapaki-pakinabang kapag gusto naming magkaroon ng data locality ng isang entity. Sabihin nating ang 100 ay isang user o group ID, at gusto naming ang lahat ng data ng isang entity ay nasa isang shard para sa mga kumplikadong query.

Kung wala kaming pakialam kung paano kumalat ang mga kahilingan sa buong cluster, may isa pang opsyon - Hashing ang buong shard.

hash(photo100_500) => 3539886280 % N

Nakukuha din namin ang hash, ang natitira sa dibisyon at ang shard na numero.

Gumagana lang ang dalawang opsyong ito kung handa tayo sa katotohanang kapag pinalaki natin ang laki ng cluster, hahatiin natin ito o dagdagan ng maraming beses. Halimbawa, mayroon kaming 16 na shards, wala kaming sapat, gusto namin ng higit pa - maaari kaming ligtas na makakuha ng 32 nang walang downtime. Kung gusto naming dagdagan hindi multiple, magkakaroon ng downtime, dahil hindi namin magagawang tumpak na hatiin ang lahat nang walang pagkalugi. Ang mga opsyon na ito ay kapaki-pakinabang, ngunit hindi palaging.

Kung kailangan naming magdagdag o mag-alis ng arbitrary na bilang ng mga server, ginagamit namin Pare-parehong pag-hash sa ring a la Ketama. Ngunit sa parehong oras, ganap naming nawawala ang lokalidad ng data; kailangan naming pagsamahin ang kahilingan sa cluster upang ang bawat piraso ay magbalik ng sarili nitong maliit na tugon, at pagkatapos ay pagsamahin ang mga tugon sa proxy.

May mga super-specific na kahilingan. Mukhang ganito: natatanggap ng proxy ng RPC ang kahilingan, tinutukoy kung aling cluster ang pupuntahan at tinutukoy ang shard. Pagkatapos ay mayroong alinman sa pagsulat masters, o, kung ang cluster ay may replica support, ito ay nagpapadala sa isang replica on demand. Ginagawa ng proxy ang lahat ng ito.

FAQ sa arkitektura at gawain ng VKontakte

Mga log

Nagsusulat kami ng mga log sa maraming paraan. Ang pinaka-halata at simple ay magsulat ng mga log sa memcache.

ring-buffer: prefix.idx = line

Mayroong isang key prefix - ang pangalan ng log, isang linya, at mayroong laki ng log na ito - ang bilang ng mga linya. Kumuha kami ng random na numero mula 0 hanggang sa bilang ng mga linyang minus 1. Ang susi sa memcache ay isang prefix na pinagsama sa random na numerong ito. I-save namin ang linya ng log at ang kasalukuyang oras sa halaga.

Kapag kinakailangan na basahin ang mga tala, isinasagawa namin Multi Get lahat ng mga susi, pinagsunod-sunod ayon sa oras, at sa gayon ay makakuha ng log ng produksyon sa real time. Ang scheme ay ginagamit kapag kailangan mong i-debug ang isang bagay sa produksyon sa real time, nang walang sinisira ang anumang bagay, nang hindi humihinto o nagpapahintulot sa trapiko sa iba pang mga makina, ngunit ang log na ito ay hindi nagtatagal.

Para sa maaasahang pag-iimbak ng mga log mayroon kaming makina log-engine. Ito ay tiyak kung bakit ito nilikha at malawakang ginagamit sa isang malaking bilang ng mga kumpol. Ang pinakamalaking kumpol na alam kong nag-iimbak ng 600 TB ng mga naka-pack na log.

Luma na ang makina, may mga kumpol na 6-7 years old na. May mga problema dito na sinusubukan naming lutasin, halimbawa, nagsimula kaming aktibong gumamit ng ClickHouse upang mag-imbak ng mga log.

Pagkolekta ng mga log sa ClickHouse

Ipinapakita ng diagram na ito kung paano tayo pumasok sa ating mga makina.

FAQ sa arkitektura at gawain ng VKontakte

Mayroong code na lokal na napupunta sa pamamagitan ng RPC sa RPC-proxy, at naiintindihan nito kung saan pupunta sa engine. Kung gusto naming magsulat ng mga log sa ClickHouse, kailangan naming baguhin ang dalawang bahagi sa scheme na ito:

  • palitan ang ilang makina ng ClickHouse;
  • palitan ang RPC proxy, na hindi ma-access ang ClickHouse, ng ilang solusyon na magagawa, at sa pamamagitan ng RPC.

Ang makina ay simple - pinapalitan namin ito ng isang server o isang kumpol ng mga server na may ClickHouse.

At upang pumunta sa ClickHouse, ginawa namin KutingBahay. Kung diretso tayo mula sa KittenHouse patungo sa ClickHouse, hindi ito makakayanan. Kahit na walang mga kahilingan, nagdaragdag ito mula sa mga koneksyon sa HTTP ng isang malaking bilang ng mga makina. Para gumana ang scheme, sa isang server na may ClickHouse itinaas ang lokal na reverse proxy, na nakasulat sa paraang makatiis sa kinakailangang dami ng mga koneksyon. Maaari rin itong mag-buffer ng data sa loob ng sarili nitong medyo mapagkakatiwalaan.

FAQ sa arkitektura at gawain ng VKontakte

Minsan hindi namin nais na ipatupad ang RPC scheme sa hindi karaniwang mga solusyon, halimbawa, sa nginx. Samakatuwid, ang KittenHouse ay may kakayahang tumanggap ng mga log sa pamamagitan ng UDP.

FAQ sa arkitektura at gawain ng VKontakte

Kung ang nagpadala at tumatanggap ng mga log ay gumagana sa parehong makina, kung gayon ang posibilidad na mawala ang isang UDP packet sa loob ng lokal na host ay medyo mababa. Bilang isang kompromiso sa pagitan ng pangangailangang ipatupad ang RPC sa isang third-party na solusyon at pagiging maaasahan, ginagamit lang namin ang pagpapadala ng UDP. Babalik tayo sa scheme na ito mamaya.

Pagsubaybay

Mayroon kaming dalawang uri ng mga log: ang mga nakolekta ng mga administrator sa kanilang mga server at ang mga isinulat ng mga developer mula sa code. Tumutugma ang mga ito sa dalawang uri ng sukatan: sistema at produkto.

Mga sukatan ng system

Gumagana ito sa lahat ng aming mga server netdata, na nangongolekta ng mga istatistika at ipinapadala ang mga ito sa Graphite Carbon. Samakatuwid, ang ClickHouse ay ginagamit bilang isang sistema ng imbakan, at hindi Whisper, halimbawa. Kung kinakailangan, maaari kang direktang magbasa mula sa ClickHouse, o gamitin grafana para sa mga sukatan, graph at ulat. Bilang mga developer, mayroon kaming sapat na access sa Netdata at Grafana.

Mga sukatan ng produkto

Para sa kaginhawahan, nagsulat kami ng maraming bagay. Halimbawa, mayroong isang hanay ng mga ordinaryong pag-andar na nagbibigay-daan sa iyo na magsulat ng Mga Bilang, Mga UniqueCount na halaga sa mga istatistika, na ipinadala sa isang lugar pa.

statlogsCountEvent   ( β€˜stat_name’,            $key1, $key2, …)
statlogsUniqueCount ( β€˜stat_name’, $uid,    $key1, $key2, …)
statlogsValuetEvent  ( β€˜stat_name’, $value, $key1, $key2, …)

$stats = statlogsStatData($params)

Kasunod nito, maaari naming gamitin ang pag-uuri at pagpapangkat ng mga filter at gawin ang lahat ng gusto namin mula sa mga istatistika - bumuo ng mga graph, i-configure ang Watchdogs.

Nagsusulat kami nang husto maraming sukatan ang bilang ng mga kaganapan ay mula 600 bilyon hanggang 1 trilyon bawat araw. Gayunpaman, nais naming panatilihin ang mga ito hindi bababa sa ilang taonupang maunawaan ang mga uso sa mga sukatan. Ang pagsasama-sama ng lahat ay isang malaking problema na hindi pa natin nareresolba. Sasabihin ko sa iyo kung paano ito gumagana nitong mga nakaraang taon.

Mayroon kaming mga function na sumusulat ng mga sukatan na ito sa lokal na memcacheupang mabawasan ang bilang ng mga entry. Minsan sa maikling panahon ay inilunsad nang lokal stats-daemon kinokolekta ang lahat ng mga talaan. Susunod, pinagsasama ng demonyo ang mga sukatan sa dalawang layer ng mga server mga log-collectors, na pinagsasama-sama ang mga istatistika mula sa isang grupo ng aming mga makina upang ang layer sa likod ng mga ito ay hindi mamatay.

FAQ sa arkitektura at gawain ng VKontakte

Kung kinakailangan, maaari tayong sumulat nang direkta sa mga kolektor ng log.

FAQ sa arkitektura at gawain ng VKontakte

Ngunit ang pagsusulat mula sa code nang direkta sa mga collectors, na lumalampas sa stas-daemom, ay isang hindi magandang scalable na solusyon dahil pinapataas nito ang load sa collector. Ang solusyon ay angkop lamang kung sa ilang kadahilanan ay hindi namin maitaas ang memcache stats-daemon sa makina, o nag-crash ito at direkta kaming pumunta.

Susunod, pinagsama-sama ng mga log-collectors ang mga istatistika meowDB - ito ang aming database, na maaari ding mag-imbak ng mga sukatan.

FAQ sa arkitektura at gawain ng VKontakte

Pagkatapos ay maaari tayong gumawa ng binary na "near-SQL" na mga seleksyon mula sa code.

FAQ sa arkitektura at gawain ng VKontakte

Eksperimento

Noong tag-araw ng 2018, nagkaroon kami ng panloob na hackathon, at may ideya na subukang palitan ang pulang bahagi ng diagram ng isang bagay na maaaring mag-imbak ng mga sukatan sa ClickHouse. Mayroon kaming mga log sa ClickHouse - bakit hindi subukan ito?

FAQ sa arkitektura at gawain ng VKontakte

Nagkaroon kami ng scheme na nagsulat ng mga log sa pamamagitan ng KittenHouse.

FAQ sa arkitektura at gawain ng VKontakte

Nagpasya kami magdagdag ng isa pang "*Bahay" sa diagram, na makakatanggap ng eksaktong mga sukatan sa format habang isinusulat ng aming code ang mga ito sa pamamagitan ng UDP. Pagkatapos ang *Bahay na ito ay ginagawang mga insert, tulad ng mga log, na naiintindihan ng KittenHouse. Perpektong maihahatid niya ang mga log na ito sa ClickHouse, na dapat ay mababasa ang mga ito.

FAQ sa arkitektura at gawain ng VKontakte

Ang scheme na may memcache, stats-daemon at logs-collectors database ay pinalitan ng isang ito.

FAQ sa arkitektura at gawain ng VKontakte

Ang scheme na may memcache, stats-daemon at logs-collectors database ay pinalitan ng isang ito.

  • Mayroong isang dispatch mula sa code dito, na lokal na nakasulat sa StatsHouse.
  • Nagsusulat ang StatsHouse ng mga sukatan ng UDP, na na-convert na sa mga pagsingit ng SQL, sa KittenHouse sa mga batch.
  • Ipinapadala sila ng KittenHouse sa ClickHouse.
  • Kung gusto naming basahin ang mga ito, pagkatapos ay basahin namin ang mga ito sa pamamagitan ng pag-bypass sa StatsHouse - direkta mula sa ClickHouse gamit ang regular na SQL.

Ito pa ba isang eksperimento, ngunit gusto namin kung paano ito lumalabas. Kung aayusin natin ang mga problema sa scheme, kung gayon marahil ay ganap tayong lumipat dito. Sa personal, umaasa ako.

Ang pamamaraan hindi nakakatipid ng bakal. Mas kaunting mga server ang kailangan, ang mga lokal na stats-daemon at logs-collectors ay hindi kailangan, ngunit ang ClickHouse ay nangangailangan ng mas malaking server kaysa sa mga nasa kasalukuyang scheme. Mas kaunting mga server ang kailangan, ngunit dapat na mas mahal at mas malakas ang mga ito.

I-deploy

Una, tingnan natin ang pag-deploy ng PHP. Kami ay umuunlad sa pumunta: gamitin GitLab ΠΈ TeamCity para sa deployment. Ang mga sangay ng pag-unlad ay pinagsama sa master branch, mula sa master para sa pagsubok sila ay pinagsama sa pagtatanghal, at mula sa pagtatanghal sa produksyon.

Bago ang pag-deploy, ang kasalukuyang sangay ng produksyon at ang nauna ay kinuha, at ang mga diff file ay isinasaalang-alang sa kanila - mga pagbabago: nilikha, tinanggal, binago. Ang pagbabagong ito ay naitala sa binlog ng isang espesyal na copyfast engine, na maaaring mabilis na magtiklop ng mga pagbabago sa aming buong server fleet. Ang ginagamit dito ay hindi direktang pangongopya, ngunit pagtitiklop ng tsismis, kapag ang isang server ay nagpadala ng mga pagbabago sa mga pinakamalapit na kapitbahay nito, sa mga kapitbahay nila, at iba pa. Nagbibigay-daan ito sa iyong i-update ang code sa sampu at mga yunit ng segundo sa buong fleet. Kapag naabot ng pagbabago ang lokal na replika, inilalapat nito ang mga patch na ito sa nito lokal na file system. Ang rollback ay isinasagawa din ayon sa parehong pamamaraan.

Marami rin kaming nagde-deploy ng kPHP at mayroon din itong sariling development sa pumunta ayon sa diagram sa itaas. Simula noon Binary ng HTTP server, pagkatapos ay hindi kami makakagawa ng diff - ang release binary ay tumitimbang ng daan-daang MB. Samakatuwid, mayroong isa pang pagpipilian dito - ang bersyon ay nakasulat sa binlog copyfast. Sa bawat build ay tumataas ito, at sa panahon ng rollback tumataas din ito. Bersyon kinopya sa mga server. Nakikita ng mga lokal na copyfast na may bagong bersyon na pumasok sa binlog, at sa pamamagitan ng parehong pagtitiklop ng tsismis ay kinukuha nila ang pinakabagong bersyon ng binary para sa kanilang sarili, nang hindi napapagod ang aming master server, ngunit maingat na ikinakalat ang load sa buong network. Ang sumusunod magandang muling paglulunsad para sa bagong bersyon.

Para sa aming mga makina, na mahalagang binary din, ang scheme ay halos magkapareho:

  • git master branch;
  • binary sa .deb;
  • ang bersyon ay nakasulat sa binlog copyfast;
  • kinopya sa mga server;
  • ang server ay kumukuha ng sariwang .dep;
  • dpkg -i;
  • magandang muling paglulunsad sa bagong bersyon.

Ang pagkakaiba ay ang aming binary ay naka-package sa mga archive .deb, at kapag pumping out sila dpkg -i ay inilalagay sa sistema. Bakit naka-deploy ang kPHP bilang isang binary, at ang mga engine ay naka-deploy bilang dpkg? Nangyari ito sa ganoong paraan. Gumagana ito - huwag hawakan ito.

Mga kapaki-pakinabang na link:

Si Alexey Akulovich ay isa sa mga tumutulong, bilang bahagi ng Komite ng Programa PHP Russia sa ika-17 ng Mayo ay magiging pinakamalaking kaganapan para sa mga developer ng PHP sa mga nakaraang panahon. Tingnan kung ano ang isang cool na PC mayroon kami, ano mga nagsasalita (dalawa sa kanila ang bumubuo ng PHP core!) - parang isang bagay na hindi mo mapapalampas kung sumulat ka ng PHP.

Pinagmulan: www.habr.com

Magdagdag ng komento