HTTP/1.x:n ja HTTP/2:n nopeusvertailua ja selaimen viritystä

HTTP eli HyperText Transfer Protocol on pääasiassa nettisivujen jakamiseen palvelimelta asiakasohjelmalle eli selaimelle käytettävä protokolla, joka pysyi lähes muuttumattomana kymmeniä vuosia, kunnes Google kehitti täysin edellisistä versioista poikkeavan HTTP/2-protokollan vuonna 2015. HTTP/2- protokollaa kehutaan sen suorituskyvystä ja sen sanotaan nopeuttavan sivujen latautumista huomattavasti.

Luettuani HTTP/2-protokollan RFC:n aloin epäilemään sen tehokkuutta HTTP/1.x- protokollaan nähden - kyseessä kun on huomattavasti aiempaa monimutkaisempi protokolla, joka myös siirtää huomattavasti suuremman määrän ylimääräistä dataa hyötykuormansa lisäksi. Internetissä kuitenkin on paljon testisivuja, joissa HTTP/1.x:n ja HTTP/2:n suorituskykyjä voi vertailla. Jokainen löytämäni testisivu näyttää, että HTTP/2 on selvästi nopeampi kuin HTTP/1.x. Mitäpä sitä sitten vastaan väittämään - HTTP/2 on selvästi nopeampi.

Vai onko?

Koska nopean päässälaskun perusteella HTTP/2-protokollan ei mitenkään pitäisi pystyä olemaan nopeampi kuin huomattavasti yksinkertaisemman ja kevyemmän HTTP/1.x-protokollan, niin aloin tutkia selaimen ja http-palvelimen välistä viestinvaihtoa tarkemmin. Kaappasin paketit Wiresharkilla ja havaitsin, että HTTP/1.1-protokollaa käyttäessä selain luo vain hyvin pienen määrän soketteja, mikä etenkin suurta määrää pieniä tiedostoja ladatessa on tehotonta. Tälle voi olla kaksi mahdollista syytä: joko protokollaversioita vertailevien nettisivujen palvelimet on tarkoituksella säädetty niin, että HTTP/2 saa vertailussa merkittävää etua, tai jokin muu tekijä rajoittaa yhteyksien määrää.


Kuva 1.
Kuvassa näkyy yhteensä seitsemän soketin muodostuksen aloittavaa SYN-pakettia. Yksi kättelyistä näyttää epäonnistuvan todennäköisesti reitittimen firmwaressa olevan bugin takia.


Kuva 2.
Epäonnistunut TCP-kättely. Reititin näyttää jostain syystä pudottavan ensimmäisen ACK-paketin, jonka jälkeen palvelin uudelleenlähettää SYN-ACK- paketin ja asiakas uudelleenlähettää ensimmäisen ACK-paketin. Myös uudelleenlähetetty ACK-paketti häviää matkalle ja kumpikin osapuoli luovuttaa.

HTTP/2-protokollan nopeushyöty vanhempaan HTTP/1.x-protokollaan perustuu siihen, että HTTP/2-protokollassa voi samassa soketissa ladata rinnakkain useamman tiedoston. HTTP/2 on saanut myös monimutkaisuutensa takia kritiikkiä - protokollaan kuuluu mm. vuonohjaus, joka on ilmeisen turha, koska se löytyy jo alla olevasta TCP-protokollasta. Myös HTTP/1.x-protokolla tukee useamman tiedoston lataamista samassa soketissa (ns. persistent connection), mutta tiedostoja ei voi ladata rinnakkain samassa soketissa, vaan seuraavan tiedoston lataamisen voi aloittaa vasta edellisen tultua valmiiksi. Satoja pikkukuvia ladatakseen selain teki yhdessä soketissa kymmeniä pyyntöjä, joihin palvelin vastasi. Yhteen pyyntöön vastaamiseen kuluu aikaa vähintään saman verran kuin PING-pakettiin vastaamisessa.

Mikään ei kuitenkaan estä selainta avaamasta palvelimeen useampaa yhteyttä, jolloin useassa eri soketissa voi olla yhtä aikaa HTTP/1.x-tiedonsiirto käynnissä. Tällöin voidaan ladata useampaa tiedostoa yhtä aikaa huomattavasti paremmalla hyötysuhteella kuin HTTP/2-protokollalla on mahdollista. Valitettavasti selaimissa on rajoitettu yhtäaikaisten sokettien maksimimäärää - sekä Chromessa että Mozillan selaimissa rajana on kuusi yhtäaikaista yhteyttä, mikä on nykysivuille naurettavan matala raja. Chromessa rajoitus on kovakoodattu, eikä sitä voi vaihtaa muuten kuin muuttamalla lähdekoodia ja kääntämällä itse Chromium. Mozillan selaimissa rajoitus on muutettavissa about:config -sivulta.


Kuva 3.
Kuvakaappaus about:config -sivulta.

Herää kysymys, miksi Google on kovakoodannut noinkin oleellisen asetuksen selaimensa lähdekoodiin. Ehkä tarkoituksena on saada HTTP/1.x vaikuttamaan merkittävästi hitaammalta verrattuna Googlen itse kehittämään HTTP/2- protokollaan? Mozillan selaimissa sokettien oletusmaksimimäärä oli aiemmin kahdeksan, mutta jossain vaiheessa se on vähennetty kuuteen. Kokeilin alkajaisiksi nostaa rajaa melko varovaisesti kuuteentoista, millä sain jo melko huomattavan nopeushyödyn aikaisempaan verrattuna - tosin HTTP/2 oli edelleen niukasti nopeampi. Nostin rajan 32:een, mutta nopeushyötyä ei tullut enää ollenkaan lisää. Wiresharkin kaappaamista paketeista huomasin, että noin kuudennentoista soketin jälkeen kaikki TCP-kättelyt palvelimelle alkavat epäonnistua jostain syystä kuvassa 2 näkyvällä tavalla.

Sekä selain että palvelin siis todellakin yrittävät muodostaa vielä lisää soketteja, mutta jostain syystä se ei onnistu. Epäilin vian olevan Huawein valmistamassa 4G-reitittimessäni, jonka SPI-palomuurissa on paljon epämääräisiä bugeja, jotka aiheuttavat silloin tällöin pakettihukkaa ja pahimmissa tapauksissa estävät TCP-kättelyn täysin toistettavasti. Varmistuakseni asiasta kokeilin jakaa nettiyhteyden kännykästä suoraan tietokoneelle, jolloin Huawein buginen 4G-purkki jää välistä pois. Ja lopulta HTTP/1.1 olikin selvästi HTTP/2-protokollaa nopeampi myös testisivun mukaan.

Miksi sokettimäärä on rajoitettu?

Yleisen, mutta väärän, käsityksen mukaan selaimen muodostamien sokettien määrää on hyvä rajoittaa, jottei palvelimia rasitettaisi liikaa. Tällä ei ole mitään totuuspohjaa - yksi soketti käyttää palvelimen muistia vain nimellisen määrän, eikä yksikään järkevästi koodattu http-palvelinohjelma käytä staattisen sivun lähettämiseen asiakkaalle merkittäviä määriä palvelinkoneen resursseja. Huom: sanon tämän ihmisenä, joka on koodannut itse sekä TCP/IP- pinon että HTTP-palvelimen. Vuosituhannen vaihteen jälkeen valmistettujen tietokoneiden kanssa tällaisilla asioilla ei ole merkitystä - puhutaan korkeintaan kymmenien tai parinsadan kilotavun muistinkulutuksesta per soketti ja palvelinohjelman säie. Lisäksi on muistettava myös, että HTTP/2- implementaation on sekä asiakkaan että palvelimen päässä tallennettava muistiin vähintään yhden soketin muistinkulutusta vastaava määrä informaatiota jokaista ladattavaa tiedostoa kohti.

Täytyy ottaa huomioon myös se, että todennäköisesti palvelimella keskimäärin auki olevien sokettien määrä on sitä pienempi, mitä nopeammin asiakkaat tulevat valmiiksi. Useamman yhtäaikaisen soketin käyttäminen saattaa siis jopa laskea palvelimen kuormitusta. Niinkutsuttu "syn flood" on kyllä yksi tapa tehdä palvelunestohyökkäys, mutta siinä käytetään jo vähintään tuhatkertaisesti suurempaa määrää SYN-paketteja, joihin vastauksena tuleviin SYN-ACK-paketteihin kuitenkin jätetään tietoisesti vastaamatta. Selainhan ei kuitenkaan muodosta yhtään turhaa sokettia, vaan jokaisessa on tarkoitus ladata ainakin yksi tiedosto.

Lisäoptimoinnin mahdollisuuksia

Huomasin, että HTTP/1.1-protokollan "persistent connection"-ominaisuutta käyttäessä Firefox lähettää jokaisessa peräkkäisessä pyynnössä user agent- ja http-referer -kentät, vaikka varsinkaan ensinmainittua ei ole mitään syytä lähettää muissa kuin ensimmäisessä pyynnössä. Myös palvelinten vastausten otsakkeissa on usein turhia kenttiä, joiden sisältö ei voi vastausten välillä muuttua. Turhaan uudelleenlähetettyjen kenttien poistaminen selaimen ja palvelimen välisestä tiedonsiirrosta antaisi vielä jonkin verran lisää nopeutta sivujen latautumiseen.

Loppusanat ja ajatuksia

Varsinkin mobiilinettiin siirtymisen jälkeen olen löytänyt päätelaitteista monenlaisia epämääräisiä reititysbugeja, jotka vaikuttavat merkittävästi internet-yhteyden toimivuuteen. Tässä tapauksessa todennäköisesti reitittimen SPI-palomuurissa oleva bugi haittaa HTTP/1.x-protokollan optimaalista toimintaa ja saa HTTP/2-protokollan vaikuttamaan vertailussa huomattavasti nopeammalta kuin se todellisuudessa on. Suurin osa nettisivuista käyttää HTTP/1.x- protokollaa, ja sitä voidaan pitää webin käytetyimpänä protokollana. Kyse ei siis ole mistään merkityksettömästä niche-jutusta. SPI-palomuuria ei ainakaan kyseisessä reitittimessä (Huawei CPE B593) saa pois päältä.

Huonosti tehdyt verkkolaitteet kirjaimellisesti rikkovat internettiä. Sen jälkeen, kun lankapuhelinverkon purkamisen jälkeen jouduin siirtymään mobiiliverkon kautta toimivan nettiyhteyden käyttäjäksi, en ole käyttänyt yhtäkään täysin ongelmatonta päätelaitetta. Ilmeisesti melko suuri osa kuluttajaluokan verkkolaitteista on firmware-ohjelmistonsa laadun suhteen ala-arvoisia. Uusien suboptimaalisten protokollien kehittäminen saattaa helposti piilottaa ongelman.