Fragen? Antworten! Siehe auch: Alternativlos
GET / HTTP/1.1Das ist ein HTTP-Request. Die Antwort sieht dann so aus:
Host: www.server.com:80
HTTP/1.1 OKDas Problem ist jetzt, dass das idealisierte Beispiele waren. Tatsächlich sehen Requests nämlich so aus:huhu
GET / HTTP/1.1Und Antworten sehen so aus:
Host: www.cnn.com:80
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:36.0) Gecko/20100101 Firefox/36.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Cookie: token=kiMLX8/1ni34+wrDrAfx7XDDNjSLJhzIXFL27VY2SwM= Connection: keep-alive
HTTP/1.1 200 OK x-servedByHost: prd-10-60-165-21.nodes.56m.dmtio.net Cache-Control: max-age=2592000 Content-Type: text/html Via: 1.1 varnish Content-Length: 11502 Accept-Ranges: bytes Date: Mon, 09 Mar 2015 16:37:46 GMT Via: 1.1 varnish Age: 702161 Connection: keep-alive X-Served-By: cache-iad2130-IAD, cache-lax1421-LAX X-Cache: HIT, HIT X-Cache-Hits: 1414, 5 X-Timer: S1425919066.634699,VS0,VE0 Vary: Accept-Encoding [… der HTML-Inhalt …]Praktisch alles davon ist überflüssig. Sowohl auf Client-, als auch auf Serverseite. Ein Teil davon ist dem Standard geschuldet (wieso muss der Server bitte das Datum mitschicken?!), aber der Großteil ist einfach Bloat von Clients und Servern. Alleine schon der User-Agent ist eine Größenordnung zu groß. Der Accept-Header ist ein typischer Fall von Feature Creep, war mal gut gemeint, hat aber so dermaßen auf ganzer Linie verkackt, dass da jetzt "*/*" als "akzeptierter MIME-Type" drin steht. Damit wird natürlich die Intention des Headers völlig unterwandert und man könnte ihn auch eigentlich komplett weglassen. Ähnlich sieht es mit Accept-Languages aus. Das war mal dafür gedacht, dass der Browser sagt "mein Benutzer spricht Deutsch, kann aber auch Englisch", und die Webseite gibt dann ihre deutschsprachige Version heraus. Das benutzen tatsächlich einige Webseiten. So gefühlt 3 oder 4. Hätte man sich also auch sparen können. Accept-Encoding sagt, dass der Browser auch gzip und deflate kann. Das hätte man schlicht als Default in den Standard schreiben können, dann würde man sich das sparen, dass alle Browser das mitschicken. DNT ist der Do-Not-Track-Header. "Liebe Werbetreibenden, bitte missbraucht meine Daten nicht!1!!" Ja nee, klar. Die Cookies sind im Verantwortungsbereich des Webseitenbetreibers, da kann der Browser nichts für. Der Connection-Header hier ist auch für den Fuß, weil keep-alive bei HTTP/1.1 Standard ist. Aber sicherheitshalber, falls der Server nur 1.0 kann, schicken wir es nochmal mit. Ihr seht schon, das ist ein verkrusteter Haufen fossiler Brennelemente.
Schlimmer noch sieht es auf der Serverseite aus. Da sind die Hälfte der Header direkt für die Tonne und kommen auch offenbar gar nicht vom Webserver sondern von dem Varnish-Cache davor. So nützlich wie ein Hirntumor!
So und jetzt schauen wur uns das mal zusammen an und überlegen uns eine Strategie. KLAR! Die Cookies sind das Problem!1!! Also war die Lösung der Webseiten-Optimierer-Spezialexperten, dass sie die 100 Milliarden Inline-Bilder auf eine separate Domain ausgliedern. Dann werden bei den Requests an die keine Cookies der Hauptdomain übertragen. WAS MAN DA FÜR EINEN TRAFFIC SPART!!1! Leider handelt man sich auch einen zusätzlichen DNS Round Trip ein. Aber hey, das sieht man in Benchmarks nicht so, weil das eh schon im Cache ist!1!!
Kurzum: HTTP/1.0 hat Probleme in der Praxis. Beachtet aber das "Connection: keep-alive". Das heißt, dass der Server die Verbindung nach diesem Request offen hält. Der Client kann dann den nächsten Request absetzen. Das entsorgt direkt das wichtigste Argument für HTTP/2, das mit dem "wir wollen aber nur ein Handshake am Anfang der Verbindung haben, weil das so teuer ist".
Also haben die HTTP/2-Apologeten argumentiert, naja gut, das Handshake ist nicht das Problem, sondern die Latenz pro Antwort ist das Problem. Um das zu illustrieren, muss man ein bisschen malen. Ich mache das mal in ASCII Art, mit ??? für den Request und === für die Antwort.
??? ???So stellt man sich eine typische HTTP-Verbindung vor. So ist das auch — im LAN. Über das Internet gibt es Latenzen zwischen ??? und ===. Die male ich jetzt mal als |||.
=== ===
??? ???Ihr seht, diese Latenzen dominieren schnell. Daher gibt es in HTTP/1.1 ein Verfahren namens Pipelining. Das sieht dann so aus:
||| ||| ||| |||
=== ===
?????????Die Latenz ist nicht weg, aber sie ist viel weniger schlimm. Leider implementieren das einige wenige Webserver nicht. Und um diese Webserver im Geschäft zu halten, schalten die Browser Pipelining aus. Nein, wirklich! Geht mal in Firefox auf about:config und sucht nach network.http.pipelining.
|||||||||||||||
=========
Weil das der zentrale argumentative Dreh- und Angelpunkt der HTTP/2-Apologeten ist, will ich jetzt mal einen Trick mit euch teilen.
Der Grund, wieso einige Webserver Pipelining nicht können, ist, dass deren Haupt-Schleife so aussieht:
Solange TCP-Verbindung besteht:Bei Pipelinig kommen jetzt mehrere Requests in einem Stück Daten rein. Der Server hat die Logik nicht, um hinter dem 1. Request nach weiteren zu gucken. Aber man kann auch mit so einem Server Pipelining machen. Ich habe das vor vielen Jahren mal für einen Usenet-Downloader implementiert, und später für SMB, bei HTTP geht das genau so. Man schickt den nächsten HTTP-Requests nicht, nachdem das letzte Byte der Antwort des vorigen angekommen ist, sondern nachdem das erste Byte der Antwort des vorigen angekommen ist.
Lese Daten
Wenn die Daten ein HTTP-Request sind, beantworte ihn
Das lohnt sich leider erst, wenn die Downloadgrößen deutlich größer als die Request-Größen sind. Daher ändere ich mal die Symbolik ein bisschen in der ASCII-Art. Vorher:
??? ???Was die Grafik euch sagen will: Der zweite Request kommt beim Server an, während der noch den ersten beantwortet. So kann man das machen. Warum das bei HTTP keine Option war? Weil inzwischen bei einigen Sites für einige Requests die Header größer als die Inhalte sind.
vvv ^^^ vvv
===============
Meine Einstellung dazu ist: Einfach immer Pipelining vorschreiben und anschalten, und wer das nicht implementiert, der kann halt nicht mitspielen. Ich hab das am Anfang in gatling auch nicht implementiert gehabt, weil es eh keiner benutzt hat. Diese Art von Verhalten geht überhaupt nur, weil die Browser einen damit durchkommen lassen. Das sollten sie nicht.
Hat HTTP/2 denn jetzt überhaupt keine Vorteile? Doch. Zwei. Erstens haben sie sich ein Verfahren überlegt, um Header zu komprimieren. Ich habe mir davon die Details noch nicht angeguckt. Ich sah bisher nur Negatives darüber, aber das will ja nichts heißen. Zweitens schreiben sie für den TLS-Teil TLS 1.2 vor.
Merkt ihr was?
Wir könnten auch einfach HTTP/1.1 nehmen, Pipelining und TLS 1.2 vorschreiben, und wir hätten so gut wie alle Vorteile von HTTP/2 ohne die Nachteile.
Welche Nachteile? Nun, HTTP/2 macht Multiplexing. Multiplexing heißt, dass sie mehr als eine virtuelle Verbindung über eine TCP-Verbindung fahren. Das ist eine ganz beschissene Idee.
Stellt euch mal Youtube vor. Sobald er das Video überträgt, ist die Multiplex-Verbindung ausgelastet und die Kommentare kommen nie. Außer der Server hat Logik, um faires Scheduling zu implementieren. Ja ganz groß!
Geht noch weiter. Stellt euch mal vor, der Server hat fair scheduling implementiert. Ist immer noch Scheiße. Denn einige Elemente einer Webseite können andere Elemente referenzieren, Inline-Bilder, Skripte, was auch immer. HTML und CSS sollten daher zuerst übertragen werden, auch damit der Browser schonmal das Layout machen kann, und dann später die Bilder reinlädt. Jetzt muss der Server also schon intelligentes Scheduling implementieren. Am besten mit Content Sniffing. Denn das wird die nächste Anforderung sein, dass der Server das HTML/CSS parsed und die Dateien schonmal öffnet und reinlädt, die der Browser gleich haben wollen wird.
Aber nein, da haben die HTTP/2-Leute eine noch bessere Idee gehabt. Sie sehen vor, dass der Browser dem Server sagt, was mit welcher Priorität kommen soll.
Ihr sehr schon: Alleine um Probleme zu lösen, die wir vorher nicht hatten, treiben wir hier ungefähr so viel Aufwand, wie wir vorher für den ganzen Webserver getrieben haben.
Daher glaube ich, dass HTTP/2 ein Schuss in den Ofen ist.
Aber wartet. Einen habe ich noch. Nehmen wir mal an, wir haben Paketverlust auf der Leitung. Bei HTTP/1 bleibt dann eine Verbindung stehen. Eine von sechs oder wieviel auch immer der Browser aufgemacht hat. Bei HTTP/2 bleibt dann alles stehen, weil alles über die eine Verbindung ging. JA SUPER!
Erwähnte ich, dass HTTP/2 mit dem Header-Wildwuchs nicht aufgeräumt hat? Dafür gibt es ja jetzt Kompression. Hier ist die Spec dazu.
Leider haben die auch ansonsten im Protokolldesign und der Spec einmal großflächig alles verkackt, was man so verkacken kann. Klickt euch mal in diesem Mailinglistenarchiv nur durch die Postings von Bob Briscoe der letzten Tage. Übrigens ist auch der Autor von varnish nicht begeistert von HTTP/2.