A VKontakte létrehozásának története a Wikipédián található, maga Pavel mondta el. Úgy tűnik, már mindenki ismeri. A HighLoad++ Pavel webhely belső részeiről, architektúrájáról és szerkezetéről mondta még 2010-ben. Azóta sok szerver kiszivárgott, így frissítjük az információkat: felboncoljuk, kiszedjük a belsejét, lemérjük, és megnézzük a VK készüléket műszaki oldalról.
Alekszej Akulovics (AterCattus) háttérfejlesztő a VKontakte csapatában. A jelentés átirata egy kollektív válasz a gyakran feltett kérdésekre a platform működésével, az infrastruktúrával, a szerverekkel és a köztük lévő interakcióval kapcsolatban, de nem a fejlesztéssel kapcsolatban. a vasról. Külön-külön az adatbázisokról és arról, hogy mi van helyette a VK-val, a naplók gyűjtéséről és a teljes projekt egészének felügyeletéről. Részletek a vágás alatt.
Több mint négy éve foglalkozom mindenféle backenddel kapcsolatos feladattal.
Média feltöltése, tárolása, feldolgozása, terjesztése: videó, élő közvetítés, hang, fotók, dokumentumok.
Infrastruktúra, platform, fejlesztői figyelés, naplók, regionális gyorsítótárak, CDN, saját RPC protokoll.
Integráció külső szolgáltatásokkal: push értesítések, külső hivatkozások elemzése, RSS feed.
Kollégáinak segítése különböző kérdésekben, amelyekre a válaszok ismeretlen kódba merülni kell.
Ez idő alatt az oldal számos összetevőjébe beleszóltam. Ezt az élményt szeretném megosztani.
Általános építészet
Minden, mint általában, egy szerverrel vagy szervercsoporttal kezdődik, amely fogadja a kéréseket.
Elülső szerver
Az elülső szerver HTTPS-en, RTMP-n és WSS-en keresztül fogad kéréseket.
HTTPS - ezek a webhely fő és mobil webes verzióira vonatkozó kérések: vk.com és m.vk.com, valamint API-nk egyéb hivatalos és nem hivatalos kliensei: mobil kliensek, messengerek. Van egy fogadásunk RTMP-forgalom élő adásokhoz külön frontszerverekkel és WSS- kapcsolatok a Streaming API-hoz.
A szervereken lévő HTTPS és WSS esetén megéri nginx. Az RTMP adásoknál nemrégiben saját megoldásunkra váltottunk kive, de ez meghaladja a jelentés kereteit. A hibatűrés érdekében ezek a szerverek közös IP-címeket hirdetnek, és csoportosan működnek, így ha valamelyik szerveren probléma adódik, a felhasználói kérések ne vesszenek el. A HTTPS és a WSS esetében ugyanezek a szerverek titkosítják a forgalmat, hogy a CPU-terhelés egy részét magukra vegyék.
A WSS-ről és az RTMP-ről nem fogunk tovább beszélni, hanem csak a szabványos HTTPS-kérésekről, amelyek általában egy webprojekthez kapcsolódnak.
háttér
Az előlap mögött általában háttérkiszolgálók találhatók. Feldolgozzák azokat a kéréseket, amelyeket az elülső szerver kap az ügyfelektől.
Ezt kPHP szerverek, amelyen a HTTP démon fut, mert a HTTPS már visszafejtve van. A kPHP egy szerver, amelyen fut prefork modellek: elindít egy mesterfolyamatot, egy csomó gyerek feldolgozást, lehallgató socketeket ad át nekik, és ők feldolgozzák a kéréseiket. Ebben az esetben a folyamatok nem indulnak újra a felhasználótól érkező egyes kérések között, hanem egyszerűen visszaállítják állapotukat az eredeti nulla értékű állapotra – kérésről kérésre, az újraindítás helyett.
Terhelés-elosztás
Az összes háttérrendszerünk nem egy hatalmas gépkészlet, amely bármilyen kérést képes feldolgozni. Mi őket külön csoportokra osztva: általános, mobil, api, videó, stádium... A probléma egy külön gépcsoportnál nem érinti az összes többit. A videóval kapcsolatos problémák esetén a zenét hallgató felhasználó nem is tud a problémákról. Hogy melyik háttérrendszerre küldje a kérést, azt az előlapon lévő nginx dönti el a konfigurációnak megfelelően.
Metrikagyűjtés és kiegyensúlyozás
Ahhoz, hogy megértsük, hány autóra van szükségünk az egyes csoportokban, mi ne hagyatkozzon a QPS-re. A háttérprogramok különbözőek, eltérő kérésekkel rendelkeznek, minden kérésnek más a QPS kiszámításának bonyolultsága. Ezért mi a szerver egészén – a CPU-n és a perf-en – a terhelés fogalmával működünk.
Több ezer ilyen szerverünk van. Minden fizikai szerver egy kPHP csoportot futtat az összes mag újrahasznosítására (mivel a kPHP egyszálú).
Tartalomszerver
A CS vagy Content Server egy tárhely. A CS egy kiszolgáló, amely tárolja a fájlokat, és feldolgozza a feltöltött fájlokat és mindenféle háttérszinkron feladatot, amelyet a fő webes felület hozzárendel hozzá.
Több tízezer fizikai szerverünk van, amelyek fájlokat tárolnak. A felhasználók szeretnek fájlokat feltölteni, mi pedig szeretjük tárolni és megosztani őket. Néhány ilyen szervert speciális pu/pp szerverek zárnak le.
pu/pp
Ha megnyitotta a hálózat lapot a VK-ban, akkor látta a pu/pp-t.
Mi az a pu/pp? Ha egyik szervert a másik után zárjuk be, akkor két lehetőség van a fájl feltöltésére és letöltésére a bezárt szerverre: közvetlenül keresztül http://cs100500.userapi.com/path vagy közbenső szerveren keresztül - http://pu.vk.com/c100500/path.
A Pu a fotófeltöltés történelmi neve, a pp pedig a fotóproxy. Vagyis az egyik szerver a fényképek feltöltésére szolgál, a másik pedig a feltöltésre. Most már nem csak a fényképek töltődnek be, hanem a név is megmaradt.
Ezek a szerverek megszakítja a HTTPS munkamenetekethogy eltávolítsa a processzorterhelést a tárolóból. Továbbá, mivel a felhasználói fájlokat ezeken a szervereken dolgozzák fel, minél kevésbé érzékeny információkat tárolnak ezeken a gépeken, annál jobb. Például HTTPS titkosítási kulcsok.
Mivel a gépeket a többi gépünk zárja, megengedhetjük magunknak, hogy ne adjunk nekik „fehér” külső IP-t, ill. adj "szürkét". Így spóroltunk az IP-készleten, és garantáltan megvédjük a gépeket a külső hozzáféréstől – egyszerűen nincs IP, amibe be lehetne jutni.
Rugalmasság megosztott IP-címeken. A hibatűrés szempontjából a séma ugyanúgy működik - több fizikai szervernek közös fizikai IP-je van, és az előttük lévő hardver választja ki, hogy hova küldje a kérést. A többi lehetőségről később beszélek.
A vitatott pont az, hogy ebben az esetben a kliens kevesebb kapcsolatot tart fenn. Ha több gépnek ugyanaz az IP-címe – ugyanazzal a gazdagéppel: pu.vk.com vagy pp.vk.com, akkor az ügyfélböngésző korlátozza az egy géphez intézett egyidejű kérések számát. De a mindenütt jelenlévő HTTP/2 idején úgy gondolom, hogy ez már nem annyira releváns.
A séma nyilvánvaló hátránya, hogy muszáj pumpálja az összes forgalmat, amely egy másik szerveren keresztül megy a tárolóba. Mivel a forgalmat gépeken keresztül pumpáljuk, még nem tudjuk ugyanazt a sémát használni a nagy forgalmat, például a videót. Közvetlenül továbbítjuk - külön közvetlen kapcsolat a különálló tárolókhoz, kifejezetten videóhoz. A könnyebb tartalmakat proxyn keresztül továbbítjuk.
Nem sokkal ezelőtt megkaptuk a proxy továbbfejlesztett verzióját. Most elmondom, miben különböznek a szokásostól, és miért van erre szükség.
nap
2017 szeptemberében az Oracle, amely korábban megvásárolta a Sun-t, rengeteg Sun alkalmazottat bocsátott el. Elmondhatjuk, hogy ebben a pillanatban a cég megszűnt. Az új rendszer nevének kiválasztásakor rendszergazdáink úgy döntöttek, hogy tisztelegnek a cég emléke előtt, és az új rendszert Sunnak nevezték el. Magunk között egyszerűen „napoknak” hívjuk.
pp volt néhány probléma. Csoportonként egy IP – nem hatékony gyorsítótár. Számos fizikai szerver közös IP-címen osztozik, és nincs mód annak szabályozására, hogy a kérés melyik szerverre kerüljön. Ezért, ha ugyanazt a fájlt különböző felhasználók keresik, akkor ha ezeken a kiszolgálókon van gyorsítótár, a fájl az egyes kiszolgálók gyorsítótárába kerül. Ez egy nagyon nem hatékony rendszer, de semmit sem lehet tenni.
Következésképpen - nem tudjuk feldarabolni a tartalmat, mert ehhez a csoporthoz nem tudunk konkrét szervert kiválasztani - közös IP-jük van. Bizonyos belső okok miatt is régiókban nem lehetett ilyen szervereket telepíteni. Csak Szentpéterváron álltak.
A napokkal megváltoztattuk a kiválasztási rendszert. Most megvan anycast útválasztás: dinamikus útválasztás, anycast, önellenőrző démon. Minden szervernek megvan a saját egyedi IP-címe, de közös alhálózata. Minden úgy van beállítva, hogy ha az egyik szerver meghibásodik, a forgalom automatikusan szétoszlik ugyanazon csoport többi szerverén. Most már lehetőség van egy adott szerver kiválasztására, nincs redundáns gyorsítótár, és a megbízhatóság nem változott.
Súly támogatás. Most már megengedhetjük magunknak, hogy igény szerint különböző teljesítményű gépeket szereljünk be, illetve átmeneti problémák esetén a működő „napok” súlyát is cseréljük, hogy csökkentsük a terhelést, hogy „pihenjenek” és újra működjenek.
Megosztás tartalomazonosító szerint. Egy vicces dolog a felosztással kapcsolatban: általában azért tördeljük a tartalmat, hogy a különböző felhasználók ugyanazon a „napon” keresztül ugyanahhoz a fájlhoz menjenek, hogy közös gyorsítótáruk legyen.
Nemrég elindítottuk a „Clover” alkalmazást. Ez egy online kvíz élő adásban, ahol a műsorvezető kérdéseket tesz fel, a felhasználók pedig valós időben válaszolnak a lehetőségek kiválasztásával. Az alkalmazásban van egy chat, ahol a felhasználók cseveghetnek. Egyidejűleg csatlakozhat a közvetítéshez több mint 100 ezer ember. Mindannyian üzeneteket írnak, amelyeket minden résztvevőnek elküldenek, és egy avatar is érkezik az üzenethez. Ha 100 ezer ember jön egy avatárért egy „napon”, akkor az néha egy felhő mögé gurulhat.
Annak érdekében, hogy ellenálljunk az ugyanarra a fájlra irányuló kéréseknek, egy bizonyos típusú tartalomnál bekapcsolunk egy olyan hülye sémát, amely a régió összes elérhető „napján” szétosztja a fájlokat.
Nap belülről
Fordított proxy nginx-en, gyorsítótár a RAM-ban vagy a gyors Optane/NVMe lemezeken. Példa: http://sun4-2.userapi.com/c100500/path — hivatkozás a „nap”-hoz, amely a negyedik régióban, a második szervercsoportban található. Bezárja az elérési út fájlt, amely fizikailag az 100500 szerveren található.
Gyorsítótár
Hozzáadunk még egy csomópontot az építészeti sémánkhoz - a gyorsítótárazási környezethez.
Alább látható az elrendezési diagram regionális gyorsítótárak, körülbelül 20 van belőlük. Ezek azok a helyek, ahol gyorsítótárak és „napok” találhatók, amelyek magukon keresztül gyorsítótárazhatják a forgalmat.
Ez a multimédiás tartalom gyorsítótárazása; itt nem tárolódnak felhasználói adatok – csak zenék, videók, fényképek.
A felhasználó régiójának meghatározásához mi a régiókban bejelentett BGP hálózati előtagokat gyűjtjük. Backback esetén a geoip adatbázist is elemezni kell, ha nem találtuk meg az IP-t előtagok alapján. A régiót a felhasználó IP-címe alapján határozzuk meg. A kódban megtekinthetjük a felhasználó egy vagy több régióját - azokat a pontokat, amelyekhez földrajzilag a legközelebb van.
Hogyan működik?
A fájlok népszerűségét régiónként számoljuk. Számos regionális gyorsítótár található, ahol a felhasználó tartózkodik, és egy fájlazonosító – ezt a párat vesszük, és minden letöltésnél növeljük az értékelést.
Ugyanakkor a démonok - a régiókban lévő szolgáltatások - időről időre jönnek az API-hoz, és azt mondják: „Ilyen és ilyen gyorsítótár vagyok, adjon listát a régióm legnépszerűbb fájljairól, amelyek még nincsenek rajtam. ” Az API egy rakás fájlt szállít minősítés szerint rendezve, a démon letölti őket, elviszi a régiókba, és onnan kézbesíti a fájlokat. Ez az alapvető különbség a pu/pp és a Sun között a cache-ekből: azonnal átadják magukon a fájlt, még akkor is, ha ez a fájl nincs a cache-ben, és a cache először letölti a fájlt magának, majd elkezdi visszaadni.
Ebben az esetben megkapjuk a tartalom közelebb kerül a felhasználókhoz és a hálózati terhelés szétosztása. Például csak a moszkvai gyorsítótárból osztunk ki 1 Tbit/s-nál többet csúcsidőben.
De vannak problémák - a cache szerverek nem gumiból vannak. A rendkívül népszerű tartalmak esetében néha nincs elég hálózat egy külön szerver számára. A cache szervereink 40-50 Gbit/s-osak, de van olyan tartalom, ami teljesen eltömíti az ilyen csatornát. A régióban népszerű fájlok egynél több példányának tárolása felé haladunk. Remélem, hogy az év végéig megvalósítjuk.
Megnéztük az általános építészetet.
Elülső szerverek, amelyek fogadják a kéréseket.
A kéréseket feldolgozó háttérprogramok.
Kétféle proxy által lezárt tárolók.
Regionális gyorsítótárak.
Mi hiányzik ebből a diagramból? Természetesen az adatbázisok, amelyekben adatokat tárolunk.
Adatbázisok vagy motorok
Ezeket nem adatbázisoknak, hanem motoroknak – motoroknak hívjuk, mert gyakorlatilag nem rendelkezünk az általánosan elfogadott értelemben vett adatbázisokkal.
Ez szükséges intézkedés.. Ez azért történt, mert 2008-2009-ben, amikor a VK népszerűsége robbanásszerűen nőtt, a projekt teljes egészében a MySQL-en és a Memcache-en működött, és voltak problémák. A MySQL szeretett összeomlani és megrontani a fájlokat, ami után nem állt helyre, és a Memcache teljesítménye fokozatosan romlott, és újra kellett indítani.
Kiderült, hogy az egyre népszerűbb projektnek volt állandó tárolója, ami rontja az adatokat, és gyorsítótár, ami lelassul. Ilyen körülmények között nehéz egy növekvő projektet kidolgozni. Elhatároztuk, hogy megpróbáljuk újraírni azokat a kritikus dolgokat, amelyekre a projekt a saját kerékpárjainkra összpontosított.
A megoldás sikeres volt. Erre adott volt a lehetőség, és egyben extrém szükség is, mert más skálázási módok akkor még nem léteztek. Nem volt egy csomó adatbázis, a NoSQL még nem létezett, csak MySQL, Memcache, PostrgreSQL volt – és ennyi.
Univerzális működés. A fejlesztést C fejlesztői csapatunk vezette, és minden következetesen történt. Motortól függetlenül mindegyik megközelítőleg azonos fájlformátumot írt a lemezre, ugyanazokat az indítási paramétereket, azonos módon dolgozták fel a jeleket, és megközelítőleg ugyanúgy viselkedtek élhelyzetek és problémák esetén. A motorok számának növekedésével az adminisztrátorok számára kényelmesebb a rendszer kezelése - nincs állatkert, amelyet karban kell tartani, és minden új, harmadik féltől származó adatbázis kezelését újra kell tanulniuk, ami lehetővé tette a gyors és kényelmes bővítést. számukat.
A motorok típusai
A csapat jó néhány motort írt. Íme néhány ezek közül: barát, tippek, kép, ipdb, levelek, listák, naplók, memcached, meowdb, hírek, nostradamus, fénykép, lejátszási listák, pmemcached, sandbox, keresés, tárhely, kedvelések, feladatok,…
Minden olyan feladathoz, amely meghatározott adatstruktúrát igényel, vagy atipikus kéréseket dolgoz fel, a C-csapat új motort ír. Miért ne.
Külön motorunk van memcached, ami a szokásoshoz hasonló, de egy rakás finomsággal, és ami nem lassít. Nem ClickHouse, de működik is. Külön rendelhető pmemcached - Van kitartóan memcached, amely a lemezen is képes adatokat tárolni, ráadásul a RAM-ba is belefér, hogy ne veszítsen adatot újraindításkor. Különféle motorok állnak rendelkezésre az egyes feladatokhoz: sorok, listák, készletek - minden, amit projektünk igényel.
Klaszterek
A kód szempontjából nincs szükség arra, hogy a motorokat vagy adatbázisokat folyamatokként, entitásokként vagy példányokként tekintsük. A kód kifejezetten klaszterekkel, motorcsoportokkal működik - klaszterenként egy típus. Tegyük fel, hogy van egy memcached fürt – ez csak egy gépcsoport.
A kódnak egyáltalán nem kell tudnia a kiszolgálók fizikai helyét, méretét vagy számát. Egy bizonyos azonosító használatával megy a klaszterbe.
Ahhoz, hogy ez működjön, hozzá kell adni egy további entitást, amely a kód és a motorok között található - meghatalmazott.
RPC proxy
Meghatalmazott összekötő busz, amelyen szinte az egész oldal fut. Ugyanakkor van nincs szolgáltatás felfedezés — ehelyett van egy konfig ehhez a proxyhoz, amely ismeri a fürt összes fürtjének és minden szilánkjának helyét. Ezt csinálják az adminok.
A programozókat egyáltalán nem érdekli, hogy mennyibe, hol és mibe kerül – csak a klaszterhez mennek. Ez sok mindent lehetővé tesz számunkra. Kérés fogadásakor a proxy átirányítja a kérést, tudva, hogy hova - ezt maga határozza meg.
Ebben az esetben a proxy egy védelmi pont a szolgáltatás meghibásodása ellen. Ha valamelyik motor lelassul vagy összeomlik, akkor a proxy megérti ezt, és ennek megfelelően reagál az ügyféloldalra. Ez lehetővé teszi az időtúllépés eltávolítását - a kód nem várja meg a motor válaszát, hanem megérti, hogy nem működik, és valahogy másképp kell viselkednie. A kódot fel kell készíteni arra, hogy az adatbázisok nem mindig működnek.
Konkrét megvalósítások
Néha még mindig nagyon szeretnénk valami nem szabványos megoldást motorként. Ezzel egyidejűleg úgy döntöttek, hogy nem a kifejezetten motorjaink számára készített, kész rpc-proxynkat használjuk, hanem külön proxyt készítünk a feladathoz.
A MySQL-hez, ami még mindig van itt-ott, db-proxyt használunk, a ClickHouse-hoz pedig - Cicaház.
Általában így működik. Van egy bizonyos szerver, azon fut a kPHP, Go, Python – általában minden olyan kód, amely használhatja az RPC protokollunkat. A kód helyileg egy RPC proxyn fut – minden kiszolgáló, ahol a kód található, saját helyi proxyját futtatja. Kérésre a meghatalmazott megérti, hová kell mennie.
Ha az egyik motor egy másikhoz akar menni, még ha szomszéd is, akkor egy proxy-n keresztül megy, mert lehet, hogy a szomszéd egy másik adatközpontban van. A motornak nem szabad arra hagyatkoznia, hogy önmagán kívül másnak tudja a helyét – ez a mi standard megoldásunk. De persze vannak kivételek :)
Példa egy TL-sémára, amely szerint minden motor működik.
Ez egy bináris protokoll, amelynek a legközelebbi analógja protobuf. A séma opcionális mezőket, összetett típusokat – a beépített skalárok kiterjesztéseit és lekérdezéseket – írja elő. Minden ennek a protokollnak megfelelően működik.
RPC TL felett TCP/UDP felett… UDP?
Van egy RPC protokollunk a motorkérelmek végrehajtására, amely a TL sémán felül fut. Mindez TCP/UDP kapcsolaton keresztül működik. A TCP érthető, de miért van szükségünk gyakran az UDP-re?
Az UDP segít elkerülhető a szerverek közötti nagyszámú kapcsolat problémája. Ha minden szerver rendelkezik RPC-proxyval, és általában bármelyik motorhoz tud kapcsolódni, akkor szerverenként több tízezer TCP-kapcsolat létezik. Terhelés van, de használhatatlan. UDP esetén ez a probléma nem áll fenn.
Nincs redundáns TCP kézfogás. Ez egy tipikus probléma: amikor új motort vagy új szervert indítanak el, egyszerre sok TCP-kapcsolat jön létre. Kis könnyed kérések esetén, például UDP hasznos terhelés esetén, a kód és a motor között minden kommunikáció megtörténik két UDP csomag: az egyik az egyik, a másik a másik irányba repül. Egy oda-vissza út - és a kód kézfogás nélkül kapott választ a motortól.
Igen, minden csak működik nagyon kis százalékos csomagvesztés mellett. A protokoll támogatja az újraküldést és az időtúllépést, de ha sokat veszítünk, szinte TCP-t kapunk, ami nem előnyös. Az UDP-t nem hajtjuk át az óceánokon.
Több ezer ilyen szerverünk van, és a séma ugyanaz: minden fizikai szerverre egy-egy motorcsomag van telepítve. Többnyire egyszálasak, hogy a lehető leggyorsabban, blokkolás nélkül futhassanak, és egyszálú megoldásként vannak szétdarabolva. Ugyanakkor ezeknél a motoroknál nincs megbízhatóbb, és nagy figyelmet fordítanak a tartós adattárolásra.
Állandó adattárolás
A motorok binlogokat írnak. A binlog egy fájl, amelynek végén egy állapot- vagy adatváltozási esemény kerül hozzáadásra. Különböző megoldásokban másképpen hívják: bináris napló, WAL, AOF, de az elv ugyanaz.
Hogy a motor ne olvassa újra a teljes binlogot sok éven át újraindításkor, a motorok írnak pillanatképek – aktuális állapot. Ha szükséges, először abból olvasnak, majd a binlogból fejezik be az olvasást. Minden binlog ugyanabban a bináris formátumban van írva - a TL séma szerint, hogy az adminisztrátorok egyformán kezelhessék őket eszközeikkel. Nincs szükség pillanatképekre. Van egy általános fejléc, amely jelzi, hogy kinek a pillanatképe az int, a motor varázsa, és melyik test nem fontos senkinek. Ez a pillanatfelvételt rögzítő motorral van probléma.
Gyorsan leírom a működési elvet. Van egy szerver, amin a motor fut. Új üres binlogot nyit meg íráshoz, és eseményt ír a változtatáshoz.
Egy ponton vagy úgy dönt, hogy saját maga készít egy pillanatfelvételt, vagy jelet kap. A szerver létrehoz egy új fájlt, beleírja a teljes állapotát, a fájl végéhez hozzáfűzi az aktuális binlog méretet - offset - és folytatja az írást. Új binlog nem jön létre.
Egy bizonyos ponton, amikor a motor újraindul, egy binlog és egy pillanatkép is lesz a lemezen. A motor beolvassa a teljes pillanatképet, és egy bizonyos ponton megemeli az állapotát.
Beolvassa azt a pozíciót, amely a pillanatkép létrehozásakor volt, és a binlog méretét.
Beolvassa a binlog végét, hogy megkapja az aktuális állapotot, és folytatja a további események írását. Ez egy egyszerű séma, minden motorunk ennek megfelelően működik.
Adatreplikáció
Ennek eredményeként az adatok replikációja a mi nyilatkozat alapú — a binlogba nem akármilyen oldalváltozást írunk, hanem mégpedig változtatási kérelmek. Nagyon hasonló ahhoz, ami a hálózaton keresztül érkezik, csak kissé módosítva.
Ugyanezt a sémát nem csak a replikációhoz használják, hanem biztonsági másolatok készítéséhez. Van egy motorunk - egy írómesterünk, amely a binlogba ír. Bármilyen más helyen, ahol az adminisztrátor beállította, ez a binlog másolásra kerül, és ennyi – van egy biztonsági másolatunk.
Ha szükséges replika olvasásaA CPU olvasási terhelésének csökkentése érdekében egyszerűen elindul az olvasómotor, amely beolvassa a binlog végét, és helyileg végrehajtja ezeket a parancsokat.
A lemaradás itt nagyon kicsi, és ki lehet deríteni, hogy a replika mennyivel marad el a mestertől.
Adatfelosztás az RPC proxyban
Hogyan működik a sharding? Hogyan érti meg a proxy, hogy melyik fürt szilánkjára küldje el? A kódban nem szerepel: „Küldj 15 szilánkért!” - nem, ezt a meghatalmazott végzi.
A legegyszerűbb séma a firstint — a kérelem első száma.
get(photo100_500) => 100 % N.
Ez egy példa egy egyszerű memcached szöveges protokollra, de természetesen a lekérdezések lehetnek összetettek és strukturáltak. A példa a lekérdezés első számát veszi fel, a maradékot pedig a fürt méretével osztva.
Ez akkor hasznos, ha egyetlen entitás adathelyét szeretnénk elérni. Tegyük fel, hogy a 100 egy felhasználói vagy csoportazonosító, és azt szeretnénk, hogy egy entitás összes adata egy shardon legyen összetett lekérdezések esetén.
Ha nem törődünk azzal, hogy a kérések hogyan oszlanak el a fürtben, van egy másik lehetőség – az egész szilánk kivonatolása.
hash(photo100_500) => 3539886280 % N
Megkapjuk a hash-t, az osztás maradékát és a szilánkszámot is.
Mindkét lehetőség csak akkor működik, ha fel vagyunk készülve arra, hogy a klaszter méretének növelésekor felosztjuk, vagy többszörösére növeljük. Például 16 szilánkunk volt, nincs elég, többet akarunk - nyugodtan szerezhetünk 32-t leállás nélkül. Ha nem többszörösét akarjuk növelni, akkor leállások lesznek, mert nem tudunk mindent pontosan felosztani veszteség nélkül. Ezek a lehetőségek hasznosak, de nem mindig.
Ha tetszőleges számú szervert kell hozzáadnunk vagy eltávolítanunk, akkor ezt használjuk Következetes hash a gyűrűn a la Ketama. Ugyanakkor teljesen elveszítjük az adatok lokalizációját, össze kell vonnunk a kérést a fürttel, hogy minden egyes darab a saját kis válaszát adja vissza, majd a válaszokat összevonjuk a proxyval.
Vannak szuperspecifikus kérések. Így néz ki: Az RPC-proxy fogadja a kérést, meghatározza, hogy melyik fürthöz lépjen, és meghatározza a szilánkot. Aztán vannak írási mesterek, vagy ha a fürt rendelkezik replika támogatással, akkor kérésre küld egy replikának. A proxy teszi mindezt.
Naplók
A naplókat többféleképpen írjuk. A legnyilvánvalóbb és legegyszerűbb az naplók írása a memcache-be.
ring-buffer: prefix.idx = line
Van egy kulcs előtag - a napló neve, egy sor, és van ennek a naplónak a mérete - a sorok száma. Vegyünk egy véletlen számot 0-tól a sorok számának mínusz 1-ig. A memcache kulcsa egy ezzel a véletlenszámmal összefűzött előtag. Az értékhez mentjük a naplósort és az aktuális időt.
Ha szükséges a naplók leolvasása, akkor elvégezzük Multi Get minden kulcsot idő szerint rendezve, így valós időben kap egy gyártási naplót. A sémát akkor használják, ha valamit valós időben kell hibakeresnie a termelésben, anélkül, hogy bármit eltörne, anélkül, hogy leállítaná vagy engedélyezné a forgalmat más gépekhez, de ez a napló nem tart sokáig.
A rönkök megbízható tárolására motorral rendelkezünk rönk-motor. Pontosan ezért hozták létre, és széles körben használják számos klaszterben. Az általam ismert legnagyobb klaszter 600 TB csomagolt rönköt tárol.
Nagyon régi a motor, vannak már 6-7 éves klaszterek. Vannak vele problémák, amelyeket megpróbálunk megoldani, például elkezdtük aktívan használni a ClickHouse-t a naplók tárolására.
Naplógyűjtés a ClickHouse-ban
Ez a diagram megmutatja, hogyan lépünk be a motorjainkba.
Létezik olyan kód, amely helyileg az RPC-n keresztül megy az RPC-proxyhoz, és megérti, hová kell menni a motorhoz. Ha naplókat akarunk írni a ClickHouse-ban, két részt kell módosítanunk ebben a sémában:
cseréljen ki néhány motort ClickHouse-ra;
cserélje ki az RPC proxyt, amely nem tud hozzáférni a ClickHouse-hoz, valamilyen megoldásra, amely képes rá, és RPC-n keresztül.
A motor egyszerű - kicseréljük egy szerverre vagy egy szervercsoportra a ClickHouse-szal.
És hogy a ClickHouse-hoz menjünk, megtettük KittenHouse. Ha a KittenHouse-ból közvetlenül a ClickHouse-ba megyünk, az nem fog megbirkózni. Még kérések nélkül is rengeteg gép HTTP-kapcsolataiból adódik össze. A séma működéséhez egy ClickHouse-szal rendelkező szerveren helyi fordított proxy fel van emelve, amely úgy van megírva, hogy elbírja a szükséges csatlakozási mennyiségeket. Viszonylag megbízhatóan képes pufferelni is az adatokat magában.
Néha nem akarjuk megvalósítani az RPC sémát nem szabványos megoldásokban, például az nginxben. Ezért a KittenHouse képes naplókat fogadni UDP-n keresztül.
Ha a naplók küldője és címzettje ugyanazon a gépen dolgozik, akkor meglehetősen kicsi annak a valószínűsége, hogy egy UDP-csomag elveszik a helyi gazdagépen belül. Kompromisszumként az RPC harmadik féltől származó megoldásokban való megvalósításának szükségessége és a megbízhatóság között egyszerűen UDP-küldést használunk. Erre a sémára később visszatérünk.
megfigyelés
Kétféle naplónk van: azokat, amelyeket a rendszergazdák gyűjtenek össze a szervereiken, és azokat, amelyeket a fejlesztők írnak kódból. Kétféle mérőszámnak felelnek meg: rendszer és termék.
Rendszermetrikák
Minden szerverünkön működik netdata, amely statisztikákat gyűjt és elküldi a címre Grafit szén. Ezért a ClickHouse-t tárolórendszerként használják, és nem például a Whisper-t. Ha szükséges, közvetlenül a ClickHouse-ból olvashat, vagy használhatja grafana mérőszámokhoz, grafikonokhoz és jelentésekhez. Fejlesztőként elegendő hozzáférésünk van a Netdatához és a Grafanához.
Termékmutatók
A kényelem kedvéért sok mindent leírtunk. Például van egy sor közönséges függvény, amely lehetővé teszi, hogy Counts, UniqueCounts értékeket írjon be a statisztikákba, amelyeket elküldenek valahova tovább.
Ezt követően válogató és csoportosító szűrőket használhatunk, és mindent megtehetünk a statisztikákból, amit csak akarunk – grafikonokat készíthetünk, őrzőkutyákat konfigurálhatunk.
Nagyon írunk sok mérőszám az események száma napi 600 milliárdtól 1 billióig terjed. Ezeket azonban szeretnénk megtartani legalább pár évea mutatók trendjeinek megértéséhez. Mindezt összerakni egy nagy probléma, amelyet még nem sikerült megoldanunk. Elmondom, hogyan működött az elmúlt években.
Vannak függvényeink, amelyek ezeket a mérőszámokat írják a helyi memcache-bea bejegyzések számának csökkentése érdekében. Egyszer rövid időn belül helyben indult stats-daemon összegyűjti az összes rekordot. Ezután a démon a metrikákat két szerverrétegbe egyesíti rönk-gyűjtők, amely egy csomó gépünkről összesíti a statisztikákat, hogy a mögöttük lévő réteg ne haljon meg.
Ha szükséges, írhatunk közvetlenül a naplógyűjtőknek.
De a kódból közvetlenül a gyűjtőkre írás, a stas-daemom megkerülésével rosszul skálázható megoldás, mert növeli a gyűjtő terhelését. A megoldás csak akkor megfelelő, ha valamilyen oknál fogva nem tudjuk felemelni a memcache stats-daemont a gépen, vagy lefagyott és direkt mentünk.
Ezután a naplógyűjtők összevonják a statisztikákat miauDB - ez az adatbázisunk, amely mérőszámokat is tárolhat.
Ezután a kódból bináris „közel SQL” kijelöléseket végezhetünk.
Kísérlet
2018 nyarán volt egy belső hackathonunk, és felmerült az ötlet, hogy megpróbáljuk a diagram piros részét lecserélni valamire, ami mérőszámokat tárolhat a ClickHouse-ban. Vannak naplóink a ClickHouse-on – miért ne próbálná ki?
Volt egy sémánk, amely a KittenHouse-on keresztül írt naplókat.
Eldöntöttük adjunk hozzá egy másik „*Ház”-t a diagramhoz, amely pontosan abban a formátumban fogja megkapni a mérőszámokat, ahogyan a kódunk írja őket UDP-n keresztül. Aztán ez a *Ház betétekké alakítja őket, mint a rönkök, amiket a KittenHouse megért. Tökéletesen el tudja juttatni ezeket a naplókat a ClickHouse-nak, amelynek képesnek kell lennie elolvasni őket.
A memcache, stats-daemon és logs-collectors adatbázist tartalmazó sémát ez váltja fel.
A memcache, stats-daemon és logs-collectors adatbázist tartalmazó sémát ez váltja fel.
Itt van egy feladás a kódból, amely helyben van megírva a StatsHouse-ban.
A StatsHouse kötegekben írja a KittenHouse-ba a már SQL-beszúrásokká konvertált UDP-metrikákat.
A KittenHouse elküldi őket a ClickHouse-nak.
Ha el akarjuk olvasni őket, akkor a StatsHouse megkerülésével - közvetlenül a ClickHouse-ból, normál SQL-lel.
Még mindig kísérlet, de szeretjük, ahogy kiderül. Ha megoldjuk a problémákat a sémával, akkor talán teljesen áttérünk rá. Én személy szerint remélem.
A rendszer nem takarít meg vasat. Kevesebb szerverre van szükség, helyi stats-démonokra és naplógyűjtőkre nincs szükség, de a ClickHouse-hoz nagyobb szerverre van szükség, mint a jelenlegi rendszerben. Kevesebb szerverre van szükség, de drágábbnak és erősebbnek kell lenniük.
Telepítés
Először nézzük meg a PHP telepítését. ben fejlődünk csoportos it: használ GitLab и TeamCity bevetésre. A fejlesztési ágak beolvadnak a master ágba, a tesztelési mesterből a stagingbe, a stagingből pedig a termelésbe.
Üzembe helyezés előtt az aktuális és az előző termelési ágat veszik, és figyelembe veszik bennük a diff fájlokat - változások: létrehozva, törölve, módosítva. Ezt a változást egy speciális copyfast motor binlogjában rögzítjük, amely gyorsan képes replikálni a változtatásokat a teljes szerverflottánkra. Itt nem közvetlenül másolást használnak, hanem pletyka replikációja, amikor az egyik szerver elküldi a módosításokat a legközelebbi szomszédjainak, azokat a szomszédjaiknak stb. Ez lehetővé teszi a kód frissítését tíz és másodpercek alatt a teljes flottán. Amikor a változás eléri a helyi replikát, alkalmazza ezeket a javításokat annak helyi fájlrendszer. A visszaállítást is ugyanezen séma szerint hajtják végre.
A kPHP-t is sokat telepítjük, és saját fejlesztése is van csoportos it a fenti diagram szerint. Ettől kezdve HTTP szerver bináris, akkor nem tudunk diff-et előállítani - a kiadási bináris tömege több száz MB. Ezért van itt egy másik lehetőség - a verzióra van írva binlog copyfast. Minden felépítéssel növekszik, és a visszaállítás során is nő. Változat replikálva a szerverekre. A helyi copyfasták látják, hogy egy új verzió lépett be a binlogba, és ugyanazzal a pletyka-replikációval veszik át maguknak a bináris legfrissebb verzióját, anélkül, hogy fárasztanák főszerverünket, de gondosan szétosztják a terhelést a hálózaton. Ami ezután következik kecses újraindítás az új verzióhoz.
Motorjaink esetében, amelyek szintén alapvetően binárisak, a séma nagyon hasonló:
git master ág;
bináris be . Deb;
a verzió a binlog copyfastba van írva;
replikálva szerverekre;
a szerver kivesz egy friss .dep fájlt;
dpkg -i;
kecses újraindítás az új verzióra.
A különbség az, hogy a bináris fájlunk archívumban van csomagolva . Deb, és kiszivattyúzásakor azokat dpkg -i kerülnek a rendszerbe. Miért van a kPHP bináris, a motorok pedig dpkg formátumban? Így történt. Működik – ne nyúljon hozzá.
Alekszej Akulovics egyike azoknak, akik a Programbizottság tagjaként segítik PHP Oroszország május 17-én lesz az utóbbi idők legnagyobb eseménye a PHP fejlesztők számára. Nézd, milyen menő PC-nk van, milyen hangszórók (Kettő közülük PHP magot fejleszt!) - olyannak tűnik, amit nem lehet kihagyni, ha PHP-t írsz.