Milión požiadaviek za sekundu s Pythonom

Je možné dosiahnuť pomocou Pythonu milión požiadaviek za sekundu? Asi až donedávna.

Mnoho spoločností migruje z Pythonu do iných programovacích jazykov, aby mohli zvýšiť svoj prevádzkový výkon a ušetriť na cenách serverov, ale nie je to potrebné. Python môže byť tým správnym nástrojom pre túto prácu.

Komunita Pythonu robí v poslednom čase veľa okolo výkonu. CPython 3.6 zvýšil celkový výkon tlmočníka implementáciou nového slovníka. CPython 3.7 bude ešte rýchlejší, a to vďaka zavedeniu rýchlejšej konvencie hovorov a vyhľadávacích pamätí slovníkov.

Pre úlohy spojené s početnými zmenami môžete použiť PyPy s kompiláciou kódu just-in-time. Môžete tiež spustiť testovaciu sadu NumPy, ktorá teraz vylepšila celkovú kompatibilitu s rozšíreniami C. Neskôr v tomto roku sa očakáva, že PyPy dosiahne zhodu s Python 3.5.

Celá táto skvelá práca ma inšpirovala k inováciám v jednej z oblastí, kde sa Python vo veľkej miere používa: vývoj webových a mikroslužieb.

Vstúpte do Japronta!

Japronto je úplne nový mikrorámec šitý na mieru vašim potrebám mikroslužieb. Medzi jeho hlavné ciele patrí byť rýchly , škálovateľný a ľahký . Vďaka asynciu vám umožňuje synchrónne aj asynchrónne programovanie . A je to nehanebne rýchle . Dokonca rýchlejšie ako NodeJS and Go.

Errata: Ako upozorňuje používateľ @heppu, stdlib server HTTP Go môže byť o 12% rýchlejší, ako ukazuje tento graf, keď sa píše opatrnejšie. Existuje tiež úžasný server typu fasthttp pre Go, ktorý je zjavne iba o 18% pomalší ako Japronto v tomto konkrétnom teste. Úžasné! Podrobnosti nájdete na //github.com/squeaky-pl/japronto/pull/12 a //github.com/squeaky-pl/japronto/pull/14.

Vidíme tiež, že server Meinheld WSGI je takmer na rovnakej úrovni ako NodeJS a Go. Napriek svojmu neodmysliteľne blokujúcemu dizajnu je v porovnaní s predchádzajúcimi štyrmi, ktoré sú asynchrónnymi riešeniami v jazyku Python, vynikajúci umelec. Nikdy teda neverte nikomu, kto tvrdí, že asynchrónne systémy sú vždy rýchlejšie. Sú takmer vždy viac súbežné, ale je za tým oveľa viac než len to.

Tento mikro benchmark som vykonal pomocou nástroja „Hello world!“ ale jasne demonštruje réžiu serverového rámca pre množstvo riešení.

Tieto výsledky boli získané na inštancii AWS c4.2xlarge, ktorá mala 8 VCPU spustených v regióne São Paulo s predvoleným zdieľaným nájomným vzťahom a virtualizáciou HVM a magnetickým úložiskom. Na stroji bežal Ubuntu 16.04.1 LTS (Xenial Xerus) s jadrom x86_64 generickým pre systém Linux 4.4.0–53. OS hlásil procesor Xeon® CPU E5–2666 v3 s frekvenciou 2,90 GHz. Použil som Python 3.6, ktorý som čerstvo zostavil z jeho zdrojového kódu.

Aby sme boli spravodliví, všetci súťažiaci (vrátane Go) riadili proces jedného pracovníka. Servery boli testované na zaťaženie pomocou wrk s 1 vláknom, 100 spojeniami a 24 simultánnymi (zreťazenými) požiadavkami na jedno pripojenie (kumulatívna paralelnosť 2 400 požiadaviek).

Potrubie HTTP je tu rozhodujúce, pretože je to jedna z optimalizácií, ktoré Japronto zohľadňuje pri vykonávaní požiadaviek.

Väčšina serverov vykonáva požiadavky od zreťazujúcich klientov rovnakým spôsobom, ako by to robili od neskorelovaných klientov. Nesnažia sa to optimalizovať. (V skutočnosti Sanic a Meinheld tiež potichu zrušia požiadavky od pipeline klientov, čo je v rozpore s protokolom HTTP 1.1.)

Jednoduchými slovami, pipeline je technika, pri ktorej klient nemusí čakať na odpoveď pred odoslaním ďalších požiadaviek cez to isté pripojenie TCP. Aby sa zabezpečila integrita komunikácie, server pošle späť niekoľko odpovedí v rovnakom poradí, v akom sú prijaté žiadosti.

Kruté detaily optimalizácií

Keď klient zreťazí veľa malých požiadaviek GET, je veľká pravdepodobnosť, že prídu v jednom pakete TCP (vďaka Nagleovmu algoritmu) na strane servera a potom ich prečítajú jedným systémovým volaním .

Systémové volanie a presun údajov z priestoru jadra do priestoru používateľa je veľmi nákladná operácia v porovnaní s presunom pamäte do priestoru procesu, napríklad. Preto je dôležité vykonať čo najmenej systémových volaní (ale nie menej).

Keď Japronto prijme údaje a úspešne z nich analyzuje niekoľko požiadaviek, pokúsi sa všetky požiadavky vykonať čo najrýchlejšie, spojiť odpovede späť v správnom poradí a potom spätne napísať jedným systémovým volaním . V skutočnosti môže jadro pomôcť pri lepení časti vďaka systémovým hovorom scatter / collect IO, ktoré Japronto zatiaľ nepoužíva.

Upozorňujeme, že to nie je vždy možné, pretože niektoré žiadosti môžu trvať príliš dlho a čakanie na ne by zbytočne zvýšilo latenciu.

Pri ladení heuristiky buďte opatrní a zvážte náklady na systémové volania a predpokladaný čas dokončenia žiadosti.

Okrem oneskorenia zápisov pre zreťazených klientov existuje aj niekoľko ďalších techník, ktoré kód využíva.

Japronto je napísané takmer výlučne v jazyku C. Analyzátor, protokol, reaper pripojenia, smerovač, požiadavka a odpoveď sú napísané ako rozšírenia C.

Japronto sa veľmi snaží oddialiť vytvorenie pythonských náprotivkov svojich vnútorných štruktúr, kým nebude výslovne požiadaný. Napríklad slovník hlavičiek sa nevytvorí, kým oň nebude v zobrazení požiadaný. Všetky hranice tokenov sú už predtým označené, ale normalizácia kľúčov hlavičiek a vytvorenie niekoľkých objektov str sa vykoná pri ich prvom prístupe.

Spoločnosť Japronto sa pri analýze stavového riadku, hlavičiek a kusového tela správy HTTP spolieha na vynikajúcu knižnicu picohttpparser C. Picohttpparser priamo využíva pokyny na spracovanie textu, ktoré sa nachádzajú v moderných procesoroch s rozšíreniami SSE4.2 (má ich takmer každý 10-ročný procesor x86_64) na rýchle prispôsobenie hraníc tokenov HTTP. O vstupy a výstupy sa stará super úžasný uvloop, ktorý sám o sebe predstavuje obal okolo libuv. Na najnižšej úrovni ide o premostenie systému na epoll, ktoré poskytuje asynchrónne oznámenia o pripravenosti na čítanie a zápis.

Python je jazyk zhromažďovania smetí, takže pri navrhovaní vysoko výkonných systémov je potrebné postupovať opatrne, aby sa zbytočne nezvyšoval tlak na zberača smetí. Interný dizajn spoločnosti Japronto sa snaží vyhnúť sa referenčným cyklom a podľa potreby vykonať čo najmenej alokácií / pridelení. Robí to tak, že predbežne umiestňuje niektoré objekty do takzvaných arén. Pokúša sa tiež znovu použiť objekty Pythonu pre budúce požiadavky, ak na ne už nie je odkaz, namiesto toho, aby ich vyhodil.

Všetky pridelenia sa vykonávajú ako násobky 4 kB. Vnútorné štruktúry sú starostlivo usporiadané tak, aby údaje, ktoré sa často používajú spolu, boli dostatočne blízko na to, aby sa minimalizovala možnosť vynechania pamäte cache.

Japronto sa snaží zbytočne nekopírovať medzi vyrovnávacími pamäťami a robí veľa operácií na mieste. Napríklad percentuálne dekóduje cestu pred zhodou v procese smerovača.

Prispievatelia otvoreného zdroja, mohol by som využiť vašu pomoc.

Na Japronte pracujem nepretržite posledné 3 mesiace - často cez víkendy, ako aj bežné pracovné dni. To bolo možné len vďaka tomu, že som si dal pauzu od svojej bežnej práce programátora a vložil som do tohto projektu všetko svoje úsilie.

Myslím, že je čas podeliť sa o ovocie svojej práce s komunitou.

Momentálne Japronto implementuje celkom solídnu sadu funkcií:

  • Implementácia HTTP 1.x s podporou nahrávania blokov
  • Plná podpora pre pipeline HTTP
  • Udržujte spojenie s konfigurovateľným žacím strojom
  • Podpora synchrónneho a asynchrónneho zobrazenia
  • Master-multiworker model založený na rozvetvovaní
  • Podpora opätovného načítania kódu pri zmenách
  • Jednoduché smerovanie

Ďalej by som sa chcel pozrieť do Websockov a asynchrónne streamovať odpovede HTTP.

V oblasti dokumentovania a testovania je potrebné urobiť ešte veľa práce. Ak máte záujem pomôcť, kontaktujte ma priamo na Twitteri. Tu je úložisko projektu GitHub od spoločnosti Japronto.

Tiež, ak vaša spoločnosť hľadá vývojára Pythonu, ktorý je čudák v oblasti výkonu a tiež robí DevOps, som o tom informovaný. Chystám sa zvážiť pozície na celom svete.

Záverečné slová

Všetky techniky, ktoré som tu spomenul, nie sú v skutočnosti špecifické pre Python. Pravdepodobne by sa dali použiť v iných jazykoch, ako je Ruby, JavaScript alebo dokonca PHP. Tiež by som mal záujem o takúto prácu, ale bohužiaľ sa to nestane, pokiaľ to niekto nebude môcť financovať.

Chcel by som poďakovať komunite Python za ich neustále investície do výkonnostného inžinierstva. Menovite Victor Stinner @VictorStinner, INADA Naoki @methane a Yury Selivanov @ 1,1 a celý tím PyPy.

Pre lásku k Pythonu.