[Tõlge] Envoy mudelide voogude (Envoy threading model)

Artikli tõlge: Envoyi lõimemudel — https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310

See artikkel tundus mulle üsna huvitav, kuna Envoyd kasutatakse kõige sagedamini osana 'istio'st' või lihtsalt kui 'ingress controller' kubernetes'is, seega ei puutu enamik inimesi sellega sama otseselt kokku kui näiteks tavaliste Nginxi või Haproxy installatsioonidega. Kuid kui midagi katki läheb, oleks hea mõista, kuidas see seestpoolt töötab. Olen püüdnud tõlkida nii palju sisu vene keelde, kui võimalik, sealhulgas erilise sõnavara jaoks, ning kellele selline asi ei meeldi, olen originaalid sulgudes jätnud. Tere tulemast lõppu.

Envoy koodibaasi madala taseme tehniline dokumentatsioon on praegu üsna napp. Selle parandamiseks plaanin teha seeria blogipostitusi erinevatest Envoy alamsüsteemidest. Kuna see on esimene artikkel, andke mulle teada, mida te arvate ja mis võiks järgmistes artiklites huvitav olla.

Üks levinumaid tehnilisi küsimusi, mida ma Envoy kohta saan, on madala taseme kirjelduse küsimine kasutatavast lõimemudelist (threading model). Selles postituses kirjeldan, kuidas Envoy seob ühendused lõimedele, samuti süsteemi kohalikku lõimede salvestust (Thread Local Storage), mida kasutatakse sees, et muuta kood paralleelsemaks ja suurema jõudlusega.

Lõimede kirjeldus (Threading overview)

[Tõlge] Envoy mudelide voogude (Envoy threading model)

Envoy kasutab kolme erinevat tüüpi lõime:

  • Peamine (Main): See lõim haldab protsessi käivitamist ja lõpetamist, kogu XDS (xDiscovery Service) API töötlemist, sealhulgas DNS-i, tervisekontrolli (health checking), klastrihalduse ja teenuse tööprotsessi (runtime), statistika lähtestamist, haldust ja üldist protsesside haldust — Linuxi signaalid, kuum käivitamine (hot restart) jne. Kõik, mis selles lõimes toimub, on asünkrooniline ja 'mitteblokeeriv'. Üldiselt koordineerib peamine lõim kõik kriitilised funktsionaalsuse protsessid, milleks ei ole vajalik suur hulk CPU-d. See võimaldab enamiku halduskoode kirjutada nii, nagu oleks see ühesüsteemne.
  • Töötaja (Worker): Vaikimisi loob Envoy töötava lõime (worker thread) iga süsteemi riistvaralise lõime jaoks, mida saab kontrollida valiku abil --concurrency. Iga töövoog käivitab "mitteblokeeriva" sündmuste tsükli (event loop), mis vastutab iga kuulaja (listener) jälgimise eest. Artikli kirjutamise ajal (29. juuli 2017) ei ole kuulaja (listener) segmenteerimist (sharding), uute ühenduste vastuvõtmine, filtrite virna loomine ühenduse loomiseks ja kõik sisend-väljundi (IO) toimingute töötlemine ühenduse eksisteerimise ajal. Taaskord võimaldab see kirjutada suure osa ühenduste töötlemise koodist nagu see oleks ühesuunaline.
  • Faili (File flusher): Iga fail, mida Envoy kirjutab, peamiselt juurdepääsu logid (access logs), omab praegu sõltumatut blokeerivat töövoogu. See on tingitud asjaolust, et kirjutamine failidesse, mis on vahemälus failisüsteemi poolt, isegi O_NONBLOCK kasutamisel O_NONBLOCK võib mõnikord blokeerida (ahh). Kui töövoogudel on vaja faili kirjutada, liigutatakse andmed tegelikult mälu bufrisse, kus need lõpuks tõukatakse läbi file flush. See on üks koodivaldkondi, kus tehniliselt võivad kõik töövood (worker threads) blokeerida (block) sama lukku (lock), püüdes mälu bufrisse täituda.

Ühenduste töötlemine (Connection handling)

Kuidas arutleti lühidalt eespool, kuulevad kõik töövood kõiki kuulajaid (listeners) ilma igasuguse segmenteerimiseta. Seega kasutatakse tuuma goed õige socket’itega töötlemiseks töövoogude vahel. Kaasaegsed tuumad on selles osas üsna head, nad kasutavad selliseid funktsioone nagu sisendi/väljundi (IO) prioriteedi tõstmine, et püüda täita voogu enne, kui nad hakkavad kasutama teisi vooge, mis kuulavad ka sama socket’it, ja samuti mitte kasutada tsüklit lukustumiseks (Spinlock) iga päringu töötlemiseks.
Kuna ühendus on töövoo (worker thread) poolt vastu võetud, ei lahku see kunagi sellest voost (thread). Kõik edasine ühenduse töötlemine toimub täielikult töövoos (worker thread), sealhulgas mis tahes edasi suunamise käitumine (forwarding behavior).

Sel on mitu olulist tagajärge:

  • Kõik Envoy ühenduste basseinid kuuluvad töövoogude hulka. Seetõttu, kuigi HTTP/2 ühenduste basseinid loovad iga ülesvoolu hostiga korraga vaid ühe ühenduse, on neli töövoogu korral neli HTTP/2 ühendust ülesvoolu hostiga stabiilses olekus.
  • Põhjus, miks Envoy nii töötab, on see, et hoides kõik ühes töövoos, saab enamus koodist kirjutada ilma lukustusteta ja nagu ühesuunaline. See disain lihtsustab suure koodi kirjutamist ja skaleerub uskumatult hästi praktiliselt piiramatu arvu töövoogude jaoks.
  • Kuid üks peamisi järeldusi on see, et mälu- ja ühenduste basseinide efektiivsuse seisukohalt on tõeliselt oluline konfigureerida parameeter --concurrency. Liigne töövoogude arv toob kaasa mälu raiskamise, suure hulga passiivsete ühenduste tekkimise ja ühenduste basaeni sisenemise kiirus väheneb. Lyftis töötavad meie Envoy sidecar konteinerid väga madala paralleelsuse juures, nii et jõudlus vastab umbes nende teenuste tasemele, mille kõrval nad asuvad. Käivitame Envoy piiriprotokollina (edge) ainult maksimaalse paralleelsuse korral.

Mida tähendab mitte-blokeeriv režiim (What non-blocking means)

Termin 'mitte-blokeeriv' on kuni nüüd korduvalt kasutatud arutades, kuidas töötavad põhivoog ja töövood. Kogu kood on kirjutatud eeldusel, et miski ei blokeeru kunagi. Kuid see ei ole täiesti tõsi (mis ei ole täiesti tõsi?).

Envoy kasutab mitmeid pikaajalisi protsessi lukustusi:

  • Nagu varem mainitud, saavad kõik töövood juurdepääsulogide kirjutamise ajal sama lukustuse enne mälus logifaili puhverdamist. Lukustuse hoidmise aeg peaks olema väga madal, kuid on võimalik, et see lukustus vaidlustatakse kõrge paralleelsuse ja kõrge läbilaskevõime korral.
  • Envoy kasutab voogude statistika töötlemiseks väga keerulist süsteemi, mis on voogudele lokaalset. See on eraldi postituse teema. Siiski mainin lühidalt, et lokaalse statistika töötlemise osana on mõnikord vajalik keskse 'statistikalao' lukustamine. Seda lukustamist ei tohiks kunagi nõuda.
  • Peavoo vajab perioodiliselt koordineerimist kõigi töövoogudega. Seda tehakse, 'publitseerides' peavoo sõnumid töövoogudele ja mõnikord ka töövoogudest tagasi peavoo. Sõnumi saatmiseks on vajalik lukustus, et avaldatud sõnum saaks järjekorda panna edasise kohaletoimetamise jaoks. Need lukustused ei tohiks kunagi olla tõsise konkurentsi all, kuid need võivad siiski tehniliselt saada blokeeritud.
  • Kui Envoy kirjutab logi süsteemi veavoo (standard error), siis sellel on blokeering kogu protsessis. Üldiselt peetakse Envoy lokaalset logimist jõudluse seisukohast kohutavaks, seega ei pöörata selle parandamisele palju tähelepanu.
  • On mõned muud juhuslikud lukustused, kuid ükski neist ei ole kriitiline jõudluse jaoks ja neid ei tohiks kunagi vaidlustada.

Thread local storage (voo lokaalne salvestus)

Kuna Envoy eristab peavoo ja töövoogude kohustusi, on nõudmine, et keeruline töötlemine võiks toimuda peavoo kaudu ja seejärel jagada seda kõrge paralleelsusega igale töövoole. Selles jaotises on kõrgel tasemel kirjeldatud Envoy Thread Local Storage (TLS) süsteemi. Järgmises jaotises kirjeldan, kuidas seda kasutatakse klastrihalduseks.
[Tõlge] Envoy mudelide voogude (Envoy threading model)

Kuna juba mainitud, tegeleb peavoo praktiliselt kõigi haldusfunktsioonide ja juhtimistasandi funktsionaalsusega Envoy protsessis. Juhtimistasand on siin natuke ülekoormatud, kuid kui vaadata seda itse Envoy protsessi kontekstis ja võrrelda töövoogude edastamisega, tundub see mõistlik. Üldiselt täidab peavoo mõningaid ülesandeid ja seejärel tuleb igat töövoogu uuendada vastavalt nende töö tulemustele. samal ajal ei pea töösuuna iga juurdepääsu korral lukustust seadma.

TLS (Thread local storage) süsteem Envoy toimib järgmiselt:

  • Kood, mis töötab põhijuhtmes, võib eraldada TLS-slotid kogu protsessi jaoks. Kuigi see on abstraktne, on praktikas tegemist indeksiga vektorisse, mis tagab O(1) juurdepääsu.
  • Põhijuhtme korral võib see seada suvalisi andmeid oma slotis. Kui see on tehtud, avaldatakse andmed iga töösuuna puhul tavalise sündmuste tsükli sündmusena.
  • Töösuunad saavad lugeda oma TLS-slotist ja välja võtta seal saadaval olevaid kohalikke andmeid.

Kuigi see on väga lihtne ja äärmiselt võimas paradigma, mis on väga sarnane RCU (Read-Copy-Update) lukustuse kontseptsioonile. Essentsiaalselt ei näe töösuunad töö käigus sloti TLS andmete muudatusi. Muudatus toimub ainult rahuperioodil töö sündmuste vahel.

Envoy kasutab seda kahel erineval viisil:

  • Hoides erinevaid andmeid igas töösuunas, tagatakse nendele andmetele juurdepääs ilma mingisuguse lukustamiseta.
  • Hoides igas töösuunas üldist näidikut globaalsetele andmetele, mis on „ainult lugemiseks.“ Seega on igal töösuunal viidatud andmete viidatud loenduri, mida ei saa töö käigus vähendada. Ainult kui kõik töötajad rahunevad ja laadivad uusi üldisi andmeid, hävitatakse vanad andmed. See on identne RCU-ga.

Klastri värskendamise protsess

Selles osas kirjeldan, kuidas TLS (Thread local storage) kasutatakse klastri haldamiseks. Klastri haldamine hõlmab API xDS ja / või DNS töötlemist, samuti tervise kontrollimist.
[Tõlge] Envoy mudelide voogude (Envoy threading model)

Klastri töösuunade haldamine hõlmab järgmisi komponente ja etappe:

  1. Klastri haldaja on komponent, mis asub Envoy sees, ja see haldab kõiki tuntud klastri upstream'e, CDS (Cluster Discovery Service) API, SDS (Secret Discovery Service) ja EDS (Endpoint Discovery Service) API-sid, DNS-i ning aktiivseid väliseid tervise kontrollimise protsesse. See vastutab iga klastri upstreami „lõpuks järjekindla“ (eventually consistent) arusaama loomise eest, mis hõlmab avastatud hoste ja tervise staatust.
  2. Tööoleku kontrollimise tööriist (health checker) teeb aktiivset tööoleku kontrolli ja teatab tööoleku muutustest klastrihaldurile.
  3. CDS (Cluster Discovery Service) / SDS (Secret Discovery Service) / EDS (Endpoint Discovery Service) / DNS kasutatakse klastrisse kuuluvuse määramiseks. Oleku muutus edastatakse klastrihaldurile.
  4. Iga töövoog täidab pidevalt sündmuste töötlemise tsüklit.
  5. Kui klastrihaldur määrab, et klastrite olek on muutunud, loob ta klastrite uut oleku snapshots, mis on ainult lugemiseks ja saadab selle igasse töövoogu.
  6. Järgneva puhkuste jooksul uuendab töövoog snapshots eraldatud TLS-slottis.
  7. Sisend / väljund sündmuse ajal, mis peaks määrama koormuse tasakaalustamiseks hosti, küsib tasakaalustaja TLS-slotist (Thread local storage) hosti teavet. Selleks ei nõuta lukustusi. Samuti on oluline märkida, et TLS võib ka algatada sündmusi uuendamisel, et koormuse tasakaalustamise süsteemid ja muud komponendid saaksid arvutada uuesti vahemälud, andmestruktuurid jne. See ületab selle postituse ulatuse, kuid on kasutuses mitmetes koha koodis.

Kasutades ülaltoodud protseduuri, suudab Envoy igat päringut käsitleda ilma lukustusteta (välja arvatud eelnevalt kirjeldatud). Lisaks TLS-i koodi keerukusele ei pea enamik koodist mõistma, kuidas korraga töötamine töötab, ja selle võib kirjutada ühesuunalist režiimi. See lihtsustab suure osa koodi kirjutamist ja tagab suurepärase jõudluse.

Teised subsüsteemid, mis kasutavad TLS

TLS (Thread local storage) ja RCU (Read Copy Update) on laialdaselt kasutusel Envoy-s.

Kasutuse näited:

  • Jooksvate funktsioonide muutmise mehhanism: Käimasolev funktsionaalsuse loend arvutatakse põhivoos. Siis antakse igale töövoole lugemiseks ainult snapshot, kasutades RCU semantikat.
  • Maršrutustabelite asendamine: marsruutide tabelite jaoks, mida pakub RDS (Route Discovery Service), luuakse marsruutide tabelid peavoolus. Edasi antakse iga töösuuna jaoks ainult lugemiseks mõeldud koopia, kasutades RCU (Read Copy Update) semantikat. See muudab marsruutide tabelite muutmise aatomiliselt efektiivseks.
  • HTTP päiste vahemälu: Kuidas selgub, on iga päringu HTTP päise arvutamine (kui teostatakse ~25K+ RPS tuuma kohta) üsna kulukas. Envoy arvutab päise tsentraalselt umbkaudu iga poole sekundi tagant ja edastab selle igale töötajale läbi TLS ja RCU.

On ka teisi juhtumeid, kuid eelnevad näited peaksid andma hea arusaama, milleks kasutatakse TLS-i.

Tuntud jõudlusprobleemid (Known performance pitfalls)

Kuigi Envoy töötab üldiselt hästi, on mõned tuntud valdkonnad, mis vajavad tähelepanu, kui seda kasutatakse väga kõrge samasuse ja läbilaskevõimega:

  • Nagu käesolevas artiklis on juba kirjeldatud, saavad kõik töötajate vood kirjutamise ajal ligipääsu logi vahemälule lukustatud. Suure samasuse ja kõrge läbilaskevõime korral on vajalik ligipääsu logide paketiseerimine iga töövoo jaoks, mis saavutatakse lõppfaili kirjutamise juures mittejärjekindla kohaletoimetamisega. Alternatiivina on võimalik luua igale töövoole eraldi ligipääsu logi.
  • Kuigi statistika on väga optimeeritud, on väga kõrge samasuse ja läbilaskevõime korral tõenäoliselt aatomiline konkurents individuaalse statistika osas. Selle probleemi lahendamiseks on soovitatavad ühe töövoo loenduriga periodilised keskmised loendurid. Seda arutatakse järgmises postituses.
  • Olemasolev arhitektuur ei tööta hästi, kui Envoy on juurutatud stsenaariumis, kus on väga vähe ühendusi, mis nõuavad intensiivset ressursside töötlemist. Pole garanteeritud, et ühendused jaotuvad töötajate voogude vahel ühtlaselt. Seda on võimalik lahendada tööde ühenduste tasakaalustamisega, rakendades ühenduste vahetamise võimalust töövoogude vahel.

Kokkuvõte (Conclusion)

Envoy voogemudel on loodud programmeerimise ja massilise paralleelsuse lihtsustamiseks, potentsiaalselt raiskavale mälule ja ühendustele, kui neid ei konfigureerita õigesti. See mudel võimaldab tal väga hästi töötada väga kõrge voogude ja läbilaskevõime korral.
Nagu ma lühidalt Twitteris mainisin, võib disain töötada ka täisfunktsionaalse võrgu stack'i peal kasutaja režiimis, näiteks DPDK (Data Plane Development Kit), mis võib viia selleni, et tavased serverid töötlevad miljonite päringute sekundis täieliku L7 töötlemise juures. Oleme väga huvitatud nägema, mis järgmise paariaasta jooksul saab ehitatud.
Viimane kiire kommentaar: olen korduvalt küsinud, miks me valisime C ++ Envoy jaoks. Põhjus on endiselt see, et see on ainus laialdaselt levinud tööstuslik tase keel, millega saab ehitada arhitektuuri, millest selles postituses rääkisime. C ++ ei sobi kindlasti kõigile või isegi paljudele projektidele, kuid teatud kasutusjuhtumite jaoks on see endiselt ainus tööriist töö tegemiseks.

Koodilingid (Links to code)

LingidFailide kohta, mille liideste ja rakenduste pealkirjad selles postituses arutatakse:

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster