[Prevod] Model navojev Envoy

Prevod članka: Model navojev Envoy - https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310

Ta članek se mi je zdel precej zanimiv in ker se Envoy najpogosteje uporablja kot del »istio« ali preprosto kot »vstopni krmilnik« kubernetesa, večina ljudi z njim nima enake neposredne interakcije kot na primer s tipičnim Namestitve Nginx ali Haproxy. Če pa se kaj pokvari, bi bilo dobro razumeti, kako deluje od znotraj. Poskušal sem čim več besedila prevesti v ruščino, vključno s posebnimi besedami; za tiste, ki jim je to boleče gledati, sem pustil izvirnike v oklepajih. Dobrodošli pri mački.

Nizkonivojska tehnična dokumentacija za kodno zbirko Envoy je trenutno precej redka. Da bi to popravil, nameravam narediti vrsto objav v spletnem dnevniku o različnih podsistemih Envoy. Ker je to prvi članek, mi prosim povejte, kaj mislite in kaj bi vas utegnilo zanimati v prihodnjih člankih.

Eno najpogostejših tehničnih vprašanj, ki jih prejmem o Envoyju, je zahteva za nizkostopenjski opis modela navojev, ki ga uporablja. V tej objavi bom opisal, kako Envoy preslika povezave v niti, kot tudi sistem Thread Local Storage, ki ga interno uporablja, da naredi kodo bolj vzporedno in visoko zmogljivo.

Pregled niti

[Prevod] Model navojev Envoy

Envoy uporablja tri različne vrste tokov:

  • Glavni: Ta nit nadzoruje zagon in prekinitev procesa, vso obdelavo API-ja XDS (xDiscovery Service), vključno z DNS, preverjanjem zdravja, splošnim upravljanjem gruče in izvajalnega časa, ponastavitvijo statistike, skrbništvom in splošnim upravljanjem procesov - signali Linuxa, vročim ponovnim zagonom itd. dogajanje v tej niti je asinhrono in "neblokirno". Na splošno glavna nit usklajuje vse procese kritične funkcionalnosti, ki za delovanje ne zahtevajo velike količine CPE. To omogoča pisanje večine nadzorne kode, kot da bi bila enonitna.
  • Delavec: Envoy privzeto ustvari delovno nit za vsako nit strojne opreme v sistemu, to je mogoče nadzorovati z možnostjo --concurrency. Vsaka delovna nit zažene »neblokirno« zanko dogodkov, ki je odgovorna za poslušanje vsakega poslušalca; v času pisanja (29. julij 2017) ni bilo drobljenja poslušalca, sprejemanja novih povezav, instanciranja sklada filtrov za povezavo in obdelavo vseh vhodno/izhodnih (IO) operacij med življenjsko dobo povezave. Spet to omogoča, da se večina kode za obravnavo povezave napiše, kot da bi bila enonitna.
  • Splakovalnik datotek: Vsaka datoteka, ki jo zapiše Envoy, predvsem dnevniki dostopa, ima trenutno neodvisno blokirajočo nit. To je posledica dejstva, da pisanje v datoteke, ki jih predpomni datotečni sistem, tudi pri uporabi O_NONBLOCK včasih se lahko blokira (vzdih). Ko morajo delovne niti pisati v datoteko, se podatki dejansko premaknejo v vmesni pomnilnik v pomnilniku, kjer se na koncu izpraznijo skozi nit izpiranje datoteke. To je eno področje kode, kjer lahko tehnično vse delovne niti blokirajo isto ključavnico, medtem ko poskušajo zapolniti medpomnilnik.

Ravnanje s povezavo

Kot je na kratko omenjeno zgoraj, vse delovne niti poslušajo vse poslušalce brez kakršnega koli drobljenja. Tako se jedro uporablja za elegantno pošiljanje sprejetih vtičnic delovnim nitim. Sodobna jedra so v tem na splošno zelo dobra, uporabljajo funkcije, kot je povečanje prioritete vhoda/izhoda (IO), da poskušajo nit zapolniti z delom, preden začnejo uporabljati druge niti, ki prav tako poslušajo isto vtičnico, in tudi ne uporabljajo krožnega sistema. zaklepanje (Spinlock) za obdelavo vsake zahteve.
Ko je povezava sprejeta v delovni niti, te niti nikoli ne zapusti. Vsa nadaljnja obdelava povezave se v celoti obravnava v delovni niti, vključno s kakršnim koli posredovanjem.

To ima več pomembnih posledic:

  • Vsa področja povezav v Envoyju so dodeljena delovni niti. Torej, čeprav področja povezav HTTP/2 vzpostavljajo samo eno povezavo z vsakim gostiteljem navzgor v toku, bodo v stabilnem stanju, če obstajajo štiri delovne niti, štiri povezave HTTP/2 na gostitelja navzgor.
  • Razlog, zakaj Envoy deluje na ta način, je, da je mogoče skoraj vso kodo napisati brez blokiranja in tako, kot če bi bila enonitna, če vse ohranite v eni sami delovni niti. Ta zasnova olajša pisanje velike količine kode in se neverjetno dobro prilagaja skoraj neomejenemu številu delovnih niti.
  • Vendar pa je eden od glavnih zaključkov ta, da je z vidika pomnilniškega področja in učinkovitosti povezave dejansko zelo pomembno konfigurirati --concurrency. Če imate več delovnih niti, kot je potrebno, boste porabili pomnilnik, ustvarili več nedejavnih povezav in zmanjšali stopnjo združevanja povezav. Pri Lyftu naši zabojniki z bočnimi prikolicami Envoy delujejo z zelo nizko sočasnostjo, tako da se zmogljivost približno ujema s storitvami, poleg katerih stojijo. Envoy izvajamo kot robni proxy samo pri največji sočasnosti.

Kaj pomeni neblokada?

Izraz "neblokiranje" je bil doslej večkrat uporabljen, ko smo razpravljali o delovanju glavne in delovne niti. Vsa koda je napisana ob predpostavki, da ni nikoli nič blokirano. Vendar to ne drži povsem (kaj ne drži povsem?).

Envoy uporablja več dolgih procesnih ključavnic:

  • Kot smo že omenili, pri pisanju dnevnikov dostopa vse delovne niti pridobijo isto zaklepanje, preden se napolni medpomnilnik dnevnika v pomnilniku. Čas zadrževanja zaklepanja bi moral biti zelo nizek, vendar je možno, da se zaklepanje izpodbija pri visoki sočasnosti in visoki prepustnosti.
  • Envoy uporablja zelo zapleten sistem za obdelavo statističnih podatkov, ki so lokalni za nit. To bo tema posebne objave. Vendar bom na kratko omenil, da je kot del lokalne obdelave statistike niti včasih potrebno pridobiti ključavnico na osrednji "shrambi statistike". To zaklepanje nikoli ne bi smelo biti potrebno.
  • Glavna nit se mora občasno usklajevati z vsemi delavskimi nitmi. To se izvede z "objavljanjem" iz glavne niti v delovne niti in včasih iz delovnih niti nazaj v glavno nit. Pošiljanje zahteva zaklepanje, tako da se lahko objavljeno sporočilo postavi v čakalno vrsto za kasnejšo dostavo. Teh ključavnic ne bi smeli nikoli resno izpodbijati, vendar jih je tehnično vseeno mogoče blokirati.
  • Ko Envoy zapiše dnevnik v sistemski tok napak (standardna napaka), pridobi zaklepanje celotnega procesa. Na splošno velja, da je lokalno beleženje Envoy grozno z vidika zmogljivosti, zato ni bilo veliko pozornosti namenjene njegovemu izboljšanju.
  • Obstaja nekaj drugih naključnih ključavnic, vendar nobena od njih ni kritična glede delovanja in se je ne sme nikoli izpodbijati.

Niti lokalni pomnilnik

Zaradi načina, na katerega Envoy ločuje odgovornosti glavne niti od odgovornosti delavske niti, obstaja zahteva, da se kompleksna obdelava lahko izvede v glavni niti in se nato zagotovi vsaki delovni niti na zelo sočasen način. Ta razdelek opisuje lokalni pomnilnik Envoy Thread (TLS) na visoki ravni. V naslednjem razdelku bom opisal, kako se uporablja za upravljanje gruče.
[Prevod] Model navojev Envoy

Kot je bilo že opisano, glavna nit obravnava skoraj vse funkcije ravni upravljanja in nadzora v procesu Envoy. Nadzorna ravnina je tukaj nekoliko preobremenjena, a ko jo pogledate znotraj samega procesa Envoy in jo primerjate s posredovanjem, ki ga izvajajo delavske niti, je smiselno. Splošno pravilo je, da proces glavne niti opravi nekaj dela, nato pa mora vsako delovno nit posodobiti glede na rezultat tega dela. v tem primeru delovni niti ni treba pridobiti zaklepanja pri vsakem dostopu.

Envoyjev sistem TLS (Thread local storage) deluje na naslednji način:

  • Koda, ki se izvaja v glavni niti, lahko dodeli režo TLS za celoten proces. Čeprav je to abstraktno, je v praksi indeks v vektorju, ki zagotavlja O(1) dostop.
  • Glavna nit lahko v svojo režo namesti poljubne podatke. Ko je to storjeno, so podatki objavljeni v vsaki delovni niti kot običajni dogodek zanke dogodkov.
  • Delovne niti lahko berejo iz svoje reže TLS in pridobijo vse lokalne podatke niti, ki so tam na voljo.

Čeprav je zelo preprosta in neverjetno močna paradigma, je zelo podobna konceptu blokiranja RCU (Read-Copy-Update). V bistvu delovne niti nikoli ne vidijo sprememb podatkov v režah TLS, medtem ko delo poteka. Sprememba se pojavi samo v času počitka med delovnimi dogodki.

Envoy to uporablja na dva različna načina:

  • S shranjevanjem različnih podatkov v vsako delovno nit je mogoče dostopati do podatkov brez kakršnega koli blokiranja.
  • Z vzdrževanjem skupnega kazalca na globalne podatke v načinu samo za branje v vsaki delovni niti. Tako ima vsaka delovna nit število podatkovnih sklicev, ki ga med izvajanjem dela ni mogoče zmanjšati. Šele ko se vsi delavci umirijo in naložijo nove skupne podatke, bodo stari podatki uničeni. To je identično RCU.

Niti za posodobitev gruče

V tem razdelku bom opisal, kako se TLS (Thread local storage) uporablja za upravljanje gruče. Upravljanje gruče vključuje xDS API in/ali obdelavo DNS ter preverjanje stanja.
[Prevod] Model navojev Envoy

Upravljanje toka gruče vključuje naslednje komponente in korake:

  1. Upravitelj gruče je komponenta v Envoyju, ki upravlja vse znane navzgornje povezave gruče, API storitve za odkrivanje gruče (CDS), API-je storitve tajnega odkrivanja (SDS) in storitve odkrivanja končne točke (EDS), DNS in aktivna zunanja preverjanja. Odgovoren je za ustvarjanje "končno konsistentnega" pogleda vsake gorvodne gruče, ki vključuje odkrite gostitelje in zdravstveno stanje.
  2. Preverjevalnik zdravja izvede aktivni pregled zdravja in sporoči spremembe zdravstvenega stanja upravitelju gruče.
  3. CDS (Cluster Discovery Service) / SDS (Secret Discovery Service) / EDS (Endpoint Discovery Service) / DNS se izvajajo za določitev članstva v gruči. Sprememba stanja se vrne upravitelju gruče.
  4. Vsaka delovna nit neprekinjeno izvaja zanko dogodkov.
  5. Ko upravitelj gruče ugotovi, da se je stanje gruče spremenilo, ustvari nov posnetek stanja gruče samo za branje in ga pošlje vsaki delovni niti.
  6. V naslednjem mirnem obdobju bo delovna nit posodobila posnetek v dodeljeni reži TLS.
  7. Med V/I dogodkom, ki naj bi določil gostitelja za uravnoteženje obremenitve, bo izravnalnik obremenitve zahteval režo TLS (Thread local storage) za pridobitev informacij o gostitelju. To ne zahteva ključavnic. Upoštevajte tudi, da lahko TLS sproži tudi dogodke posodobitve, tako da lahko izravnalniki obremenitve in druge komponente ponovno izračunajo predpomnilnike, podatkovne strukture itd. To presega obseg te objave, vendar se uporablja na različnih mestih v kodi.

Z zgornjim postopkom lahko Envoy obdela vsako zahtevo brez blokiranja (razen kot je opisano prej). Poleg zapletenosti same kode TLS večini kode ni treba razumeti, kako deluje večnitnost, in jo je mogoče napisati enonitno. To poleg vrhunske zmogljivosti olajša pisanje večine kode.

Drugi podsistemi, ki uporabljajo TLS

TLS (Thread local storage) in RCU (Read Copy Update) se pogosto uporabljata v Envoyju.

Primeri uporabe:

  • Mehanizem za spreminjanje funkcionalnosti med izvajanjem: Trenutni seznam omogočenih funkcij se izračuna v glavni niti. Vsaka delovna nit nato dobi posnetek samo za branje z uporabo semantike RCU.
  • Zamenjava tabel poti: Za tabele poti, ki jih zagotavlja RDS (Route Discovery Service), se tabele poti ustvarijo v glavni niti. Posnetek samo za branje bo naknadno zagotovljen vsaki delovni niti z uporabo semantike RCU (Read Copy Update). Zaradi tega je spreminjanje tabel poti atomsko učinkovito.
  • Predpomnjenje glave HTTP: Izkazalo se je, da je izračun glave HTTP za vsako zahtevo (medtem ko se izvaja ~25K+ RPS na jedro) precej drag. Envoy centralno izračuna glavo približno vsake pol sekunde in jo posreduje vsakemu delavcu prek TLS in RCU.

Obstajajo tudi drugi primeri, vendar bi morali prejšnji primeri dobro razumeti, za kaj se uporablja TLS.

Znane pasti zmogljivosti

Medtem ko Envoy na splošno deluje precej dobro, obstaja nekaj pomembnih področij, ki zahtevajo pozornost, ko se uporablja z zelo visoko sočasnostjo in prepustnostjo:

  • Kot je opisano v tem članku, trenutno vse delovne niti pridobijo zaklepanje, ko pišejo v medpomnilnik pomnilnika dnevnika dostopa. Pri visoki sočasnosti in visoki prepustnosti boste morali sestaviti dnevnike dostopa za vsako delovno nit na račun nepravilne dostave pri pisanju v končno datoteko. Lahko pa ustvarite ločen dnevnik dostopa za vsako delovno nit.
  • Čeprav so statistike zelo optimizirane, bo pri zelo visoki sočasnosti in prepustnosti verjetno prišlo do atomskega spora glede posameznih statistik. Rešitev te težave so števci na delovno nit s periodično ponastavitvijo centralnih števcev. O tem bomo razpravljali v naslednji objavi.
  • Trenutna arhitektura ne bo delovala dobro, če bo Envoy nameščen v scenariju, kjer je zelo malo povezav, ki zahtevajo znatne procesorske vire. Nobenega zagotovila ni, da bodo povezave enakomerno porazdeljene med delovne niti. To je mogoče rešiti z implementacijo izravnave delavskih povezav, ki bo omogočila izmenjavo povezav med delavskimi nitmi.

Zaključek

Nitni model Envoy je zasnovan tako, da zagotavlja enostavno programiranje in množično vzporednost na račun potencialno potratnega pomnilnika in povezav, če niso pravilno konfigurirani. Ta model omogoča zelo dobro delovanje pri zelo velikem številu niti in prepustnosti.
Kot sem na kratko omenil na Twitterju, lahko zasnova deluje tudi na vrhu omrežnega sklada v polnem uporabniškem načinu, kot je DPDK (Data Plane Development Kit), kar lahko povzroči, da običajni strežniki obravnavajo milijone zahtev na sekundo s polno obdelavo L7. Zelo zanimivo bo videti, kaj bo zgrajeno v naslednjih nekaj letih.
Še zadnji hiter komentar: velikokrat so me vprašali, zakaj smo za Envoy izbrali C++. Razlog ostaja v tem, da je to še vedno edini široko uporabljen industrijski jezik, v katerem je mogoče zgraditi arhitekturo, opisano v tej objavi. C++ zagotovo ni primeren za vse ali celo veliko projektov, vendar je za določene primere uporabe še vedno edino orodje za opravljanje dela.

Povezave do kode

Povezave do datotek z vmesniki in izvedbami glav, obravnavanimi v tej objavi:

Vir: www.habr.com

Dodaj komentar