Artikli tõlge:
See artikkel tundus mulle piisavalt huvitav, ja kuna Envoy-d kasutatakse kõige sagedamini osana «istio» või lihtsalt «ingress controller»'ina Kubernetes'es, ei ole enamikel inimestel sellega nii otsest kokkupuudet kui näiteks tavaliste Nginx või Haproxy seadistustega. Kui aga midagi läheb valesti, oleks hea mõista, kuidas see seestpoolt toimib. Püüdlesin tõlkida nii palju teksti vene keelde, sealhulgas spetsiaalseid termineid, ja nende jaoks, kellele see ebamugav on, jätsin originaalid sulgudesse. Tere tulemast artikli juurde.
Envoy koodibaasi madala taseme tehniline dokumentatsioon on praegu üsna napp. Selle parandamiseks plaanin koostada seeria blogiartikleid erinevate Envoy alam-süsteemide kohta. Kuna see on esimene artikkel, andke palun teada, mida te arvate ja mis teid võiks järgmistes artiklites huvitada.
Üks levinumaid tehnilisi küsimusi, mida ma Envoy kohta saan, on nõudmine madalama taseme kirjeldamiseks kasutatava lõngade mudeli (threading model) kohta. Selles postituses kirjeldan, kuidas Envoy seondab ühendusi lõngadega, ning selgitan lõngade kohaliku salvestamise süsteemi (Thread Local Storage), mida kasutatakse seespool, et koodi rohkem paralleelseks ja suure jõudlusega muuta.
Lõngade ülevaade (Threading overview)
![[Перевод] Envoy mudel voogudest (Envoy threading model)](/wp-content/uploads/2019/04/c5028463db1eb5b07ccb1a3af1f64a03.jpeg)
Envoy kasutab kolme erinevat tüüpi lõngasid:
- Peamine (Main): See lõng haldab protsessi käivitamist ja lõpetamist, kogu XDS (xDiscovery Service) API töötlemist, sealhulgas DNS-i, töökindluse kontrollimist (health checking), klastrihalduse ja teenusrakenduse (runtime) üldhaldust, statistika lähtestamist, haldamist ja protsesside üldhaldust — Linuxi signaalid, kuum ümberlaadimine (hot restart) jne. Kõik, mis selles lõngas toimub, on asünkroonne ja 'blokeerimata'. Üldiselt koordineerib peamine lõng kõiki kriitilisi funktsionaalsuse protsesse, mille täitmiseks ei ole vaja suurt protsessorikoormust. See võimaldab suurema osa halduskoode kirjutada justkui see oleks ühe lõnga kasutamine.
- Töötaja (Worker): Oletame, et Envoy loob vaikimisi iga süsteemi riistvaratiheduse jaoks töötlustee (worker thread), mida saab juhtida kasutades valikut
--concurrency. Iga töötaja thread käivitab 'mittesünkimise' sündmuse tsükli (event loop), mis vastutab iga kuulaja (listener) jälgimise (listening) eest. Artikli kirjutamise hetkeks (29. juuli 2017) ei ole kuulaja (listener) segmenteerimist (sharding), uute ühenduste vastuvõtmist, filtrikihi exemaari loomist ning kõikide sisend-väljund (IO) operatsioonide töötlemist kogu seose kestuse jooksul. See võimaldab suure osa ühenduste töötlemise koodist kirjutada nagu see oleks üheaegne. - Faili (File flusher): Iga fail, mille kirjutab Envoy, põhiliselt juurdepääsupäevikud (access logs), omab praegu sõltumatut blokeerivat töötlejat. See tuleneb sellest, et failidesse kirjutamine, mis on failisüsteemi poolt vahemälustatud, isegi kui kasutatakse
O_NONBLOCKvõib mõnikord blokeeruda (ohates). Kui töötlejatest on vaja kirjutada faili, siis tõstetakse andmed tegelikult mälu puhvri, kust need lõpuks kirjutatakse kaudu. faili puhastamine. See on üks koode valdkondi, kus tehniliselt võivad kõik töövood (worker threads) blokeerida (block) sama lukku (lock), püüdes täita mälupuhvrit.
Ühenduste töötlemine (Connection handling)
Kuna eelnevalt lühidalt arutatud, kuulavad kõik töövood kõiki kuulajaid (listeners) ilma igasuguse segmenteerimiseta. Nii kasutatakse tuuma (kernel), et tõhusalt suunata vastu võetud sokked töövoogudele. Kaasaegsed tuumad on selles osas väga head, nad kasutavad funktsioone nagu sisendi- ja väljundi prioriteedi tõstmine (IO), et püüda täita voogu tööga, enne kui hakkavad kasutama teisi vooge, mis samuti kuulavad sama sokki, ning vältida iga päringu töötlemiseks tsüklit (Spinlock).
Niipea kui ühendus on vastu võetud töövoos (worker thread), ei lahku see kunagi sellest voost (thread). Kogu edasine ühenduse töötlemine toimub täielikult töövoos (worker thread), sealhulgas kõik edastamise käitumine (forwarding behavior).
See toob kaasa mitmeid olulisi tagajärgi:
- Kõik Envoy ühenduste bassid kuuluvad töövoogu. Seega, kuigi HTTP/2 ühenduste bassid loovad iga üleliidese hostiga vaid ühe ühenduse korraga, on nelja töövoolu korral neliv põhiköidikud üleliidese hostiga stabiilses olekus.
- Põhjus, miks Envoy nii töötab, on see, et hoides kõik ühes töövoos, saab peaaegu kogu koodi kirjutada blokeeringuteta ja justkui ühe lõimega. See disain lihtsustab suure hulga koodi kirjutamist ja skaleerub uskumatult hästi peaaegu piiramatule arvule töövoogudele.
- Siiski, üks peamisi järeldusi on see, et tõhususe seisukohalt on ühenduste ja mälu basseini seadistamine tegelikult väga oluline.
--concurrency. Liigne voolude arv peaks olema vajalik, vastasel juhul võib see põhjustada mälu kadu, rohkem passiivseid ühendusi ja madalamat kiirusel ligipääsu ühenduste basseinile. Lyftis töötavad meie envoy sidecar konteinerid väga madala paralleelsusega, seega jõudlus vastab ligikaudu neile teenustele, mille kõrval nad asuvad. Me käivitame Envoy piiriproxina (edge) ainult maksimaalse paralleelsuse korral.
Mis tähendab mitteblokeeriv režiim (What non-blocking means)
Mõistet 'mittetõkestav' on seni mitu korda kasutatud arutamisel, kuidas töötavad põhijooned ja töövood. Kood on kirjutatud eeldusel, et miski ei blokeeri kunagi. Siiski, see pole päris tõsi (mis ei ole päris tõsi?).
Envoy kasutab mitmeid pikaajalisi protsessi blokeeringuid:
- Nagu juba mainitud, saavad kõik töövood sama lukustuse, kui kirjutatakse juurdepääsu logisid enne, kui logi puhver mällu täidetakse. Lukustuse hoidmise aeg peab olema väga madal, kuid on võimalik, et seda lukustust vaieldakse kõrge paralleelsuse ja kõrge läbilaskevõime korral.
- Envoy kasutab väga keerulist süsteemi statistika töötlemiseks, mis on voogude lõikes lokaalne. See on eraldi postituse teema. Siiski mainin lühidalt, et voogude lokaalse statistika töötlemise osana võib mõnikord olla vajalik saada lukustus keskse „statistika salvestamise“ jaoks. Seda lukustust ei tohi kunagi vajada.
- Peamine töövoog vajab aeg-ajalt koordineerimist kõigi töövoogudega. See toimub nii, et peamisest töövoogust 'avaldatakse' teavet töövoogudesse ja mõnikord ka töövoogudest tagasi peamisele töövoole. Edastamiseks on vajalik lukustus, et avaldatud sõnum saaks järjekorda panna hilisemaks edastamiseks. Need lukustused ei tohi kunagi olla tõsise konkurentsi all, kuid need võivad ikkagi tehniliselt lukku minna.
- Kui Envoy kirjutab logi süsteemi veavoolu (standard error), saadakse kogu protsessi lukustus. Üldiselt peetakse lokaalset logimist Envoy poolt halva soorituse seisukohalt kohutavaks, mistõttu sellele ei pöörata palju tähelepanu.
- On mõned teised juhuslikud lukustused, kuid need ei ole tootlikkuse jaoks kriitilised ja neid ei tohiks kunagi vaidlustada.
Niidi lokaalne salvestamine (Thread local storage)
Kuna viisi, kuidas Envoy eraldab peamise ja töötava lõime ülesanded, on nõue, et keeruline töötlemine võiks toimuda peamisel lõimel ja seejärel edastada iga töötava lõime jaoks kõrge paralleelsuse tasemel. Selles osas käsitletakse Envoy lõimeteenuste (TLS) süsteemi kõrgtasandil. Järgmises osas kirjeldan, kuidas seda kasutatakse klastrite haldamiseks.
![[Перевод] Envoy mudel voogudest (Envoy threading model)](/wp-content/uploads/2019/04/a47af1e8eb1f55a4d7609b3ff3ee9ce1.jpeg)
Nagu juba mainitud, töötleb peamine lõim praktiliselt kõiki juhtimisfunktsioone ja funktsionaalset kontrollerit Envoy protsessis. Siin on kontroller veidi üle koormatud, kuid kui seda vaadata Envoy enda protsessi raames ja võrrelda seda töötavate lõimede edastusprotsessiga, tundub see mõistlik. Üldreeglina täidab peamine lõim teatavat tööd ja peab siis värskendama iga töötava lõime vastavalt selle töö tulemustele, suhteliselt ei pea töötav lõim iga kord juurdepääsu saamiseks lukku seadma..
Envoy TLS (lõime kohaliku salvestamise) süsteem töötab järgmiselt:
- Peamine teema, mis töötab peamise lõime sees, saab kogu protsessi jaoks eraldada TLS sloti. Kuigi see on abstraktne, on praktikas see indeks vektorisse, mis tagab O(1) juurdepääsu.
- Peamine lõim võib oma slotis seada ükskõik milliseid andmeid. Kui see on tehtud, avaldatakse andmed igas töötlõimes nagu tavaline sündmus sündmuste tsüklis.
- Töötlõimed saavad lugeda oma TLS slotist ning seal kätte saada kõik kohalikud andmed, mis on neile kättesaadavad.
Kuigi see on väga lihtne ja uskumatult võimas paradigma, mis sarnaneb RCU (Read-Copy-Update) lukustuse kontseptsiooniga. Sisuliselt ei näe töötlõimed kunagi andmete muutusi TLS slotides töö käigus. Muutus toimub ainult töötluste vahel, rahu ajal.
Envoy kasutab seda kahel eri viisil:
- Säilitades igas töötlõimes erinevaid andmeid, pääseb nendele andmetele juurde ilma igasuguse lukustamiseta.
- Säilitades üldise viite globaalsele tõe andmetele lugemisrežiimis igas töövoos. Seega on igal töövool andmete viitamine, mida ei saa töö käigus vähendada. Ainult siis, kui kõik töötajad rahunevad ja laadivad uusi üldiseid andmeid, hävitatakse vanad andmed. See on identne RCU-ga.
Klastri uuendamise voog (Cluster update threading)
Selles osas kirjeldan, kuidas TLS (Thread local storage) kasutatakse klastrihalduseks. Klastrihaldus hõlmab API xDS ja/või DNS-i töötlemist ning tervislikkuse kontrollimist (health checking).
![[Перевод] Envoy mudel voogudest (Envoy threading model)](/wp-content/uploads/2019/04/238f5718729e1f12f8f0d6507f16abe8.jpeg)
Klastri voogude haldamine hõlmab järgmisi komponente ja etappe:
- Klastri haldur on komponent Envoy's, mis haldab kõiki teadaolevaid ülesvoolu (upstream) klasse, CDS (Cluster Discovery Service) API, SDS (Secret Discovery Service) ja EDS (Endpoint Discovery Service) API-sid, DNS-i ja aktiivseid väliseid töötluskontrolle (health checking). Ta vastutab iga ülesvoolu (upstream) klastri „lõppkokkuvõttes kooskõlastatud“ (eventually consistent) esinduse loomise eest, mis sisaldab avastatud hoste ja tööoleku (health status) seisundit.
- Tööoleku kontrollija (health checker) teostab aktiivset tööoleku kontrolli ja teatab tööoleku muutustest klastri haldurile.
- CDS (Cluster Discovery Service) / SDS (Secret Discovery Service) / EDS (Endpoint Discovery Service) / DNS kasutatakse klastrisse kuuluvuse määramiseks. Oleku muutmine tagastatakse klastri haldurile.
- Iga töövoog täidab pidevalt sündmuste töötlemise tsüklit.
- Kui klastri haldur tuvastab, et klastri olek on muutunud, loob ta uue oleku snapshots, mis on ainult lugemiseks, ja saadab selle igale töövoole.
- Järgmise vaikuse perioodi jooksul uuendab töövoog TLS-i eraldatud kohas oleku ülevaadet.
- Sisend-väljundisündmuse ajal, mis määrab koormuse tasakaalustamiseks hosti, küsib koormuse tasakaalustaja TLS-i (Thread local storage) kohta teavet hosti kohta. Selleks ei ole lukustusi vaja. Samuti tasub märkida, et TLS võib samuti algatada sündmusi värskendamise ajal, nii et koormuse tasakaalustamise alamsüsteemid ja teised komponendid saavad uuesti arvestada vahemälusid, andmestruktuure jne. See ületab selle postituse raames käsitletud teema, kuid seda kasutatakse erinevates koodikohtades.
Kasutades ülaltoodud protseduuri, suudab Envoy iga päringu töödelda ilma lukustusteta (välja arvatud eelnevalt kirjeldatud). Peale TLS-i enda keerukuse ei pea enamik koodi mõistma, kuidas mitme lõime jõudlus töötab, ja see võib olla kirjutatud ühel lõimel. See lihtsustab suure osa koodi kirjutamist, lisaks suurepärasele jõudlusele.
Teised alamsüsteemid, mis kasutavad TLS (Other subsystems that make use of TLS)
TLS (Thread local storage) ja RCU (Read Copy Update) on Envoy's laialdaselt kasutatavad.
Kasutusnäited:
- Funktsionaalsuse muude muutmise mehhanism: Praegune lubatud funktsionaalsuse loend arvutatakse põhivoos. Seejärel antakse igale töövoole ainult lugemiseks kohandatud allikas, kasutades RCU semantiikat.
- Marsruuditabelite asendamine: RDS (Marsruudi avastamise teenus) pakutavate marsruuditabelite jaoks luuakse tabelid põhivoos. Seejärel antakse igale töövoole ainult lugemiseks kohandatud allikas, kasutades RCU (Read Copy Update) semantiikat. See muudab marsruuditabelite muutmise aatomiliselt efektiivseks.
- HTTP peade vahemälu: Kuidas selgub, on HTTP päise arvutamine iga päringu jaoks (~25K+ RPS tuuma kohta) üsna kulukas. Envoy arvutab päise tsentraliseeritult umbes iga poole sekundi järel ja annab selle igale töötajale TLS ja RCU kaudu.
On ka teisi juhtumeid, kuid eelnevad näited peaksid andma hea ülevaate TLS kasutusvõimalustest.
Tuntud jõudluse karid
Kuigi Envoy töötab üldiselt üsna hästi, on mitmeid tuntud valdkondi, mis vajavad tähelepanu, kui seda kasutatakse väga kõrge paralleelsuse ja läbilaskevõimega.
- Nagu selles artiklis juba mainitud, saavad kõik töötlemisvood praegu lukustuse, kui nad kirjutavad mälubufferisse logifaili. Kõrge paralleelsuse ja läbilaskevõime korral tuleb igas töötlemisvoos logifailide pakkimine teostada, et saavutada ebaühtlane kohaletoimetamine põhifaili kirjutamisel. Alternatiivina on võimalik luua iga töötlemisvoo jaoks eraldi logifail.
- Kuigi statistika on väga hästi optimeeritud, on väga kõrge paralleelsuse ja läbilaskevõime korral tõenäoliselt individuaalsel statistikal aatomkonkurents. Selle probleemi lahendus on kasutada ühe töötlemisvoo jaoks loendureid, millel on perioodiline kesklöökide lähtestamine. Seda arutatakse järgnevates postitustes.
- Olemasolev arhitektuur ei tööta hästi, kui Envoy on juurutatud olukordades, kus on väga vähe ühendusi, mis vajavad märkimisväärseid ressursse töötlemiseks. Pole garanteeritud, et ühendused jaotuvad võrdselt töötamisvoogude vahel. Seda saab lahendada tööhõive ühenduste tasakaalustamise rakendamisega, mille puhul on võimalik täiendada ühendusi töötamisvoogude vahel.
Kokkuvõte (Conclusion)
Envoy voogude mudel on loodud programmeerimise lihtsuse ja massilise paralleelsuse tagamiseks, kuid see võib potentsiaalselt raisata mälu ja ühendusi, kui neid ei ole korralikult seadistatud. See mudel võimaldab tal töötada väga hästi väga suurte voogude arvu ja läbilaskevõimega.
Nagu ma juba mainisin Twitteris, võib disain töötada ka täieliku funktsionaalsuse omava võrgu virna peal kasutaja režiimis, näiteks DPDK (Data Plane Development Kit), mis võib põhjustada seda, et tavalised serverid töötlevad miljoneid päringuid sekundis täieliku L7 töötlemisega. On väga huvitav näha, mida järgmise paari aasta jooksul ehitatakse.
Viimane kiire kommentaar: mind on korduvalt küsitud, miks me valisime Envoy jaoks C++. Põhjus on endiselt see, et see on ainus laialdaselt kasutatav tööstusstandardina keelekeel, millele saab üles ehitada selles postituses kirjeldatud arhitektuuri. C++ ei sobi kindlasti kõikidele ega isegi paljudele projektidele, kuid teatud kasutusjuhtumite korral on see endiselt ainus tööriist, mis töö ära teeb (to get the job done).
Koodilingid (Links to code)
Lingid liideste ja pealkirjade rakenduste failidele, millest selles postituses räägitakse:
Allikas: habr.com
