Miljoonaa pyyntöä sekunnissa Pythonin kanssa

Onko mahdollista saada miljoona pyyntöä sekunnissa Pythonilla? Luultavasti vasta äskettäin.

Monet yritykset ovat siirtymässä pois Pythonista ja muilta ohjelmointikieliltä voidakseen parantaa toimintansa suorituskykyä ja säästää palvelimen hinnoissa, mutta oikeastaan ​​ei ole tarvetta. Python voi olla oikea työkalu työhön.

Python-yhteisö tekee viime aikoina paljon suorituskykyä. CPython 3.6 paransi tulkkien yleistä suorituskykyä uudella sanakirjan toteutuksella. CPython 3.7 tulee olemaan vieläkin nopeampi nopeamman puhelukäytännön ja sanakirjahakujen välimuistien käyttöönoton ansiosta.

Numeromurskaustehtävissä voit käyttää PyPyä sen juuri oikeaan aikaan -kokoelman kanssa. Voit myös suorittaa NumPyn testipaketin, joka on nyt parantanut yleistä yhteensopivuutta C-laajennusten kanssa. Myöhemmin tänä vuonna PyPy: n odotetaan saavuttavan Python 3.5 -yhteensopivuuden.

Kaikki tämä upea työ inspiroi minua innovoimaan yhdellä osa-alueesta, jolla Pythonia käytetään laajasti: verkko- ja mikropalveluiden kehittämisessä.

Anna Japronto!

Japronto on upouusi mikropalvelu, joka on räätälöity mikropalvelujen tarpeisiin. Sen päätavoitteisiin kuuluu nopea , skaalautuva ja kevyt . Sen avulla voit tehdä sekä synkronisen että asynkronisen ohjelmoinnin asyncion ansiosta . Ja se on häpeämättömän nopea . Jopa nopeammin kuin NodeJS ja Go.

Virhe : Kuten käyttäjä @heppu huomauttaa, Go: n stdlib-HTTP-palvelin voi olla 12% nopeampi kuin tämä kaavio näyttää, kun kirjoitetaan huolellisemmin. Go: lle on myös mahtava fasthttp- palvelin, joka on ilmeisesti vain 18% hitaampi kuin Japronto tässä nimenomaisessa vertailuarvossa. Mahtava! Lisätietoja on osoitteissa //github.com/squeaky-pl/japronto/pull/12 ja //github.com/squeaky-pl/japronto/pull/14.

Voimme myös nähdä, että Meinheld WSGI -palvelin on melkein samanlainen kuin NodeJS ja Go. Luonnostaan ​​estävästä suunnittelustaan ​​huolimatta se on erinomainen esiintyjä verrattuna neljään edelliseen, jotka ovat asynkronisia Python-ratkaisuja. Älä siis koskaan luota ketään, joka sanoo, että asynkroniset järjestelmät ovat aina nopeampia. Ne ovat melkein aina yhtäaikaisia, mutta siinä on paljon enemmän kuin vain.

Suoritin tämän mikromääritelmän käyttäen "Hello world!" sovellus, mutta se osoittaa selvästi palvelinkehyksen yleiskustannukset useille ratkaisuille.

Nämä tulokset saatiin AWS c4.2xlarge -instanssilla, jossa oli 8 VCPU: ta, jotka käynnistettiin São Paulon alueella oletusarvoisesti jaetulla vuokralla sekä HVM-virtualisoinnilla ja magneettisella tallennustilalla. Kone oli käynnissä Ubuntu 16.04.1 LTS: llä (Xenial Xerus) Linux 4.4.0–53 -geneerisen x86_64-ytimen kanssa. Käyttöjärjestelmä raportoi Xeon® CPU E5–2666 v3 @ 2.90GHz CPU. Käytin Python 3.6: ta, jonka koin juuri sen lähdekoodista.

Ollakseni oikeudenmukainen, kaikki kilpailijat (mukaan lukien Go) suorittivat yhden työntekijän prosessia. Palvelimia testattiin käyttäen wrk-ohjelmaa, jossa oli 1 lanka, 100 yhteyttä ja 24 samanaikaista (putkijohtoista) pyyntöä per yhteys (2400 pyynnön kumulatiivinen rinnakkaisuus).

HTTP-putkisto on tässä ratkaisevan tärkeää, koska se on yksi optimoinnista, jonka Japronto ottaa huomioon pyyntöjä suorittaessaan.

Suurin osa palvelimista toteuttaa pyyntöjä putkilinja-asiakkailta samalla tavalla kuin muilta asiakkailta. He eivät yritä optimoida sitä. (Itse asiassa Sanic ja Meinheld myös hylkäävät hiljaa pyynnöt putkilinja-asiakkailta, mikä on HTTP 1.1 -protokollan vastaista.)

Yksinkertaisesti sanottuna, putkisto on tekniikka, jossa asiakkaan ei tarvitse odottaa vastausta ennen seuraavien pyyntöjen lähettämistä samalla TCP-yhteydellä. Viestinnän eheyden varmistamiseksi palvelin lähettää takaisin useita vastauksia samassa järjestyspyynnössä.

Optimointien yksityiskohdat

Kun asiakas lähettää useita pieniä GET-pyyntöjä yhteen, on todennäköistä, että ne saapuvat yhdeksi TCP-paketiksi (Naglen algoritmin ansiosta) palvelinpuolelle, ja sitten ne luetaan takaisin yhdellä järjestelmäkutsulla .

Järjestelmäkutsun suorittaminen ja datan siirtäminen ydintilasta käyttäjän tilaan on erittäin kallista toimenpidettä verrattuna esimerkiksi muistin siirtämiseen prosessitilan sisällä. Siksi on tärkeää, että soitat mahdollisimman vähän järjestelmäkutsuja (mutta ei vähemmän).

Kun Japronto vastaanottaa tietoja ja jäsentää niistä useita pyyntöjä onnistuneesti, se yrittää suorittaa kaikki pyynnöt mahdollisimman nopeasti, liimata vastaukset takaisin oikeassa järjestyksessä ja kirjoittaa sitten takaisin yhdellä järjestelmäkutsulla . Itse asiassa ydin voi auttaa liimausosassa siron / keräämisen IO-järjestelmän puheluiden ansiosta, joita Japronto ei vielä käytä.

Huomaa, että tämä ei ole aina mahdollista, koska jotkut pyynnöt voivat kestää liian kauan, ja niiden odottaminen lisäisi tarpeettomasti viivettä.

Ole varovainen, kun virität heuristiikkaa, ja ota huomioon järjestelmäkutsujen kustannukset ja odotettavissa oleva pyynnön suorittamisaika.

Kirjoitusten viivästyttämisen lisäksi putkilinjan asiakkaille on useita muita tekniikoita, joita koodi käyttää.

Japronto on kirjoitettu melkein kokonaan C.-muodossa. Parseri, protokolla, yhteyden lukulaite, reititin, pyyntö- ja vastausobjektit kirjoitetaan C-laajennuksina.

Japronto yrittää kovasti viivästyttää sisäisten rakenteiden Python-kollegoiden luomista, kunnes sitä nimenomaisesti kysytään. Esimerkiksi otsikkosanakirjaa ei luoda ennen kuin sitä pyydetään näkymässä. Kaikki tunnuksen rajat on jo merkitty aiemmin, mutta otsikkonäppäinten normalisointi, ja useita str-objekteja luodaan, kun niitä käytetään ensimmäistä kertaa.

Japronto luottaa erinomaiseen picohttpparser C -kirjastoon jäsentääkseen tilarivin, otsikot ja paloitellun HTTP-viestirungon. Picohttpparser käyttää suoraan tekstinkäsittelyohjeita, jotka löytyvät nykyaikaisista suorittimista, joissa on SSE4.2-laajennukset (melkein missä tahansa 10-vuotiaassa x86_64-suorittimessa on se), jotta ne vastaavat nopeasti HTTP-tunnusten rajoja. I / O: ta hoitaa erittäin mahtava uvloop, joka itsessään on kääre libuvin ympärille. Alimmalla tasolla tämä on silta epoll-järjestelmäpuheluun, joka tarjoaa asynkronisia ilmoituksia luku- ja kirjoitusvalmiudesta.

Python on roskakorinkieli, joten korkean suorituskyvyn järjestelmien suunnittelussa on oltava varovainen, jotta roskankerääjään kohdistuva paine ei tarpeettomasti lisääntyisi. Japronton sisäinen suunnittelu pyrkii välttämään vertailujaksoja ja tekemään niin vähän varauksia / jakoja kuin tarpeen. Se tekee tämän sijoittamalla jotkut esineet ns. Areenoille. Se yrittää myös käyttää Python-objekteja tulevia pyyntöjä varten, jos niihin ei enää viitata sen sijaan, että heitettäisiin ne.

Kaikki varaukset tehdään 4 kt: n kerrannaisina. Sisäiset rakenteet on järjestetty huolellisesti niin, että usein yhdessä käytettävät tiedot ovat riittävän lähellä muistia, mikä minimoi välimuistivirheiden mahdollisuuden.

Japronto yrittää olla kopioimatta puskurien välillä tarpeettomasti, ja tekee monia toimintoja paikallaan. Esimerkiksi se purkaa polun prosenttiosuuden ennen sovittamista reitittimen prosessissa.

Avoimen lähdekoodin kirjoittajat, voisin käyttää apuasi.

Olen työskennellyt Japronton parissa jatkuvasti viimeiset 3 kuukautta - usein viikonloppuisin sekä normaaleina työpäivinä. Tämä oli mahdollista vain siksi, että pidin tauon tavallisesta ohjelmoijatyöstäni ja panin kaiken ponnisteluni tähän projektiin.

Luulen, että on aika jakaa työni hedelmä yhteisön kanssa.

Tällä hetkellä Japronto toteuttaa melko vankan ominaisuusjoukon:

  • HTTP 1.x-toteutus, joka tukee yksittäisiä latauksia
  • Täysi tuki HTTP-putkistolle
  • Pysy ajan tasalla liitännöistä konfiguroitavalla leikkuulaitteella
  • Synkronisten ja asynkronisten näkymien tuki
  • Haarukkaan perustuva päällikön ja monityöntekijän malli
  • Tuki koodin uudelleenlataukselle muutosten yhteydessä
  • Yksinkertainen reititys

Seuraavaksi haluaisin tutkia Websocketsia ja suoratoistaa HTTP-vastauksia asynkronisesti.

Dokumentoinnissa ja testauksessa on tehtävä paljon työtä. Jos olet kiinnostunut auttamaan, ota yhteyttä minuun suoraan Twitterissä. Tässä on Japronton GitHub-projektivarasto.

Lisäksi, jos yrityksesi etsii Python-kehittäjää, joka on suorituskykyfriikki ja tekee myös DevOpsia, olen valmis kuulemaan siitä. Harkitsen kantoja maailmanlaajuisesti.

Viimeiset sanat

Kaikki tekniikat, jotka olen maininnut tässä, eivät todellakaan ole nimenomaan Pythonia. Heitä voidaan todennäköisesti käyttää muilla kielillä, kuten Ruby, JavaScript tai jopa PHP. Olisin myös kiinnostunut tekemään sellaista työtä, mutta valitettavasti näin ei tapahdu, ellei joku voi rahoittaa sitä.

Haluan kiittää Python-yhteisöä jatkuvasta investoinnista suorituskykyyn. Nimittäin Victor Stinner @VictorStinner, INADA Naoki @methane ja Yury Selivanov @ 1st1 sekä koko PyPy-tiimi.

Pythonin rakkaudesta.