Hogy könnyebben megértsük, miről szól ez a cikk, kezdjük a Stack Overflow átlagos napi statisztikájának változásával. Az alábbi adatok a 2013. november 12-i statisztikákból származnak:
- A terheléskiosztó 148 084 833 HTTP kérést fogadott el
- Ebből 36 095 312 oldalbetöltés volt
- 833,992,982,627 bájt (776 GB) HTTP forgalom küldésére szolgál
- Összesen 286 574 644 032 bájt (267 GB) adatot értek el
- Összesen 1 125 992 557 312 bájt (1 048 GB) adatot küldtek
- 334,572,103 SQL lekérdezés (beleértve csak HTTP kéréseket is)
- 412 865 051 Redis kérés
- 3 603 418 Tag Engine kérés
- Az SQL lekérdezéseknél 558 224 585 ms (155 óra) időbe telt
- Redis kérésekre 99 346 916 ms (27 óra) tartott
- 132 384 059 ms (36 órát) töltöttem a tag engine kéréssel
- A folyamat feldolgozása 2 728 177 045 ms (757 órá) alatt tartott ASP.Net feldolgozása
Az alábbi adatok mutatják a statisztikák változásait 2016. február 9-i állapot szerint, így összehasonlíthatjuk:
- Load Balancer által érkezett HTTP kérések: 209,420,973 (+61,336,090)
- 66 294 789 (+30 199 477) betöltődik az oldal
- HTTP adatok küldtek: 1,240,266,346,053 (+406,273,363,426) bytes (1.24 TB)
- A befogadott adatok összessége: 569 449 470 023 (+282 874 825 991) bájt (569 GB)
- Az összes küldött adatmennyiség: 3 084 303 599 266 (+1 958 311 041 954) bájt (3,08 TB)
- SQL lekérdezések (csak HTTP kérésekből): 504,816,843 (+170,244,740)
- Redis Cache megtekintések: 5,831,683,114 (+5,418,818,063)
- Rugalmas keresések: 17 158 874 (2013-ban nem követték)
- Tag motor kérések: 3 661 134 (+57 716)
- Az SQL lekérdezések futtatására használt összesített idő: 607,073,066 (+48,848,481) ms (168 óra)
- Redis gyorsítótár használati ideje: 10,396,073 (-88,950,843) ms (2,8 óra)
- Tag motor kérések által elfogyasztott idő: 147 018 571 (+14 634 512) ms (40,8 óra)
- A feldolgozás ASP.Net alatt eltöltött idő: 1 609 944 301 (-1 118 232 744) ms (447 óra)
- 22,71 (-5,29) ms 49 180 275 szám oldal átlagos renderelési idő (ebből 19,12 ms ASP.Net alatt fogyasztott)
- 11,80 (-53,2) MS 6 370 076 első oldal átlagos renderelési ideje (ebből 8,81 ms ASP.Net alatt fogyasztható)
Talán azon tűnődsz, miért dolgozik ASP.Net naponta 61 millió további kérést, miközben 757 órával csökkenti a feldolgozási időt (2013-hoz képest). Ez főként a 2015 elején végrehajtott frissítéseknek köszönhető, valamint a sok alkalmazáson belüli teljesítményoptimalizálási munkának. Ne feledd: a teljesítmény továbbra is eladási pont. Ha inkább érdekelnek a hardver részletei, ne aggódj, a következő cikkben csatolom majd a szerverek hardveradatait egy melléklet formájában (ezt a linket frissítem, amikor eljön az ideje).
Mi változott az elmúlt két évben? Nem sokat, csak néhány szervert és hálózati eszközt cserélek. Íme egy áttekintés azokról a szerverekről, amelyeket ma a weboldalad működtetéséhez használtak (figyeld meg, hogy 2013 óta változtak)
- 4 Microsoft SQL Server szerver (ebből 2 új hardvert használ)
- 11 IIS webszerver (új hardver)
- 2 Redis szerver (új hardver)
- 3 Tag Engine szerver (ebből 2 új hardvert használ)
- 3 Elasticsearch szerver (ugyanaz, mint fent)
- 4 HAProxy terheléselosztási szerver (2 hozzáadva, hogy támogassa a CloudFlare-t)
- 2 hálózati eszköz (Nexus 5596 mag + 2232TM Fabric Extender, minden eszköz 10Gbps sávszélességre frissítve)
- 2 x Fortinet 800C tűzfal (a Cisco 5525-X ASA-kat helyettesíti)
- 2 Cisco ASR-1001 router (a Cisco 3945 routereket váltja fel)
- 2 Cisco ASR-1001-x router (ÚJ!) )
Mire van szükségünk ahhoz, hogy a Stack Overflow működjön? 2013 óta nem sok minden változott, de az optimalizációknak és az új hardvernek köszönhetően most már csak egy webszerverre van szükségünk. Ezt a helyzetet már véletlenül is teszteltük, többször sikerrel. Kérlek, vedd figyelembe: épp azt mondtam, hogy működik, nem mondtam, hogy jó ötlet. De minden alkalommal, amikor ez megtörténik, elég érdekes.
Most, hogy vannak alapértékeink a szerver skálázási ötleteiről, nézzük meg, hogyan készítettük ezeket a menő weboldalakat. Kevés rendszer létezik teljesen önállóan (és a miénk sem kivétel), és ha nincs holisztikus nézőpont, amely ezeket az elemeket integrálja, az építészeti tervezés jelentősége jelentősen csökken. Célunk, hogy megértsük az egész helyzetet. A jövőben sok cikk fog megjelenni, amelyek mélyebben foglalkoznak minden egyes területen. Ez a cikk csupán a kulcsfontosságú hardver logikai szerkezetének összefoglalása, és a következő cikk részletesen tartalmazza ezeket a hardvereket.
Ha szeretnéd megnézni, hogyan néz ki ez a hardver ma, itt van néhány fotó, amit készítettem az A szekrényről (a B szekrény pontosan ugyanaz), amikor 2015 februárjában frissítettem a szervert:
Most pedig merüljünk bele az építészet elrendezésébe. Az alábbiakban összefoglalója a fő meglévő rendszerek logikai architektúrájáról:
Alapelvek
Íme néhány gyakori elv, amelyeket nem kell egymás után bevezetni:
- Mindennek van redundáns biztonsági mentése.
- Minden szervernek és hálózati eszköznek legalább két 10Gbps-es sávszélességű kapcsolata van.
- Minden szervernek két tápforrása van, amelyek két UPS egységen keresztül, mögöttük két generátoron és két hálózati feszültségforráson keresztül szolgáltatnak.
- Minden szervernek van egy redundáns tartaléka, amely az A és B rack helyen található.
- Minden szervernek és szolgáltatásnak dupla redundáns mentése van egy külön adatközpontban (Coloradóban), bár főként New Yorkot érintek.
- Mindennek van redundáns biztonsági mentése.
internet
Először meg kell találnod a weboldalunkat, ami DNS-es dolog. A weboldalak megtalálása gyors, ezért most a CloudFlare-nek adjuk, mert DNS szervereik vannak a világ minden szegletében. API-kon keresztül frissítjük a DNS rekordokat, és ők felelősek a DNS "kezeléséért". Azonban gonosz fejünkben még mindig vannak saját DNS szervereink a mélyen gyökerező bizalmi problémák miatt. Amikor az apokalipszis apokaliptikus – talán a GPL, Punyon vagy a gyorsítótár problémák miatt –, és az emberek még mindig programozni akarnak, hogy eltereljék a figyelmüket, akkor átváltunk a saját DNS szervereinkre.
Amint a böngésződ megtalálja a rejtekhelyünket, a négy internetszolgáltatónk (Level 3, Zayo, Cogent és Lightower New Yorkban) HTTP forgalma belép a négy fejlett routerünk egyikébe. A Border Gateway Protocolt (BGP, egy nagyon szabványos protokoll) használjuk, hogy peer-to-peer forgalmat irányítsunk a hálózati szolgáltatóktól, hogy irányítsuk azt, és a leghatékonyabb módot biztosítsuk szolgáltatásainkhoz. Az ASR-1001 és az ASR-1001-X routerek két csoportra vannak osztva, amelyek mindegyike aktív/aktív módot használ mindkét hálózati szolgáltató forgalma kezelésére – itt redundáns biztonsági mentések vannak. Bár mindegyiknek ugyanaz a fizikai sávszélessége van, 10Gbps, a külső forgalom továbbra is független a külső VLAN forgalomától, és külön csatlakozik a terheléselosztáshoz. Miután a forgalom áthalad a routeren, a terheléselosztóhoz érkezik.
Szerintem itt az ideje megemlíteni, hogy MPLS-ünk van, 10Gbps-es sávszélességgel a két adatközpont között, bár ez nem igazán kapcsolódik közvetlenül a weboldal szolgáltatásokhoz. Ezt a technológiát használjuk a helyszínen kívüli replikálásra és az adatok gyors helyreállítására bizonyos vészhelyzetek kezelésére. "De Nick, ebben nincs semmi feleslegesség!" Nos, technikai szempontból igazad van (pozitív értelemben), ezen a szinten ez valójában egyetlen hibás pont. De várj! A hálózati szolgáltatón keresztül két további OSPF failover útvonalunk is van (az MPLS az első választás, és ezek a második és harmadik lehetőség költségek miatt). A korábban említett eszközkészletek mindegyike csatlakozik Colorado adatközpontjához, a terheléselosztás hálózati forgalomának megfelelően, esetleges failover-ben. Természetesen összekapcsolhattuk volna ezt a két eszközkészletet egymással, így négy útvonal álljon rendelkezésre, de felejtsük el, lépjünk tovább.
Terheléselosztás (HAProxy)
A terheléselosztást a HAProxy 1.5.15-ös verzióval valósították meg, amely CentOS 7-en fut (a kedvenc Linux verziónk). És hozzáadni a TLS (SSL) biztonságos átviteli protokollt a HAProxy-hoz. Figyelemmel kísérjük a HAProxy 1.7-et is, amely azonnal támogatja a HTTP/2 protokollt.
Ellentétben más szerverekkel, amelyek kettős 10Gbps LACP hálózati kapcsolattal rendelkeznek, minden terheléskiosztó két 10Gbps-es kapcsolattal rendelkezik: az egyik a külső hálózathoz, a másik a DMZ-hez. Ezek a szerverek 64GB vagy annál nagyobb memóriával rendelkeznek, hogy hatékonyabban kezeljék az SSL protokollréteget. Amikor több TLS ülést tudunk gyorsítótárba helyezni és újrahasznosítani a memóriában, kevesebb számítási erőforrást fogyasztunk ugyanahhoz az ügyfélhez való csatlakozáskor. Ez azt jelenti, hogy gyorsabban és olcsóbban tudjuk helyreállítani az üléseket. A memória olyan olcsó, hogy könnyű választás.
A terheléselosztás maga is könnyen beállítható. Különböző weboldalakat hallgatunk több különböző IP-n (főleg tanúsítvány- és DNS-kezelési okokból), majd a forgalmat különböző háttérrendszerekre irányítjuk (főként a host fejlécek alapján). Itt csak annyit csinálunk, hogy korlátozzuk a sebességet, és néhány fejlécinformációt (a webrétegről) lekaparunk, hogy bejelentkezzünk a HAProxy rendszernaplóiba, így rögzíthetjük minden kérés teljesítménymutatóit. Ezt később részletesen megemlítjük.
Webréteg (IIS 8.5, ASP.Net MVC 5.2.3 és .Net 4.6.1)
A terheléselosztás a forgalmat 9 fő webszervernek (01-09) és 2 fejlesztő webszervernek (10-11, a tesztkörnyezetünk) osztja el. A fő szerver a Stack Overflow-t, a Careers-t és az összes Stack Exchange oldalt futtatja, míg a meta.stackoverflow.com és meta.stackexchange.com két másik szerveren fut. Maga a fő Q&A alkalmazás többbérlős, vagyis egyetlen alkalmazás kezeli az összes kérést a Q&A oldalról. Más szóval, az egész Q&A alkalmazást futtathatjuk egyetlen alkalmazási poolon egy szerveren. Más alkalmazások, mint például a Careers, API v2, Mobile API stb., függetlenek. Íme, mit látsz az IIS-ben a master és fejlesztő szervereknél:
Íme a Stack Overflow webszintjének eloszlása, ahogy az Opserverben (belső monitorozási irányítópultunk) látható:
És íme ezeknek a webszervereknek az erőforrás-felhasználása:
Egy későbbi cikkben részletesebben kifejtem majd arról, miért adunk túl sok erőforrást, és a folyamatos építésre, a mozgástérre és a redundanciára koncentrálunk.
Szolgáltatási réteg (IIS, ASP.Net MVC 5.2.3, . NET 4.6.1 és HTTP. SYS)
A webréteg mellett található a szolgáltatási réteg. A IIS 2012 tetején is futnak Windows 8.5R2-ben. Ez a réteg néhány belső szolgáltatást futtat, amelyek támogatják a webréteget és a termelési környezet egyéb belső rendszereit. A két fő szolgáltatás: "Stack Server", amely egy tag motort futtat, és http.sys-en alapul (nem IIS); Providence API (IIS-en alapul). Érdekes tény: a két folyamatot össze kellett kapcsolnom, hogy különböző socketekhez csatlakozzak, mert a Stack Server nagyon gyakran használta az L2 és L3 gyorsítótárokat, amikor kétperces időközönként frissítette a problémákat.
Az ilyen szolgáltatásokat futtató gépek kritikusak a tag engine és a backend API-k szempontjából, ezért redundánsnak kell lenniük, de nem 9x redundánsnak. Például minden cikket és címkéket betöltünk az adatbázisból n percenként (jelenleg 2 perc), ami nem alacsony. Nem szeretnénk ezt a terhelési műveletet 9-szer megismételni a webrétegen, 3-szor elég biztonságos számunkra. Különböző hardverkonfigurációkat is használunk ezekhez a szerverekhez, hogy jobban optimalizáljuk a tag engine és az elastic index feladatok számítási és adatbetöltési jellemzőit (szintén ebben a rétegben fut). Maga a "tag engine" viszonylag összetett téma, amelyet egy külön cikkben fogunk tárgyalni. Az alapelv, hogy amikor eléred a cím/kérdések/címkézett/java-t, belépsz a címkézési motorba, hogy megtaláld a hozzá illő kérdéseket. A motor kezeli az összes címké-párosítást, kivéve a /search-et, így mindenhol, beleértve az új navigációt is, az adatokat ezen a szolgáltatáson keresztül kapja.
Gyorsítótár és kiadás/előfizetés (Redis)
Néhány helyen Redis-t használtunk, és szilárd stabilitása van. Bár havi akár 160 milliárd művelet is történik, a CPU egy példány aránya nem haladja meg a 2%-ot, ami általában alacsonyabb:
Mi Redis-t használunk L1/L2 szintű gyorsítótározó rendszerekhez. Az "L1" szint az a HTTP gyorsítótár, amely webszerveren vagy bármilyen hasonló alkalmazásban működik. Az "L2" szint az, hogy adatokat szerezzenek a Redis-en keresztül, miután az előző szintű gyorsítótár meghibásodik. Adataink Protobuf formátumban tárolódnak, amelyet Marc Gravel írt protobuf-dot-net segítségével valósítanak meg. A Redis klienshez a StackExchange.Redis könyvtárat használtuk, amely egy nyílt forráskódú könyvtár, amelyet saját fejlesztése során fejlesztettünk. Ha egy webszerver nem találja meg mind az L1, mind az L2 gyorsítótárt, akkor adatokat kér az adatforrásaiból (adatbázis lekérdezések, API-hívások stb.), és elmenti az eredményeket a helyi gyorstárba és a Redis-be. Lehet, hogy a következő szerver hiányzik az L1 gyorsítótárból ugyanazok az adatok letöltése során, de az adatokat L2/Redisben fogja visszaszerezni, így nincs szükség adatbázis-lekérdezésekre vagy API-hívásokra.
Sok Q&A oldalt futtatunk, mindegyiknek saját L1/L2 gyorsítótára: a kulcs előtagként az L1 gyorsítótárban, az adatbázis azonosítója pedig az L2/Redis gyorsítótárban. A következő cikkekben majd részletezzük ezt a témát.
A két fő Redis szerver (egy master és egy slave) mellett minden oldali példányt futtattunk, és egy gépi tanulásra is beállítottunk egy példányt (főként memória miatt), két másik dedikált slave szerverrel együtt. Ezt a szervercsoportot olyan szolgáltatások nyújtására használják, mint például kérdések ajánlása a kezdőlapon és jobb munkakeresés kialakítása. Ezt a platformot Providence-nek hívják, és Kevin Montrose írt róla.
A fő Redis szerver 256GB RAM-mal rendelkezik (kb. 90GB használt RAM-mal), míg a Providence szerver 384GB memóriával rendelkezik (kb. 125GB használt memóriával).
A Redis nemcsak gyorsítótározásra szolgál, hanem van egy publikációs és előfizetési mechanizmusa is, ahol az egyik szerver közzéteheti az üzenetet, míg a többi előfizető fogadhatja az üzenetet (beleértve a szerver alsó áramú klienseiről érkező Redis-t). Ezt a mechanizmust arra használjuk, hogy töröljük az L1 gyorsítótárt más szolgáltatásokon, hogy fenntartsuk a cache konzisztenciáját a webszerveren. De van egy másik fontos felhasználása: a websocketek.
Websockets (NetGain)
Websocketeket használunk valós idejű frissítések továbbítására a felhasználóknak, például értesítéseket a felső sávban, szavazásokat, új navigációt, új válaszokat, kommenteket és még sok mást.
A socket szerver maga a webrétegen fut, natív socketeket használva. Ez egy nagyon kis alkalmazás, amely a nyílt forráskódú könyvtár implementációnkon alapul: StackExchange.NetGain. Csúcsidőben körülbelül 500 000 párhuzamos websocket kapcsolatunk volt, ami rengeteg böngésző. Érdekesség: néhány böngésző már több mint 18 hónapja nyitva van, és találnod kell valakit, hogy megnézd, életben vannak-e ezek a fejlesztők. Az alábbi diagram mutatja a websocket egyidejű mintázatát ezen a héten:
Miért használunk websocketeket? A mi skálánkban ez sokkal hatékonyabb, mint a közvélemény-kutatás. Így kevesebb erőforrással több adatot tudunk továbbítani, és valós idejűbbek a felhasználók számára. Ez a megközelítés azonban nem mentes a maga problémái: az ideiglenes portok, a kimerült fájlkezelők a terheléselosztókon nagyon érdekes problémák, és később beszélünk róluk.
Keresés (Elasticsearch)
Spoiler: Nincs sok izgalomra itt. A webréteg Elasticsearch 1.4-et használ, és egy ultra könnyű, nagy teljesítményű StackExchange.Elastic klienst valósít meg. Ellentétben a legtöbb dologgal, ezt a részt nem tervezzük nyílt forráskódúvá tenni, egyszerűen azért, mert nagyon kis részhalmazt tár fel az API-kból, amelyeket használni kell. Biztos vagyok benne, hogy a nyilvánosságra hozatal felülmúlja a veszteséget, és csak összezavarná a fejlesztőket. Ezekben a helyeken rugalmas:/search funkciót használunk kapcsolódó kérdések kiszámításához és javaslatok adásához.
Minden Elastic klaszter (egy-egy adatközponthoz) 3 csomópontot tartalmaz, mindegyiknek saját indexe van. A Karrier oldalon további indexek is találhatók. Egy kissé kevésbé szabványos része a rugalmas körökben a konfigurációnknak, hogy a 3 szerverből álló klaszterünk erősebb, mint a szokásos: minden szerver SSD tárhelyet használ, 192GB memóriát, 10 Gbps sávszélességű kettős hálózatot.
Ugyanez az alkalmazásdomain, mint a Stack Server (igen, a .Net Core is dobált minket itt) egy címkémotort is üzemeltet, amely szintén az Elasticsearch-et használja a folyamatos indexeléshez. Itt egy kis trükköt használunk, például a ROWVERSION-t használjuk az SQL Serverben (az adatforrásban), hogy összehasonlítsuk a "utolsó hely" dokumentumot a Elastic-ben. Mivel látszólag sorozatos, könnyű besorolni és indexelni a tartalmat, ha az utolsó látogatás után módosítják.
A fő oka annak, hogy a Elasticsearch-et használjuk az SQL teljes szöveges keresés helyett, a skálázhatósága és költséghatékonysága. Az SQL viszonylag drága a CPU-kon, míg az Elastic sokkal olcsóbb, és mostanában rengeteg új funkcióval rendelkezik. Miért ne használnánk a Solr-t? A hálózaton keresztül kell keresnünk (egyszerre több indextel), és a Solr nem támogatja ezt a forgatókönyvet a döntéseink idején. Azért nem használtuk még a 2.x-et, mert a típusok sokat változtak a 2.x-ben, ami azt jelenti, hogy mindent újra indexelnünk kell, ha frissíteni akarunk. Egyszerűen nincs elég időm megtervezni a követelmények változását és áthelyezését.
Adatbázis (SQL Server)
Az SQL Servert egyetlen igazságforrásként használjuk. Az összes adat a Elasticben és Redisben az SQL Serverről származik. Két SQL Server klaszterünk van, és AlwaysOn elérhetőségi csoportokkal vagyunk konfigurálva. Minden klaszternek van egy elsődleges szervere New Yorkban (amely szinte az összes terhelést átveszi), valamint egy replika szerver, valamint egy replika szerver Coloradóban (a katasztrófa-helyreállítási adatközpontunk). Minden másolási művelet aszinkron.
Az első klaszter egy Dell R720xd szerverekből áll, mindegyik 384GB memóriával, egy PCIe SSD-vel 4TB hellyel, valamint két 12 magos CPU-val. Tartalmazza a Stack Overflow-t, a Sites-t (ez rossz név, később elmagyarázom), PRIZM-et és a Mobile adatbázisát.
A második klaszter egy Dell R730xd szerverekből áll, mindegyik 768GB memóriával, egy PCIe SSD-vel 6TB hellyel és két 8 magos CPU-val. Ez a klaszter tartalmazza az összes többi adatbázist, beleértve a Karriereket, Open ID-t, Csevegést, kivételnaplókat és más kérdezz-felelek oldalakat (pl. Szuperfelhasználó, Szerverhiba stb.).
Az adatbázis rétegen nagyon alacsony szinten szeretnénk tartani a CPU-használatot, bár a gyakorlatban a CPU-használat valamivel magasabb lesz, amikor tervezett gyorsítótára-problémák merülnek fel (amelyeket éppen megoldunk). Jelenleg a NY-SQL02 és 04 a fő szerverek, míg a 01 és 03 a replika szerverek, és ma újraindítottuk őket az SSD frissítés miatt. Íme, hogyan teljesítettek az elmúlt 24 órában:
Az SQL használata nagyon egyszerű. Az egyszerű azt jelenti, hogy gyors. Bár néhány lekérdezési állítás torzítható, maga az SQL interakció viszonylag natív módon zajlik. Van néhány régi Linq2Sql-ünk, de minden új fejlesztésünk a Dapper-t használja, amely a nyílt forráskódú mikro-ORM keretrendszerünk, amely POCO-t használ. Hadd magyarázzam el másképp: a Stack Overflow adatbázisában csak egy tárolt eljárás van, és ezt az utolsó megmaradt tárolt eljárást fogom megszüntetni, és helyette kódot vetek rá.
Könyvtár
Nos, változtassuk meg a véleményünket, íme vannak dolgok, amik közvetlenebben segíthetnek. Már említettem néhányat közülük, de adok egy listát a sok nyílt forráskódú .Net könyvtárról, amelyeket karbantartunk és mindenki használ. Nyílt forráskódot használunk őket, mert nincs alapvető üzleti értékük, de segíthetnek a fejlesztőknek világszerte. Remélem, most már használhatod őket:
- Dapper (.Net Core) – Egy nagy teljesítményű mikro-ORM keretrendszer ADO.Net
- StackExchange.Redis – Egy nagy teljesítményű Redis kliens
- MiniProfiler – egy könnyű profiler, amit minden oldalon használunk (támogatja a Ruby, Go és Node programokat is).
- Kivételes – SQL, JSON, MySQL stb. hibánaplózáshoz
- Jil – Nagy teljesítményű JSON serializáció és deserializátor
- Sigil – .Net CIL Generation Helper (akkor használják, ha C# nem elég gyors)
- NetGain – Nagy teljesítményű websocket szerver
- Opserver – Monitorozó irányítópult, amely közvetlenül a legtöbb rendszert lekérdezi, és információkat tud letölteni az Orionról, Bosunról vagy WMI-ről
- Bosun – Monitorozó rendszer a háttérben, Go nyelven írva
|