I den siste artikkelen så vi på HTTP-meldinger og så eksempler på tekstkommandoer og koder som går fra klienten til serveren og tilbake i en HTTP-transaksjon. Men hvordan går informasjonen i disse meldingene gjennom nettverket? Når åpnes nettverksforbindelsene? Når er forbindelsene lukket? Dette er noen av spørsmålene denne artikkelen vil svare på når vi ser på HTTP fra et lavnivåperspektiv. Men først må vi forstå noen av abstraksjonene under HTTP.
For å forstå HTTP-tilkoblinger må vi bare vite litt om hva som skjer i lagene under HTTP. Nettverkskommunikasjon, som mange applikasjoner, består av lag. Hvert lag i a kommunikasjonsstabel er ansvarlig for et bestemt og begrenset antall ansvar.
For eksempel er HTTP det vi kaller en programlagringsprotokoll fordi det tillater to applikasjoner å kommunisere over et nettverk. Ofte er en av applikasjonene en nettleser, og den andre applikasjonen er en webserver som IIS eller Apache. Vi så hvordan HTTP-meldinger tillater nettleseren å be om ressurser fra serveren. Men, HTTP-spesifikasjonene sier ikke noe om hvordan meldingene faktisk krysser nettverket og når serveren - det er jobben med lavere lag protokoller. En melding fra en nettleser må gå ned i en rekke lag, og når den kommer til webserveren, reiser den opp gjennom en rekke lag for å nå webtjenesteprosessen.
ProtokollagLaget under HTTP er a transportlag protokoll. Nesten all HTTP-trafikk beveger seg over TCP (kort for Transmission Control Protocol), selv om dette ikke kreves av HTTP. Når en bruker skriver inn en nettadresse i nettleseren, trekker nettleseren først vertsnavnet fra nettadressen (og eventuelt portnummeret), og åpner en TCP-kontakt ved å spesifisere serveradressen (avledet fra vertsnavnet) og porten (som standard til 80).
Når en applikasjon har en åpen kontakt, kan det begynne å skrive data inn i kontakten. Det eneste nettleseren trenger å bekymre seg om, er å skrive en riktig formatert HTTP-forespørselsmelding i kontakten. TCP-laget aksepterer dataene og sikrer at meldingen blir levert til serveren uten å gå seg vill eller duplisere. TCP vil automatisk sende alle opplysninger som kan gå seg vill i transitt, og derfor er TCP kjent som en pålitelig protokoll. I tillegg til feilsøking gir TCP også flytkontroll. Strømstyringsalgoritmene i TCP vil sikre at avsenderen ikke sender data for fort til mottakeren behandler dataene. Strømkontroll er viktig i denne verden av varierte nettverk og enheter.
Kort sagt, TCP gir tjenester som er avgjørende for vellykket levering av HTTP-meldinger, men det gjør det på en gjennomsiktig måte, slik at de fleste programmer ikke trenger å bekymre seg for TCP. Som forrige figur viser, er TCP bare det første laget under HTTP. Etter TCP på transportlaget kommer IP som et nettverkslag protokoll.
IP er kort for Internett protokoll. Selv om TCP er ansvarlig for feilsøking, flytkontroll og total pålitelighet, er IP ansvarlig for å ta informasjon og flytte dem gjennom de ulike bryterne, rutene, gateways, repeaters og andre enheter som flytter informasjon fra ett nettverk til det neste og over hele verden. IP prøver hardt å levere dataene til destinasjonen (men det garanterer ikke levering - det er TCPs jobb). IP krever at datamaskiner har en adresse (den berømte IP-adressen, et eksempel er 208.192.32.40). IP er også ansvarlig for å bryte data i pakker (ofte kalt datagrammer), og noen ganger fragmentere og reassemble disse pakkene slik at de er optimalisert for et bestemt nettverkssegment.
Alt vi har snakket om så langt, skjer i en datamaskin, men til slutt må disse IP-pakkene reise over et stykke ledning, en fiberoptisk kabel, et trådløst nettverk eller en satellittlänk. Dette er ansvaret for data link lag. Et vanlig valg av teknologi på dette punktet er Ethernet. På dette nivået blir datapakker rammer, og protokoller på lavnivå som Ethernet er fokusert på 1s, 0s og elektriske signaler.
Til slutt når signalet til serveren og kommer inn via et nettverkskort der prosessen er reversert. Datakoblingslaget gir pakker til IP-laget, som overfører data til TCP, som kan sette sammen dataene i den opprinnelige HTTP-meldingen som sendes av klienten og skyve den inn i webserverprosessen. Det er et vakkert konstruert arbeidsstykke som alle er gjort mulig av standarder.
Hvis du lurer på hvordan det ser ut til å skrive et program som vil gjøre HTTP-forespørsler, er følgende C # -kode et enkelt eksempel på hvordan koden kan se ut. Denne koden har ingen feilhåndtering, og prøver å skrive noen serverrespons i konsollvinduet (slik at du må be om en tekstressurs), men det fungerer for enkle forespørsler. En kopi av følgende kodeeksempel er tilgjengelig fra https://bitbucket.org/syncfusion/http-succinctly. Eksempelnavnet er stikkontakt.
bruker system; bruker System.Net; bruker System.Net.Sockets; bruker System.Text; offentlig klasse GetSocket offentlig statisk tomgang Main (streng [] args) var host = "www.wikipedia.org"; var ressurs = "/"; Console.WriteLine ("Koble til 0", vert); hvis (args.GetLength (0)> = 2) host = args [0]; ressurs = args [1]; var result = GetResource (vert, ressurs); Console.WriteLine (resultat); privat statisk streng GetResource (streng vert, streng ressurs) var hostEntry = Dns.GetHostEntry (vert); var socket = CreateSocket (hostEntry); SendRequest (socket, vert, ressurs); returnere GetResponse (socket); privat statisk Socket CreateSocket (IPHostEntry hostEntry) const int httpPort = 80; foreach (var adresse i hostEntry.AddressList) var endPoint = ny IPEndPoint (adresse, httpPort); var socket = ny Socket (endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); socket.Connect (endepunkt); hvis (socket.Connected) retur socket; returnere null; privat statisk tomt SendRequest (stikkontakt, strengverdi, strengressurs) var requestMessage = String.Format ("GET 0 HTTP / 1.1 \ r \ n" + "Verter: 1 \ r \ n" + " \ r \ n ", ressurs, vert); var requestBytes = Encoding.ASCII.GetBytes (requestMessage); socket.Send (requestBytes); privat statisk streng GetResponse (socket socket) int bytes = 0; byte [] buffer = ny byte [256]; var result = new StringBuilder (); gjør bytes = socket.Receive (buffer); result.Append (Encoding.ASCII.GetString (buffer, 0, bytes)); mens (byte> 0); returresultat.ToString ();
Legg merke til hvordan programmet må se opp serveradressen (ved bruk av Dns.GetHostEntry
), og formuler en riktig HTTP-melding med en FÅ
operatør og Vert
Overskrift. Den faktiske nettverksdelen er ganske enkelt, fordi kontakten og TCP tar vare på det meste av arbeidet. TCP forstår for eksempel hvordan du administrerer flere tilkoblinger til samme server (de vil alle motta forskjellige portnumre lokalt). På grunn av dette vil to utestående forespørsler til den samme serveren ikke bli forvirret og motta data beregnet på den andre.
Hvis du vil ha litt synlighet i TCP og IP, kan du installere et gratis program som Wireshark (tilgjengelig for OSX og Windows fra wireshark.org). Wireshark er en nettverksanalysator som kan vise deg all informasjon som flyter gjennom nettverksgrensesnittene dine. Ved hjelp av Wireshark kan du observere TCP-håndtrykk, hvilke TCP-meldinger kreves for å etablere en forbindelse mellom klienten og serveren før de faktiske HTTP-meldingene begynner å flyte. Du kan også se TCP- og IP-overskriftene (20 byte hver) på hver melding. Følgende figur viser de to siste trinnene i håndtrykket, etterfulgt av a FÅ
forespørsel og a 304
omdirigere.
Med Wireshark kan du se når HTTP-tilkoblinger er etablert og stengt. Den viktige delen å ta bort fra alt dette er ikke hvordan håndtrykk og TCP fungerer på det laveste nivået, men at HTTP avhenger nesten helt på TCP for å ta vare på alt hardt arbeid og TCP innebærer noen overhead, som håndtrykk. Dermed er ytelsesegenskapene til HTTP også avhengig av ytelseskarakteristikkene til TCP, og dette er emnet for neste del.
I de aller gamle dagene på nettet var de fleste ressurser tekstlige. Du kan be om et dokument fra en webserver, gå av og lese i fem minutter, og be om et annet dokument. Verden var enkel.
For dagens web, krever de fleste nettsider mer enn en enkelt ressurs for å gi fullstendig gjengivelse. Hver side i et webprogram har ett eller flere bilder, en eller flere JavaScript-filer og en eller flere CSS-filer. Det er ikke uvanlig at den første forespørselen om en startside skal gi 30 eller 50 ekstra forespørsler for å hente alle de andre ressursene som er knyttet til en side.
I gamle dager var det også enkelt for en nettleser å etablere en forbindelse med en server, sende en forespørsel, motta svaret og lukke forbindelsen. Hvis dagens nettlesere åpnet tilkoblinger en om gangen, og ventet på at hver ressurs skulle lastes ned helt før du begynner neste nedlasting, ville weben føle seg veldig treg. Internett er fullt av latens. Signaler må reise lange avstander og vei seg gjennom ulike deler av maskinvare. Det er også noen overhead i å etablere en TCP-tilkobling. Som vi så i Wireshark skjermbildet, er det et tre-trinns håndtrykk for å fullføre før en HTTP-transaksjon kan begynne.
Utviklingen fra enkle dokumenter til komplekse sider har krevd noe oppfinnsomhet i den praktiske bruken av HTTP.
De fleste brukeragenter (aka nettlesere) vil ikke foreta forespørsler på en seriell en-mot-en måte. I stedet åpner de flere parallelle tilkoblinger til en server. For eksempel, når du laster ned HTML for en side, kan nettleseren se to koder på siden, slik at nettleseren vil åpne to parallelle tilkoblinger for å laste ned de to bildene samtidig. Antall parallelle tilkoblinger avhenger av brukeragenten og agentens konfigurasjon.
I lang tid vurderte vi to som det maksimale antall parallelle tilkoblinger en nettleser ville opprette. Vi betraktet to som maksimum fordi den mest populære nettleseren i mange år - Internet Explorer (IE) 6 - bare tillater to samtidige tilkoblinger til en enkelt vert. IE fulgte bare reglene som stavet ut i HTTP 1.1-spesifikasjonen, som sier:
En enkeltbrukerklient SKAL IKKE opprettholde mer enn 2 tilkoblinger med hvilken som helst server eller proxy.
For å øke antall parallelle nedlastinger, bruker mange nettsteder noen triks. For eksempel er grensegrensen for to tilkoblinger per vert, noe som betyr at en nettleser som IE 6 vil gjerne gjøre to parallelle tilkoblinger til www.odetocode.com, og to parallelle tilkoblinger til images.odetocode.com. Ved å ta imot bilder på en annen server, kan nettsteder øke antall parallelle nedlastinger og gjøre sidene deres lastet raskere (selv om DNS-postene ble konfigurert for å peke alle fire forespørsler til den samme serveren, fordi grensen for to tilkoblinger er per vertsnavn, ikke IP-adresse).
Ting er forskjellige i dag. De fleste brukeragenter vil bruke et annet sett med heuristikk når de bestemmer hvor mange parallelle tilkoblinger som skal etableres. For eksempel vil Internet Explorer 8 nå åpne opptil seks samtidige tilkoblinger.
Det virkelige spørsmålet er å: Hvor mange forbindelser er for mange? Parallelle tilkoblinger vil følge loven om sviktende avkastning. For mange tilkoblinger kan mette og stramme nettverket, spesielt når mobile enheter eller upålitelige nettverk er involvert. Å ha for mange tilkoblinger kan dermed skade ytelsen. En server kan også bare godta et begrenset antall tilkoblinger, så hvis 100 000 nettlesere samtidig lager 100 tilkoblinger til en enkelt webserver, vil det skje dårlige ting. Likevel, bruk av mer enn én tilkobling per agent er bedre enn å laste ned alt på en seriell måte.
Heldigvis er parallelle tilkoblinger ikke den eneste ytelsesoptimaliseringen.
I de tidlige dagene på nettet ville en brukeragent åpne og lukke en forbindelse for hver enkelt forespørsel den sendte til en server. Denne implementeringen var i samsvar med HTTPs ide om å være en helt statsløs protokoll. Etter hvert som antall forespørsler per side vokste, gjorde også overhead generert av TCP-håndtrykk og datainnstillingene i minnet som kreves for å etablere hver TCP-kontakt. For å redusere dette overhead og forbedre ytelsen, foreslår HTTP 1.1-spesifikasjonen at klienter og servere skal implementere vedvarende forbindelser, og gjør vedvarende tilkoblinger standard type tilkobling.
En vedvarende tilkobling forblir åpen etter fullføring av en forespørsel-respons-transaksjon. Denne virkemåten etterlater en brukeragent med en allerede åpen stikkontakt, som den kan bruke til å fortsette å stille forespørsler til serveren uten overhead for å åpne en ny stikkontakt. Vedvarende tilkoblinger unngår også den langsomme startstrategien som er en del av TCP-overbelastningsstyring, noe som gjør vedvarende tilkoblinger bedre over tid. Kort sagt, vedvarende tilkoblinger reduserer minnebruk, reduserer CPU-bruk, reduserer nettverksbelastning, reduserer ventetiden, og forbedrer generelt svartiden på en side. Men som alt i livet er det en downside.
Som nevnt tidligere, kan en server bare støtte et begrenset antall innkommende tilkoblinger. Det eksakte nummeret avhenger av hvor mye minne som er tilgjengelig, konfigurasjonen av serverprogramvaren, programmets ytelse og mange andre variabler. Det er vanskelig å gi et nøyaktig nummer, men generelt sett, hvis du snakker om å støtte tusenvis av samtidige tilkoblinger, må du begynne å teste for å se om en server vil støtte belastningen. Faktisk er mange servere konfigurert for å begrense antall samtidige tilkoblinger langt under det punktet hvor serveren vil falle over. Konfigurasjonen er et sikkerhetsmål for å forhindre angrep på tjenestenes angrep. Det er relativt enkelt for noen å lage et program som vil åpne tusenvis av vedvarende forbindelser til en server og holde serveren fra å svare på virkelige kunder. Vedvarende forbindelser er en ytelsesoptimalisering, men også et sårbarhet.
Å tenke i takt med et sårbarhet, må vi også lure på hvor lenge det er å holde en vedvarende forbindelse åpen. I en verden med uendelig skalerbarhet kan forbindelsene være åpne så lenge brukeragentprogrammet kjørte. Men fordi en server støtter et begrenset antall tilkoblinger, konfigureres de fleste servere for å stenge en vedvarende tilkobling hvis den er ledig i noen tidsperiode (for eksempel fem sekunder i Apache). Brukeragenter kan også lukke tilkoblinger etter en tomgangstid. Den eneste synligheten i tilkoblede forbindelser er gjennom en nettverksanalysator som Wireshark.
I tillegg til aggressiv lukning av vedvarende tilkoblinger, kan de fleste webserverprogramvarene konfigureres for å deaktivere vedvarende tilkoblinger. Dette er vanlig med delte servere. Felles servere ofre ytelse for å tillate så mange tilkoblinger som mulig. Fordi vedvarende tilkoblinger er standardkoblingsstilen med HTTP 1.1, må en server som ikke tillater vedvarende tilkoblinger, inkludere en Forbindelse
header i hver HTTP-respons. Følgende kode er et eksempel.
HTTP / 1.1 200 OK Innholdstype: tekst / html; charset = utf-8 Server: Microsoft-IIS / 7.0 X-AspNet-Versjon: 2.0.50727 X-Powered-By: ASP.NET Tilkobling: Lukk Innholdslengde: 17149
De Tilkobling: Lukk
header er et signal til brukeragenten at tilkoblingen ikke vil være vedvarende og bør lukkes så snart som mulig. Agenten har ikke lov til å foreta en ny forespørsel på samme tilkobling.
Parallelle tilkoblinger og vedvarende tilkoblinger brukes både mye og støttes av klienter og servere. HTTP-spesifikasjonen tillater også pipelined tilkoblinger, som ikke er like mye støttet av enten servere eller klienter. I en pipelined forbindelse kan en brukeragent sende flere HTTP-forespørsler på en tilkobling før han venter på det første svaret. Pipelining muliggjør en mer effektiv pakking av forespørsler i pakker og kan redusere ventetid, men det er ikke så mye støttet som parallelle og vedvarende forbindelser.
I dette kapitlet har vi sett på HTTP-tilkoblinger og snakket om noen av de optimaliseringene som ble gjort mulig med HTTP-spesifikasjonene. Nå som vi har dratt dypt inn i HTTP-meldinger og til og med undersøkt forbindelsene og TCP-støtten under protokollen, tar vi et skritt tilbake og ser på Internett fra et bredere perspektiv.