[Tõlge] Envoy keermestamise mudel

Artikli tõlge: Envoy keermestusmudel – https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310

Mulle tundus see artikkel üsna huvitav ja kuna Envoy'd kasutatakse kõige sagedamini kubernetese "istio" osana või lihtsalt "sisenemise kontrollerina", ei ole enamikul inimestel sellega sama otsest suhtlust kui näiteks tüüpilisega. Nginxi või Haproxy installid. Kui aga midagi katki läheb, oleks hea seestpoolt aru saada, kuidas see toimib. Püüdsin tõlkida võimalikult suure osa tekstist vene keelde, sealhulgas erisõnu, kellel on seda valus vaadata, jätsin originaalid sulgudesse. Tere tulemast kassi.

Envoy koodibaasi madal tehniline dokumentatsioon on praegu üsna napp. Selle parandamiseks kavatsen teha ajaveebi postituste sarja Envoy erinevate alamsüsteemide kohta. Kuna tegemist on esimese artikliga, siis andke mulle teada, mida arvate ja mis võiksite järgmistes artiklites huvi pakkuda.

Üks levinumaid tehnilisi küsimusi, mis mulle Envoy kohta esitatakse, on selles kasutatava keermestusmudeli madala taseme kirjelduse küsimine. Selles postituses kirjeldan, kuidas Envoy kaardistab ühendusi lõimedega, aga ka Thread Local Storage süsteemi, mida see sisemiselt kasutab koodi paralleelsemaks ja suure jõudlusega muutmiseks.

Keermestamise ülevaade

[Tõlge] Envoy keermestamise mudel

Envoy kasutab kolme erinevat tüüpi vooge:

  • Peamine: See lõim juhib protsesside käivitamist ja lõpetamist, kogu XDS-i (xDiscovery Service) API töötlemist, sealhulgas DNS-i, tervisekontrolli, üldist klastri ja käitusaja haldust, statistika lähtestamist, haldust ja üldist protsessihaldust – Linuxi signaale. kuum taaskäivitamine jne. selles lõimes toimuv on asünkroonne ja "mitteblokeeriv". Üldiselt koordineerib põhilõng kõiki kriitilisi funktsionaalsusprotsesse, mille tööks ei ole vaja palju protsessorit. See võimaldab enamiku juhtkoodide kirjutada nii, nagu oleks see ühe keermega.
  • Töötaja: Vaikimisi loob Envoy iga süsteemi riistvaralõime jaoks töölõime, seda saab juhtida suvandiga --concurrency. Iga töötaja lõim käitab "mitteblokeerivat" sündmuste tsüklit, mis vastutab iga kuulaja kuulamise eest; kirjutamise ajal (29. juulil 2017) ei toimu kuulaja tükeldamist, uute ühenduste vastuvõtmist, filtrite virna käivitamist. ühendus ja kõigi sisend/väljund (IO) toimingute töötlemine ühenduse eluea jooksul. Jällegi võimaldab see enamiku ühenduse haldamise koodi kirjutada nii, nagu oleks see ühe keermega.
  • Faili loputusseade: Igal failil, mille Envoy kirjutab, peamiselt juurdepääsulogidel, on praegu sõltumatu blokeerimislõng. See on tingitud asjaolust, et failisüsteemi vahemällu salvestatud failidele kirjutamine isegi kasutamisel O_NONBLOCK võib mõnikord ummistuda (ohkamine). Kui töölõimed peavad faili kirjutama, teisaldatakse andmed tegelikult mällu puhvrisse, kus need lõpuks lõimest läbi loputatakse faili loputamine. See on üks koodiala, kus tehniliselt võivad kõik töötaja lõimed mälupuhvri täitmisel sama luku blokeerida.

Ühenduse käsitlemine

Nagu ülalpool lühidalt käsitletud, kuulavad kõik töötajate lõimed kõiki kuulajaid ilma killustamata. Seega kasutatakse kernelit aktsepteeritud pistikupesade graatsiliseks saatmiseks töölõimedesse. Kaasaegsed tuumad on selles üldiselt väga head, nad kasutavad selliseid funktsioone nagu sisendi/väljundi (IO) prioriteedi tõstmine, et püüda lõime tööga täita enne, kui nad hakkavad kasutama teisi lõime, mis samuti samas pesas kuulavad, ja samuti ei kasuta nad ümberringi. lukustamine (Spinlock) iga päringu töötlemiseks.
Kui ühendus on töölõimes vastu võetud, ei lahku see kunagi sellest lõimest. Kogu ühenduse edasine töötlemine, sealhulgas mis tahes edastamiskäitumine, käsitletakse täielikult töötaja lõimes.

Sellel on mitmeid olulisi tagajärgi:

  • Kõik ühenduskogumid rakenduses Envoy on määratud töötaja lõimele. Ehkki HTTP/2-ühenduste kogumid loovad korraga ainult ühe ühenduse iga ülesvoolu hostiga, on nelja töötaja lõime korral püsiolekus neli HTTP/2 ühendust ühe ülesvoolu hosti kohta.
  • Põhjus, miks Envoy niimoodi töötab, on see, et hoides kõike ühel töötaja lõimel, saab peaaegu kogu koodi kirjutada ilma blokeerimiseta ja nii, nagu oleks see ühe lõimega. See disain muudab suure hulga koodi kirjutamise lihtsaks ja skaala uskumatult hästi peaaegu piiramatule arvule töölõimedele.
  • Üks peamisi väljavõtteid on aga see, et mälukogumi ja ühenduse tõhususe seisukohast on tegelikult väga oluline konfigureerida --concurrency. Vajalikust rohkemate töölõimede olemasolu raiskab mälu, loob rohkem jõudeühendusi ja vähendab ühenduste koondamise kiirust. Lyftis töötavad meie saadiku külgkorvi konteinerid väga väikese samaaegselt, nii et jõudlus vastab ligikaudu teenustele, mille kõrval nad istuvad. Käitame Envoy servi puhverserverina ainult maksimaalse samaaegsuse korral.

Mida tähendab mitteblokeerimine?

Mõistet "mitteblokeeriv" ​​on seni kasutatud mitu korda, kui arutatakse põhi- ja töölõime toimimist. Kogu kood on kirjutatud eeldusel, et midagi pole kunagi blokeeritud. See pole aga täiesti tõsi (mis ei ole täiesti tõsi?).

Envoy kasutab mitut pika protsessi lukku:

  • Nagu arutatud, omandavad juurdepääsulogide kirjutamisel kõik töölõimed sama luku enne, kui mälusisene logipuhver täitub. Luku hoidmise aeg peaks olema väga madal, kuid luku vastu on võimalik vaidlustada suure samaaegse ja suure läbilaskevõimega.
  • Envoy kasutab lõime kohaliku statistika käsitlemiseks väga keerulist süsteemi. See on eraldi postituse teema. Küll aga mainin lühidalt, et lõimestatistika kohaliku töötlemise osana on vahel vaja soetada lukk kesksele "statistikapoele". Seda lukustamist ei tohiks kunagi nõuda.
  • Põhilõime tuleb perioodiliselt kooskõlastada kõigi töölõimedega. Seda tehakse "avaldades" põhilõimelt töölõimedele ja mõnikord töölõimedest tagasi põhilõime. Saatmiseks on vaja lukku, et avaldatud kirja saaks hilisemaks edastamiseks järjekorda panna. Neid lukke ei tohiks kunagi tõsiselt vaidlustada, kuid neid saab siiski tehniliselt blokeerida.
  • Kui saadik kirjutab süsteemi veavoogu logi (standardviga), lukustub see kogu protsess. Üldiselt peetakse Envoy kohalikku metsaraiet jõudluse seisukohast kohutavaks, seega pole selle parandamisele palju tähelepanu pööratud.
  • On veel mõned juhuslikud lukud, kuid ükski neist ei ole jõudluse jaoks kriitiline ja seda ei tohiks kunagi vaidlustada.

Lõime kohalik salvestusruum

Kuna saadik eraldab põhilõime kohustused töölõime kohustustest, on nõue, et põhilõime saab teha keeruka töötlemise ja seejärel edastada need igale töötaja lõimele väga samaaegselt. See jaotis kirjeldab kõrgel tasemel saatja lõime kohalikku salvestusruumi (TLS). Järgmises osas kirjeldan, kuidas seda klastri haldamiseks kasutatakse.
[Tõlge] Envoy keermestamise mudel

Nagu juba kirjeldatud, käsitleb põhilõime peaaegu kõiki saatjaprotsessi haldus- ja juhtimistasandi funktsioone. Juhttasand on siin pisut ülekoormatud, kuid kui vaadata seda saadikuprotsessis endas ja võrrelda seda edasisaatmisega, mida töötaja lõimed teevad, on see mõistlik. Üldreegel on see, et põhilõime protsess teeb teatud tööd ja seejärel peab iga töötaja lõime värskendama vastavalt selle töö tulemusele. sel juhul ei pea töötaja lõime igal juurdepääsul lukustama.

Envoy's TLS (Thread local storage) süsteem töötab järgmiselt:

  • Põhilõimes töötav kood võib kogu protsessi jaoks eraldada TLS-i pesa. Kuigi see on abstraheeritud, on see praktikas indeks vektorisse, mis tagab juurdepääsu O(1).
  • Pealõng võib oma pessa installida suvalisi andmeid. Kui see on tehtud, avaldatakse andmed igas töötaja lõimes tavalise sündmusetsükli sündmusena.
  • Töötajate lõimed saavad lugeda oma TLS-i pesast ja hankida kõik seal saadaolevad lõimepõhised andmed.

Kuigi see on väga lihtne ja uskumatult võimas paradigma, on see väga sarnane RCU (Read-Copy-Update) blokeerimise kontseptsiooniga. Põhimõtteliselt ei näe töötaja lõimed kunagi töötamise ajal TLS-i pesades andmeid. Muutused toimuvad ainult töösündmuste vahelisel puhkeperioodil.

Envoy kasutab seda kahel erineval viisil.

  • Kui salvestate igale töölõimele erinevaid andmeid, pääseb andmetele juurde ilma igasuguse blokeerimiseta.
  • Säilitades jagatud osuti globaalsetele andmetele kirjutuskaitstud režiimis igas töötaja lõimes. Seega on igal töötaja lõimel andmete viidete arv, mida ei saa töö ajal vähendada. Alles siis, kui kõik töötajad rahunevad ja uued jagatud andmed üles laadivad, hävitatakse vanad andmed. See on identne RCU-ga.

Klastri värskenduse lõimestamine

Selles jaotises kirjeldan, kuidas TLS-i (Thread local storage) kasutatakse klastri haldamiseks. Klastrite haldamine hõlmab xDS API ja/või DNS-i töötlemist ning tervisekontrolli.
[Tõlge] Envoy keermestamise mudel

Klastrite voohaldus sisaldab järgmisi komponente ja samme.

  1. Klastrihaldur on Envoy komponent, mis haldab kõiki teadaolevaid klastri ülesvoolu, klastrituvastusteenuse (CDS) API-d, salaotsingu teenuse (SDS) ja lõpp-punkti tuvastamise teenuse (EDS) API-sid, DNS-i ja aktiivseid väliseid kontrolle. See vastutab iga ülesvoolu klastri "lõpuks järjepideva" vaate loomise eest, mis hõlmab nii avastatud hoste kui ka tervislikku seisundit.
  2. Tervisekontroll teeb aktiivse tervisekontrolli ja annab terviseseisundi muutustest teada klastrijuhile.
  3. Klastri liikmelisuse määramiseks kasutatakse CDS-i (klastri otsinguteenus) / SDS-i (salajane otsinguteenus) / EDS-i (lõpp-punkti otsinguteenus) / DNS-i. Olekumuudatus tagastatakse klastrihaldurile.
  4. Iga töötaja lõim täidab pidevalt sündmusetsüklit.
  5. Kui klastrihaldur teeb kindlaks, et klastri olek on muutunud, loob ta klastri olekust uue kirjutuskaitstud hetktõmmise ja saadab selle igale töötaja lõimele.
  6. Järgmise vaikse perioodi jooksul värskendab töötaja lõim eraldatud TLS-i pesas olevat hetktõmmist.
  7. I/O sündmuse ajal, mis peaks määrama hosti koormuse tasakaalu, taotleb koormuse tasakaalustaja hosti kohta teabe hankimiseks TLS-i (Thread local storage) pesa. Selleks pole lukke vaja. Pange tähele, et TLS võib käivitada ka värskendussündmusi, et koormuse tasakaalustajad ja muud komponendid saaksid vahemälu, andmestruktuure jne ümber arvutada. See ei kuulu selle postituse ulatusse, kuid seda kasutatakse koodi erinevates kohtades.

Ülaltoodud protseduuri kasutades saab saadik töödelda iga päringut ilma blokeerimiseta (välja arvatud eelnevalt kirjeldatud juhtudel). Peale TLS-koodi enda keerukuse ei pea enamik koodist aru saama, kuidas mitmelõimeline toimib, ja seda saab kirjutada ühe lõimega. See muudab suurema osa koodi kirjutamise lisaks suurepärasele jõudlusele lihtsamaks.

Muud alamsüsteemid, mis kasutavad TLS-i

TLS-i (Thread local storage) ja RCU-d (Read Copy Update) kasutatakse laialdaselt Envoy's.

Näited kasutamise kohta:

  • Funktsionaalsuse muutmise mehhanism täitmise ajal: Praegune lubatud funktsioonide loend arvutatakse põhilõimes. Seejärel antakse igale töötaja lõimele RCU semantika abil kirjutuskaitstud hetktõmmis.
  • Marsruuditabelite vahetamine: RDS-i (Route Discovery Service) pakutavate marsruuditabelite jaoks luuakse marsruuditabelid põhilõime. Kirjutuskaitstud hetktõmmis antakse seejärel igale töötaja lõimele, kasutades RCU (Read Copy Update) semantikat. See muudab marsruuditabelite muutmise aatomiliselt tõhusaks.
  • HTTP päise vahemällu salvestamine: Nagu selgub, on iga päringu HTTP-päise arvutamine (töötades ~25K+ RPS-i tuuma kohta) üsna kulukas. Envoy arvutab päise tsentraalselt umbes iga poole sekundi järel ja edastab selle igale töötajale TLS-i ja RCU kaudu.

On ka teisi juhtumeid, kuid eelmised näited peaksid andma hea ülevaate sellest, milleks TLS-i kasutatakse.

Tuntud jõudluse lõksud

Kuigi Envoy toimib üldiselt üsna hästi, on mõned märkimisväärsed valdkonnad, mis nõuavad tähelepanu, kui seda kasutatakse väga suure samaaegsuse ja läbilaskevõimega:

  • Nagu selles artiklis kirjeldatud, lukustuvad praegu kõik töölõimed juurdepääsulogi mälupuhvrisse kirjutamisel. Suure samaaegsuse ja suure läbilaskevõime korral peate lõppfaili kirjutamisel iga töötaja lõime juurdepääsulogid komplekteerima, kui soovite lõppfaili kirjutada. Teise võimalusena saate iga töötaja lõime jaoks luua eraldi juurdepääsulogi.
  • Kuigi statistika on väga optimeeritud, tekib väga suure samaaegsuse ja läbilaskevõime korral üksikstatistika osas tõenäoline tüli. Selle probleemi lahenduseks on loendurid töötaja lõime kohta koos keskloendurite perioodilise lähtestamisega. Sellest tuleb juttu järgmises postituses.
  • Praegune arhitektuur ei tööta hästi, kui Envoy juurutatakse stsenaariumis, kus olulisi töötlemisressursse nõudvaid ühendusi on väga vähe. Ei ole mingit garantiid, et ühendused jaotuvad töölõngade vahel ühtlaselt. Seda saab lahendada töötajate ühenduste tasakaalustamise rakendamisega, mis võimaldab vahetada ühendusi tööliste lõimede vahel.

Järeldus

Envoy keermestusmudel on loodud pakkuma programmeerimise lihtsust ja tohutut paralleelsust potentsiaalselt raiskava mälu ja ühenduste arvelt, kui see pole õigesti konfigureeritud. See mudel võimaldab sellel väga hästi toimida väga suure niitide arvu ja läbilaskevõimega.
Nagu ma Twitteris lühidalt mainisin, võib kujundus töötada ka täiskasutajarežiimis võrgupinu peal, nagu DPDK (Data Plane Development Kit), mille tulemuseks võivad olla tavalised serverid, mis töötlevad miljoneid päringuid sekundis täis L7 töötlemisega. Väga huvitav on näha, mida järgmise paari aasta jooksul ehitatakse.
Viimane kiire kommentaar: minult on mitu korda küsitud, miks me valisime Envoy jaoks C++. Põhjuseks jääb see, et see on endiselt ainuke laialdaselt kasutatav tööstusklassi keel, millesse selles postituses kirjeldatud arhitektuuri saab ehitada. C++ ei sobi kindlasti kõigi või isegi paljude projektide jaoks, kuid teatud kasutusjuhtudel on see siiski ainuke tööriist töö tegemiseks.

Lingid koodile

Lingid selles postituses käsitletud liideste ja päise rakendustega failidele:

Allikas: www.habr.com

Lisa kommentaar