[ÖversĂ€ttning] Envoy threading modell

ÖversĂ€ttning av artikeln: Envoy-trĂ„dmodell - https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310

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

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_NONBLOCK ibland 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

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

Klusterflödeshantering omfattar följande komponenter och steg:

  1. 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.
  2. HÀlsokontrollen utför aktiv hÀlsokontroll och rapporterar hÀlsotillstÄndsÀndringar till klusterhanteraren.
  3. 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.
  4. Varje arbetstrÄd kör kontinuerligt en hÀndelsebearbetningsslinga.
  5. 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.
  6. Under nÀsta tysta period kommer arbetstrÄden att uppdatera ögonblicksbilden i den allokerade TLS-platsen.
  7. 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

Köp pĂ„litlig hosting för webbplatser med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster