ĂversĂ€ttning av artikeln:
Jag tyckte att den hÀr artikeln var ganska intressant, och eftersom Envoy oftast anvÀnds som en del av "istio" eller helt enkelt som en Kubernetes "ingress controller", har de flesta inte samma direkta interaktion med den som till exempel med typiska Nginx- eller Haproxy-installationer. Men om nÄgot gÄr sönder vore det bra att förstÄ hur det fungerar inifrÄn. Jag försökte översÀtta sÄ mycket text som möjligt till ryska, inklusive specialord. För er som tycker det Àr smÀrtsamt att titta pÄ detta har jag lÀmnat originalen inom parentes. VÀlkommen under snittet.
Teknisk dokumentation pÄ lÄg nivÄ för Envoy-kodbasen Àr för nÀrvarande ganska gles. För att ÄtgÀrda detta planerar jag att göra en serie blogginlÀgg om de olika Envoy-delsystemen. Eftersom detta Àr den första artikeln, berÀtta gÀrna vad du tycker och vad du kan vara intresserad av i framtida artiklar.
En av de vanligaste tekniska frÄgorna jag fÄr om Envoy Àr en begÀran om en lÄgnivÄbeskrivning av den trÄdmodell som anvÀnds. I det hÀr inlÀgget kommer jag att beskriva hur Envoy mappar kopplingar till trÄdar, och det Thread Local Storage-system som anvÀnds internt för att göra kod mer parallell och effektiv.
Ăversikt över gĂ€ngning
![[ĂversĂ€ttning] Envoy threading modell](/wp-content/uploads/2019/04/c5028463db1eb5b07ccb1a3af1f64a03.jpeg)
Envoy anvÀnder tre olika typer av strömmar:
- Huvudsaklig: Den hÀr trÄden hanterar processstart och avstÀngning, all XDS (xDiscovery Service) API-bearbetning, inklusive DNS, hÀlsokontroll, allmÀn kluster- och runtime-hantering, statistikÄterstÀllning, administration och allmÀn processhantering. Linux Signaler, omstarter under drift, etc. Allt som hÀnder i den hÀr trÄden Àr asynkront och blockerar inte. Sammantaget koordinerar huvudtrÄden alla kritiska processer som inte krÀver en stor processor. Detta gör att det mesta av kontrollkoden kan skrivas som om den vore enkeltrÄdad.
- Arbetstagare: Som standard skapar Envoy en arbetstrÄd för varje hÄrdvarutrÄd i systemet, detta kan styras med alternativet
--concurrency. Varje arbetstrÄd kör en "icke-blockerande" hÀndelseloop som ansvarar för att lyssna pÄ varje lyssnare (det finns ingen lyssnarshardning i skrivande stund (29 juli 2017)), acceptera nya anslutningar, instansiera en filterstack för anslutningen och hantera all IO under anslutningens livslÀngd. à terigen, detta gör att det mesta av koden för anslutningshantering kan skrivas som om den vore enkeltrÄdad. - Filrensare: Varje fil som Envoy skriver, frÀmst Ätkomstloggar, har för nÀrvarande en oberoende lÄstrÄd. Detta beror pÄ att skrivning till filer som cachas av filsystemet Àven nÀr man anvÀnder
O_NONBLOCKibland kan det bli blockerat (suck). NÀr arbetstrÄdar behöver skriva till en fil flyttas data faktiskt till en buffert i minnet dÀr de sÄ smÄningom spolas genom trÄden. filrensning. Detta Àr ett kodomrÄde dÀr tekniskt sett alla arbetstrÄdar kan blockera samma lÄs samtidigt som de försöker fylla en minnesbuffert.
Hantering av anslutningar
Som diskuterats kort ovan lyssnar alla arbetartrÄdar pÄ alla lyssnare utan nÄgon segmentering. PÄ sÄ sÀtt anvÀnds kÀrnan för att intelligent skicka mottagna sockets till arbetstrÄdar. Moderna kÀrnor Àr generellt sett mycket bra pÄ detta, och anvÀnder funktioner som IO-boosting för att försöka fylla en trÄd med arbete innan andra trÄdar som ocksÄ lyssnar pÄ samma socket börjar anvÀnda den, och anvÀnder inte spinlock för att hantera varje förfrÄgan.
NÀr en anslutning vÀl har accepterats i en arbetstrÄd lÀmnar den aldrig den trÄden. All vidare bearbetning av anslutningen hanteras helt i arbetstrÄden, inklusive eventuellt vidarebefordringsbeteende.
Detta har flera viktiga konsekvenser:
- Alla anslutningspooler i Envoy Àr associerade med en arbetstrÄd. SÄ, Àven om HTTP/2-anslutningspooler bara gör en anslutning till varje uppströmsvÀrd Ät gÄngen, om det finns fyra arbetstrÄdar, kommer det att finnas fyra HTTP/2-anslutningar per uppströmsvÀrd i stationÀrt tillstÄnd.
- Anledningen till att Envoy fungerar pÄ det hÀr sÀttet Àr att genom att hÄlla allt pÄ en enda arbetstrÄd kan nÀstan all kod skrivas lÄsfri och som om den vore enkeltrÄdad. Den hÀr designen gör det enkelt att skriva stora mÀngder kod och skalar otroligt bra till ett nÀstan obegrÀnsat antal arbetstrÄdar.
- En av de viktigaste slutsatserna Àr dock att det, ur ett minnespool- och anslutningseffektivitetsperspektiv, faktiskt Àr mycket viktigt att finjustera parametern.
--concurrency. Att ha fler arbetstrÄdar Àn nödvÀndigt kommer att resultera i slöseri med minne, fler inaktiva anslutningar och en lÄngsammare anslutningshastighet för anslutningspoolen. PÄ Lyft körs vÄra Envoy sidecar-containrar med mycket lÄg samtidighet, sÄ prestandan Àr ungefÀr i linje med de tjÀnster de stÄr bredvid. Vi kör endast Envoy som en edge-proxy vid maximal samtidighet.
Vad icke-blockerande betyder
Termen "icke-blockerande" har anvÀnts flera gÄnger hittills nÀr man diskuterar hur huvud- och arbetstrÄdarna fungerar. All kod skrivs med antagandet att ingenting nÄgonsin lÄser sig. Detta Àr dock inte helt sant (vad Àr inte helt sant?).
Envoy anvÀnder flera lÄnga processlÄs:
- Som nÀmnts, nÀr man skriver Ätkomstloggar, fÄr alla arbetstrÄdar samma lÄs innan de fyller loggbufferten i minnet. LÄsets hÄlltid bör vara mycket lÄg, men det Àr möjligt att detta lÄs kommer att ifrÄgasÀttas vid hög samtidighet och hög dataflöde.
- Envoy anvÀnder ett mycket sofistikerat system för att hantera statistik som Àr lokal för en trÄd. Detta blir Àmnet för ett separat inlÀgg. Jag vill dock kort nÀmna att det som en del av den lokala bearbetningen av trÄdstatistik ibland Àr nödvÀndigt att skaffa ett lÄs pÄ det centrala "statistiklagret". Detta lÄs borde aldrig vara nödvÀndigt.
- HuvudtrÄden behöver regelbundet koordineras med alla arbetartrÄdar. Detta görs genom att "publicera" frÄn huvudtrÄden till arbetstrÄdarna, och ibland frÄn arbetstrÄdarna tillbaka till huvudtrÄden. Skicka krÀver ett lÄs sÄ att det publicerade meddelandet kan placeras i kö för senare leverans. Dessa block borde aldrig utsÀttas för allvarlig konkurrens, men de kan tekniskt sett fortfarande blockeras.
- NÀr Envoy skriver en logg till systemfelsströmmen (standardfel) fÄr den ett processomfattande lÄs. Generellt sett anses Envoys lokala loggning vara fruktansvÀrd prestandamÀssigt, sÄ det finns inte mycket fokus pÄ att förbÀttra den.
- Det finns nÄgra andra slumpmÀssiga lÄs, men ingen av dem Àr prestandakritiska och bör aldrig ifrÄgasÀttas.
TrÄd lokal lagring
PÄ grund av hur Envoy separerar huvudtrÄdens ansvarsomrÄden frÄn arbetstrÄdens ansvarsomrÄden finns det ett krav att komplex bearbetning kan utföras pÄ huvudtrÄden och sedan levereras till varje arbetstrÄd med en hög grad av parallellitet. Det hÀr avsnittet beskriver Envoy Thread Local Storage (TLS)-systemet pÄ en övergripande nivÄ. I nÀsta avsnitt kommer jag att beskriva hur det anvÀnds för att hantera ett kluster.
![[ĂversĂ€ttning] Envoy threading modell](/wp-content/uploads/2019/04/a47af1e8eb1f55a4d7609b3ff3ee9ce1.jpeg)
Som beskrivits tidigare hanterar huvudtrÄden i stort sett all hanterings- och kontrollplansfunktionalitet i Envoy-processen. Kontrollplanet hÀr Àr lite överbelastat, men nÀr det ses inom sjÀlva Envoy-processen och jÀmförs med den vidarebefordran som arbetstrÄdar gör, Àr det vettigt. Den allmÀnna regeln Àr att huvudtrÄdsprocessen gör ett visst arbete och sedan behöver den uppdatera varje arbetstrÄd med resultatet av det arbetet, pÄ sÄ sÀtt behöver inte arbetstrÄden stÀlla in ett lÄs för varje Ätkomst.
Envoys TLS-system (Thread local storage) fungerar enligt följande:
- Kod som körs pĂ„ huvudtrĂ„den kan allokera en TLS-plats för hela processen. Ăven om detta Ă€r abstrakt, Ă€r det i praktiken ett index i en vektor, vilket ger O(1)-Ă„tkomst.
- HuvudtrÄden kan lÀgga in godtyckliga data i sin plats. NÀr detta Àr klart publiceras data till varje arbetstrÄd som en vanlig hÀndelseloop.
- ArbetartrÄdar kan lÀsa frÄn sin TLS-plats och hÀmta all trÄdlokal data som finns tillgÀnglig dÀr.
Ăven om det Ă€r ett mycket enkelt och otroligt kraftfullt paradigm, Ă€r det mycket likt RCU (Read-Copy-Update) lĂ„skonceptet. I huvudsak ser arbetstrĂ„dar aldrig nĂ„gra Ă€ndringar av data i TLS-platser medan de utför arbete. FörĂ€ndring sker endast under viloperioden mellan arbetshĂ€ndelser.
Envoy anvÀnder detta pÄ tvÄ olika sÀtt:
- Genom att lagra olika data pÄ varje arbetstrÄd kan dessa data nÄs utan nÄgon lÄsning.
- HÄller en delad pekare till globala data i skrivskyddat lÀge pÄ varje arbetstrÄd. SÄledes har varje arbetstrÄd ett datareferensantal som inte kan minskas medan arbetet pÄgÄr. Först nÀr alla arbetare har lugnat ner sig och laddat upp den nya delade datan kommer den gamla datan att förstöras. Detta Àr identiskt med RCU.
TrÄdning för klusteruppdatering
I det hÀr avsnittet kommer jag att beskriva hur TLS (Thread local storage) anvÀnds för att hantera klustret. Klusterhantering inkluderar xDS- och/eller DNS API-bearbetning, samt hÀlsokontroll.
![[ĂversĂ€ttning] Envoy threading modell](/wp-content/uploads/2019/04/238f5718729e1f12f8f0d6507f16abe8.jpeg)
Klusterflödeshantering omfattar följande komponenter och steg:
- Klusterhanteraren Àr en komponent i Envoy som hanterar alla kÀnda klusteruppströmsenheter, Cluster Discovery Service (CDS) API, Secret Discovery Service (SDS) och Endpoint Discovery Service (EDS) API:er, DNS och aktiv extern hÀlsokontroll. Den ansvarar för att skapa en "sÄ smÄningom konsekvent" vy över varje uppströms kluster, vilket inkluderar upptÀckta vÀrdar samt hÀlsostatus.
- HÀlsokontrollen utför aktiv hÀlsokontroll och rapporterar hÀlsotillstÄndsÀndringar till klusterhanteraren.
- CDS (Cluster Discovery Service) / SDS (Secret Discovery Service) / EDS (Endpoint Discovery Service) / DNS utförs för att faststÀlla klustermedlemskap. TillstÄndsÀndringen returneras till klusterhanteraren.
- Varje arbetstrÄd kör kontinuerligt en hÀndelsebearbetningsslinga.
- NÀr klusterhanteraren faststÀller att klustrets tillstÄnd har Àndrats skapar den en ny skrivskyddad ögonblicksbild av klustertillstÄndet och skickar den till varje arbetstrÄd.
- Under nÀsta tysta period kommer arbetstrÄden att uppdatera ögonblicksbilden i den allokerade TLS-platsen.
- Under en I/O-hÀndelse som behöver identifiera en vÀrd för lastbalansering, kommer lastbalanseraren att frÄga TLS-platsen (Thread local storage) för att hÀmta information om vÀrden. Detta krÀver inga lÄs. Observera ocksÄ att TLS ocksÄ kan utlösa hÀndelser nÀr den uppdateras, sÄ att lastbalanserare och andra komponenter kan berÀkna om cacher, datastrukturer etc. Detta ligger utanför ramen för det hÀr inlÀgget, men anvÀnds pÄ olika stÀllen i koden.
Med hjÀlp av ovanstÄende procedur kan Envoy behandla alla förfrÄgningar utan nÄgon blockering (förutom de som beskrivits tidigare). Bortsett frÄn komplexiteten i sjÀlva TLS-koden behöver den mesta delen av kod inte förstÄ hur multitrÄdning fungerar och kan skrivas i enkeltrÄdat lÀge. Detta gör det mesta av koden enklare att skriva, förutom att det ger överlÀgsen prestanda.
Andra delsystem som anvÀnder TLS
TLS (Thread local storage) och RCU (Read Copy Update) anvÀnds flitigt i Envoy.
Exempel pÄ anvÀndning:
- Mekanism för att Àndra funktionalitet under körning: Den aktuella listan över aktiverade funktioner berÀknas i huvudtrÄden. Varje arbetstrÄd fÄr sedan en skrivskyddad ögonblicksbild med hjÀlp av RCU-semantik.
- ErsÀtta routtabellerFör routtabeller som tillhandahÄlls av RDS (Route Discovery Service) skapas routtabeller i huvudtrÄden. Den skrivskyddade ögonblicksbilden kommer sedan att tillhandahÄllas varje arbetstrÄd med hjÀlp av RCU-semantik (Read Copy Update). Detta gör att Àndringar av routningstabeller Àr atomÀrt effektiva.
- HTTP-header-cachning: Det visar sig att det Àr ganska dyrt att berÀkna HTTP-headern för varje begÀran (medan man kör ~25K+ RPS per kÀrna). Envoy berÀknar centralt headern ungefÀr var halva sekund och skickar den till varje arbetare via TLS och RCU.
Det finns andra fall, men de föregÄende exemplen borde ge en god förstÄelse för vad TLS anvÀnds till.
KĂ€nda prestandafallgropar
Ăven om Envoy generellt sett presterar ganska bra, finns det nĂ„gra kĂ€nda omrĂ„den som krĂ€ver uppmĂ€rksamhet nĂ€r de anvĂ€nds med mycket hög samtidighet och dataflöde:
- Som beskrivs i den hÀr artikeln lÄses för nÀrvarande alla arbetstrÄdar nÀr de skriver till Ätkomstloggens minnesbuffert. Vid hög samtidighet och högt dataflöde mÄste du batcha Ätkomstloggarna för varje arbetstrÄd pÄ bekostnad av leverans i fel ordning nÀr du skriver till den slutliga filen. Alternativt kan du skapa en separat Ätkomstlogg för varje arbetsflöde.
- Ăven om statistiken Ă€r starkt optimerad, kommer det vid mycket hög parallellitet och genomströmning sannolikt att finnas atomĂ€r konkurrens om individuell statistik. Lösningen pĂ„ detta problem Ă€r rĂ€knare per arbetartrĂ„d med periodisk Ă„terstĂ€llning av centrala rĂ€knare. Detta kommer att diskuteras i ett senare inlĂ€gg.
- Den befintliga arkitekturen kommer inte att fungera bra om Envoy distribueras i ett scenario dÀr det finns vÀldigt fÄ anslutningar som krÀver betydande resurser för att bearbeta. Det finns ingen garanti för att anslutningar kommer att vara jÀmnt fördelade mellan arbetstrÄdar. Detta kan lösas genom att implementera balansering av arbetaransslutningar, vilket möjliggör utbyte av anslutningar mellan arbetartrÄdar.
Slutsats
Envoys trÄdmodell Àr utformad för att ge enkel programmering och massiv parallellitet, pÄ bekostnad av potentiellt slöseri med minne och anslutningar om den inte konfigureras korrekt. Denna modell gör att den presterar mycket bra vid mycket höga trÄdantal och genomströmning.
Som jag kort nÀmnde pÄ Twitter kan designen ocksÄ köras ovanpÄ en fullfjÀdrad anvÀndarlÀgesnÀtverksstack som DPDK (Data Plane Development Kit), vilket kan resultera i att vanliga servrar hanterar miljontals förfrÄgningar per sekund med full L7-bearbetning. Det ska bli vÀldigt intressant att se vad som byggs under de nÀrmaste Ären.
En sista snabb kommentar: Jag har blivit frÄgad mÄnga gÄnger varför vi valde C++ för Envoy. Anledningen Àr fortfarande att det fortfarande Àr det enda allmÀnt anvÀnda sprÄket i produktionskvalitet dÀr arkitekturen som beskrivs i det hÀr inlÀgget kan byggas. C++ Àr definitivt inte rÀtt för alla eller ens mÄnga projekt, men för vissa anvÀndningsfall Àr det fortfarande det enda verktyget för att fÄ jobbet gjort.
LĂ€nkar till kod
LÀnkar till filer med grÀnssnitt och header-implementeringar som diskuteras i det hÀr inlÀgget:
KĂ€lla: will.com
