Quando i server web inviano una pagina, perché non inviano tutti i CSS, JS e le immagini richiesti senza che venga richiesto?


45

Quando una pagina Web contiene un singolo file CSS e un'immagine, perché browser e server perdono tempo con questo percorso tradizionale che richiede molto tempo:

  1. il browser invia una richiesta GET iniziale per la pagina Web e attende la risposta del server.
  2. il browser invia un'altra richiesta GET per il file css e attende la risposta del server.
  3. il browser invia un'altra richiesta GET per il file immagine e attende la risposta del server.

Quando invece hanno potuto usare questo percorso breve, diretto e che fa risparmiare tempo?

  1. Il browser invia una richiesta GET per una pagina Web.
  2. Il web server risponde con ( index.html seguito da style.css e image.jpg )

2
Non è possibile effettuare alcuna richiesta fino a quando non viene recuperata la pagina Web. Successivamente, le richieste vengono effettuate in ordine man mano che l'HTML viene letto. Ma ciò non significa che venga fatta una sola richiesta alla volta. In effetti, vengono fatte diverse richieste ma a volte ci sono dipendenze tra le richieste e alcune devono essere risolte prima che la pagina possa essere dipinta correttamente. I browser a volte si fermano quando una richiesta viene soddisfatta prima di apparire per gestire altre risposte facendo sembrare che ogni richiesta venga gestita una alla volta. La realtà è più sul lato browser in quanto tendono ad essere ad alta intensità di risorse.
closetnoc,

20
Sono sorpreso che nessuno abbia menzionato la memorizzazione nella cache. Se ho già quel file non mi serve inviarlo.
Corey Ogburn,

2
Questo elenco potrebbe essere lungo centinaia di cose. Sebbene sia più breve rispetto all'effettivo invio dei file, è ancora piuttosto lontano da una soluzione ottimale.
Corey Ogburn,

1
In realtà, non ho mai visitato una pagina web che ha più di 100 risorse uniche ..
Ahmed,

2
@AhmedElsoobky: il browser non sa quali risorse possono essere inviate come intestazione delle risorse memorizzate nella cache senza prima recuperare la pagina stessa. Sarebbe anche un incubo per la privacy e la sicurezza se il recupero di una pagina dice al server che ho un'altra pagina memorizzata nella cache, che è probabilmente controllata da un'organizzazione diversa rispetto alla pagina originale (un sito Web multi-tenant).
Lie Ryan,

Risposte:


63

La risposta breve è "Perché HTTP non è stato progettato per questo".

Tim Berners-Lee non ha progettato un protocollo di rete efficiente ed estensibile. Il suo unico obiettivo progettuale era la semplicità. (Il professore della mia classe di networking al college ha detto che avrebbe dovuto lasciare il lavoro ai professionisti.) Il problema che descrivi è solo uno dei tanti problemi con il protocollo HTTP. Nella sua forma originale:

  • Non c'era una versione del protocollo, solo una richiesta per una risorsa
  • Non c'erano intestazioni
  • Ogni richiesta richiedeva una nuova connessione TCP
  • Non c'è stata compressione

Il protocollo è stato successivamente rivisto per risolvere molti di questi problemi:

  • Le richieste sono state versionate, ora le richieste sembrano GET /foo.html HTTP/1.1
  • Sono state aggiunte le intestazioni per le meta informazioni con la richiesta e la risposta
  • Le connessioni potevano essere riutilizzate Connection: keep-alive
  • Sono state introdotte risposte bloccate per consentire il riutilizzo delle connessioni anche quando le dimensioni del documento non sono note in anticipo.
  • È stata aggiunta la compressione Gzip

A questo punto HTTP è stato adottato per quanto possibile senza interrompere la retrocompatibilità.

Non sei la prima persona a suggerire che una pagina e tutte le sue risorse debbano essere inviate al client. In effetti, Google ha progettato un protocollo che può essere chiamato SPDY .

Oggi sia Chrome che Firefox possono utilizzare SPDY anziché HTTP per i server che lo supportano. Dal sito Web SPDY, le sue caratteristiche principali rispetto a HTTP sono:

  • SPDY consente a client e server di comprimere le intestazioni di richiesta e risposta, il che riduce l'utilizzo della larghezza di banda quando le intestazioni simili (ad esempio i cookie) vengono inviate più volte per più richieste.
  • SPDY consente più richieste multiple simultaneamente su una singola connessione, risparmiando sui round trip tra client e server e impedendo alle risorse a bassa priorità di bloccare le richieste a priorità più alta.
  • SPDY consente al server di inviare attivamente risorse al client di cui sa che il client avrà bisogno (ad es. File JavaScript e CSS) senza attendere che il client lo richieda, consentendo al server di utilizzare in modo efficiente la larghezza di banda non utilizzata.

Se desideri offrire il tuo sito Web con SPDY ai browser che lo supportano, puoi farlo. Ad esempio Apache ha mod_spdy .

SPDY è diventato la base per HTTP versione 2 con tecnologia push server.


2
Dang risposta buona e informata! I browser Web sono di serie per natura e le richieste possono essere fatte piuttosto rapidamente. Uno sguardo a un file di registro mostrerà che le richieste di risorse vengono fatte piuttosto rapidamente una volta analizzato l'HTML. È quello che è. Non è un cattivo sistema, ma non altrettanto efficiente in termini di codice / risorse.
closetnoc,

6
Per la cronaca, SPDY non è il santo graal. Fa alcune cose bene, ma introduce altri problemi. Ecco un articolo contenente alcuni punti che parlano di nuovo SPDY.
Jost,

3
Consiglio vivamente a chiunque sia interessato a leggere le critiche nel link di @Jost. Ti dà un indizio della complessità coinvolta nel capire come fare una cosa molto comunemente implementata non solo in modo incrementale meglio ma piuttosto molto meglio che tutti iniziano a usarlo . È facile pensare a un miglioramento che rende le cose leggermente migliori per un sottoinsieme relativamente ampio di casi d'uso. Rendere le cose migliori in modo tale che tutti inizino a usare il tuo nuovo protocollo perché è molto meglio che valga la pena cambiare il costo è un'altra cosa, e non è facile da fare.
msouth,

11
avrebbe dovuto lasciare il lavoro ai professionisti : se lo avesse fatto, ci sarebbero voluti sei anni per elaborare uno standard che sarebbe stato obsoleto il giorno in cui sarebbe uscito, e presto sarebbero comparsi una dozzina di standard concorrenti. Inoltre, i professionisti avevano bisogno del permesso di qualcuno? Perché non l'hanno fatto da soli?
Shantnu Tiwari,

2
Ad essere sinceri non ci sono professionisti qualificati allora. Nessuno sa come costruire un world wide web, perché nessuno ne aveva mai costruito uno. Il concetto di hypermedia non è stato inventato da Tim, aveva esperienza con vari sistemi hypermedia di area locale dieci anni prima di scrivere la proposta di "Gestione delle informazioni" per risolvere il problema della "perdita di informazioni" al CERN.
Lie Ryan,

14

Il browser Web non è a conoscenza delle risorse aggiuntive fino a quando non scarica la pagina Web (HTML) dal server, che contiene i collegamenti a tali risorse.

Potresti chiederti, perché il server non analizza il proprio HTML e non invia tutte le risorse aggiuntive al browser durante la richiesta iniziale per la pagina Web? È perché le risorse potrebbero essere distribuite su più server e il browser Web potrebbe non aver bisogno di tutte quelle risorse poiché ne ha già memorizzate alcune nella cache o potrebbe non supportarle.

Il browser Web mantiene una cache di risorse in modo da non dover scaricare le stesse risorse più e più volte dai server che le ospitano. Durante la navigazione di pagine diverse su un sito Web che utilizzano tutte la stessa libreria jQuery, non si desidera scaricare quella libreria ogni volta, solo la prima volta.

Pertanto, quando il browser Web riceve una pagina Web dal server, controlla quali risorse collegate NON ha già nella cache, quindi effettua ulteriori richieste HTTP per tali risorse. Abbastanza semplice, molto flessibile ed estensibile.

Un browser Web può in genere effettuare due richieste HTTP in parallelo. Questo non è diverso da AJAX - sono entrambi metodi asincroni per il caricamento di pagine Web - caricamento asincrono dei file e caricamento asincrono dei contenuti. Con keep-alive , possiamo effettuare diverse richieste utilizzando una sola connessione e con il pipelining possiamo effettuare diverse richieste senza dover attendere le risposte. Entrambe queste tecniche sono molto veloci perché la maggior parte dei costi di solito proviene dall'apertura / chiusura delle connessioni TCP:

keep-alive

pipelining

Un po 'di storia web ...

Le pagine Web sono iniziate come e-mail in chiaro, con i sistemi informatici progettati attorno a questa idea, formando una piattaforma di comunicazione un po 'gratuita per tutti; i server web erano ancora proprietari al momento. Successivamente, sono stati aggiunti più "strati alle specifiche e-mail" sotto forma di ulteriori tipi MIME, come immagini, stili, script, ecc. Dopo tutto, MIME è l'acronimo di Multi-Purpose Internet Mail Extension. Prima o poi avremmo avuto quello che è essenzialmente la comunicazione e-mail multimediale, i server web standardizzati e le pagine web.

HTTP richiede che i dati vengano trasmessi nel contesto di messaggi simili a e-mail, sebbene i dati più spesso non siano effettivamente e-mail.

Man mano che la tecnologia si evolve, deve consentire agli sviluppatori di incorporare progressivamente nuove funzionalità senza rompere il software esistente. Ad esempio, quando un nuovo tipo MIME viene aggiunto alle specifiche, ad esempio JPEG, ci vorrà del tempo prima che i server Web e i browser Web lo implementino. Non devi solo forzare all'improvviso JPEG nelle specifiche e iniziare a inviarlo a tutti i browser Web, ma consenti al browser Web di richiedere le risorse supportate, il che rende felici tutti e la tecnologia va avanti. Uno screen reader necessita di tutti i JPEG su una pagina Web? Probabilmente no. Dovresti essere costretto a scaricare un sacco di file Javascript se il tuo dispositivo non supporta Javascript? Probabilmente no. Googlebot deve scaricare tutti i tuoi file Javascript per indicizzare correttamente il tuo sito? No.

Fonte: ho sviluppato un server web basato su eventi come Node.js. Si chiama Rapid Server .

Riferimenti:

Ulteriori letture:


Bene, in realtà, possiamo occuparci di tutti quei problemi collaterali (cose come: Cache, Content-Type header..etc), ci sono soluzioni alternative per risolvere questi problemi. E come ho suggerito nei commenti sul post sopra, possiamo usare qualcosa come questa intestazione> Risorse nella cache: image.jpg; style.css; per risolvere il problema di memorizzazione nella cache .. (Se hai tempo, puoi dare un'occhiata ai commenti sopra ..)
Ahmed,

Sì, quell'idea mi era venuta in mente in precedenza, ma è semplicemente un sovraccarico eccessivo per HTTP e non risolve il fatto che le risorse possono essere distribuite su più server. Inoltre, non credo che il tuo metodo di risparmio di tempo proposto risparmierebbe davvero tempo perché i dati verranno inviati come flusso, non importa come li guardi, e con keep-alive, 100 richieste HTTP simultanee diventano essenzialmente 1 richiesta. La tecnologia e le capacità che proponi sembrano già esistere in un certo senso. Vedi en.wikipedia.org/wiki/HTTP_persistent_connection
perry

@perry: cosa pensi dell'idea di un'alternativa https://per l'invio di file di grandi dimensioni distribuiti pubblicamente che devono essere autenticati ma non riservati: includere nell'URL un hash di alcune parti dell'intestazione di una risposta legittima, che a sua volta potrebbe includere una firma o un hash del payload dei dati e i browser convalidano i dati ricevuti rispetto all'intestazione? Un tale progetto non solo salverebbe alcuni passaggi dell'handshake SSL, ma soprattutto permetterebbe ai proxy di cache. Ottieni l'URL tramite un collegamento SSL e i dati potrebbero essere alimentati da qualsiasi luogo.
supercat,

11

Perché non sanno quali siano queste risorse. Le risorse richieste da una pagina Web sono codificate in HTML. Solo dopo che un parser determina quali sono questi asset, y può essere richiesto dall'utente-agente.

Inoltre, una volta che tali risorse sono note, devono essere offerte singolarmente in modo che possano essere servite le intestazioni appropriate (tipo di contenuto) in modo che l'agente utente sappia come gestirle.


2
Soprattutto se si utilizza qualcosa come require.js. Il browser chiede solo ciò di cui ha bisogno. Immagina di dover caricare tutto tutto in una volta ...
Aran Mulholland,

2
Questa è la risposta giusta e quella che sembra mancare alla maggior parte dei commentatori - affinché il server invii proattivamente le risorse, deve sapere quali sono, il che significa che il server dovrebbe analizzare l'HTML.

1
Ma la domanda si chiede perché il server Web non invia le risorse, non perché il client non può richiederle contemporaneamente. È molto facile immaginare un mondo in cui i server hanno un pacchetto di risorse correlate che vengono tutte inviate insieme e che non si basa sull'analisi dell'HTML per creare il pacchetto.
David Meister,

@DavidMeister Perché il server non sa sempre cosa vuole il client - un webcrawler per un motore di ricerca potrebbe non interessarsi del CSS / JS e ci sono molte altre risorse collegate in un documento oltre a quelle - non è necessario inviare l'intero Feed RSS verso il basso nel pacchetto a un browser Web (probabilmente la maggior parte del contenuto è già nell'HTML), mentre un lettore di feed potrebbe semplicemente analizzare l' <head>elemento alla ricerca di collegamenti alternativi RSS per trovare proprio questo: il client potrebbe inviare un elenco di a cosa è interessato, ma poi ha bisogno di sapere cosa è disponibile e siamo tornati all'inizio
Zhaph - Ben Duguid

@ Zhaph-BenDuguid Sto parlando di un mondo alternativo, per evidenziare che la risposta ha tanto a che fare con il funzionamento del protocollo come qualsiasi altra cosa. Inoltre, potrebbe essere più veloce per un server inviare tutti i dati contemporaneamente anche se non è necessario. In sostanza, stai eliminando i problemi di latenza dall'uso della larghezza di banda.
David Meister,

8

Perché, nel tuo esempio, il server web invierebbe sempre CSS e immagini indipendentemente dal fatto che il client li abbia già, sprecando notevolmente la larghezza di banda (e rendendo così la connessione più lenta , anziché più veloce riducendo la latenza, che era presumibilmente la tua intenzione). Si noti che i file CSS, JavaScript e di immagine vengono generalmente inviati con tempi di scadenza molto lunghi proprio per questo motivo (come quando è necessario modificarli, è sufficiente modificare il nome del file per forzare la nuova copia che verrà nuovamente memorizzata nella cache per molto tempo).

Ora, puoi provare a aggirare lo spreco di larghezza di banda dicendo " OK, ma il client potrebbe indicare che ha già alcune di quelle risorse, quindi il server non lo invierà di nuovo ". Qualcosa di simile a:

GET /index.html HTTP/1.1
Host: www.example.com
If-None-Match: "686897696a7c876b7e"
Connection: Keep-Alive

GET /style.css HTTP/1.1
Host: www.example.com
If-None-Match: "70b26618ce2c246c71"

GET /image.png HTTP/1.1
Host: www.example.com
If-None-Match: "16d5b7c2e50e571a46"

E quindi ottenere solo i file che non sono stati modificati vengono inviati su una connessione TCP (utilizzando pipeline HTTP su connessione permanente). E indovina cosa? Funziona già così (potresti anche usare If-Modified-Since invece di If-None-Match ).


Ma se vuoi davvero ridurre la latenza sprecando molta larghezza di banda (come nella tua richiesta originale), puoi farlo oggi usando HTTP / 1.1 standard durante la progettazione del tuo sito web. Il motivo per cui molte persone non lo fanno è perché non pensano che ne valga la pena.

Per farlo, non è necessario disporre di CSS o JavaScript in un file separato, è possibile includerli nel file HTML principale utilizzando <style>e <script>tag (probabilmente non è nemmeno necessario farlo manualmente, il motore del modello può probabilmente farlo automaticamente) . Puoi anche includere immagini nel file HTML usando l'URI dei dati , in questo modo:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

Naturalmente, la codifica base64 aumenta leggermente l'utilizzo della larghezza di banda, ma se non ti interessa perdere la larghezza di banda, questo non dovrebbe essere un problema.

Ora, se ti interessa davvero, potresti persino rendere i tuoi script web abbastanza intelligenti da ottenere il meglio da entrambi i mondi: alla prima richiesta (l'utente non ha un cookie), invia tutto (CSS, JavaScript, immagini) incorporato in un unico HTML file come descritto sopra, aggiungere un collegamento rel = "prefetch" tag per copie esterne dei file e aggiungere un cookie. Se l'utente ha già un cookie (ad es. Ha già visitato in precedenza), inviagli solo un normale HTML con <img src="example.jpg">, <link rel="stylesheet" type="text/css" href="style.css">ecc.

Quindi alla prima visita il browser richiederebbe solo un singolo file HTML e ottenere e mostrare tutto. Quindi (quando inattivo) precaricherebbe CSS, JS, immagini esterne specificate. La volta successiva che l'utente visita, il browser richiede e ottiene solo le risorse modificate (probabilmente solo un nuovo HTML).

I dati extra di immagini CSS + JS + verrebbero inviati solo due volte, anche se si fa clic su centinaia di volte sul sito Web. Molto meglio di centinaia di volte come suggerito dalla soluzione proposta. E non avrebbe mai (né la prima volta né le volte successive) più di un viaggio di andata e ritorno che aumenta la latenza.

Ora, se sembra troppo lavoro e non vuoi andare con un altro protocollo come SPDY , ci sono già moduli come mod_pagespeed per Apache, che possono fare automaticamente un po 'di quel lavoro per te (unendo più file CSS / JS in uno, auto-inclinando piccoli CSS e minimizzandoli, crea piccole immagini con segnaposto in attesa di caricamento degli originali, caricamento pigro di immagini, ecc.) senza richiedere la modifica di una sola riga della tua pagina web.


3
Penso che questa sia la risposta corretta.
el.pescado,

7

HTTP2 si basa su SPDY e fa esattamente ciò che suggerisci:

Ad alto livello, HTTP / 2:

  • è binario, anziché testuale
  • è completamente multiplato, invece di ordinato e bloccante
  • può quindi utilizzare una connessione per il parallelismo
  • utilizza la compressione dell'intestazione per ridurre il sovraccarico
  • consente ai server di "spingere" in modo proattivo le risposte nella cache dei client

Altro è disponibile su HTTP 2 Faq


3

Perché non presume che queste cose siano effettivamente richieste .

Il protocollo non definisce alcuna gestione speciale per alcun tipo particolare di file o user-agent. Non conosce la differenza tra, per esempio, un file HTML e un'immagine PNG. Per fare ciò che stai chiedendo, il server Web dovrebbe identificare il tipo di file, analizzarlo per capire a quali altri file fa riferimento e quindi determinare quali altri file sono effettivamente necessari, dato che cosa intendi fare il file . Ci sono tre grandi problemi con questo.

Il primo problema è che non esiste un modo standard e affidabile per identificare i tipi di file sul server . HTTP gestisce tramite il meccanismo Content-Type, ma ciò non aiuta il server, che deve capire da solo queste cose (in parte in modo da sapere cosa inserire nel Content-Type). Le estensioni dei nomi di file sono ampiamente supportate, ma fragili e facilmente ingannabili, a volte per scopi dannosi. I metadati del filesystem sono meno fragili, ma la maggior parte dei sistemi non lo supporta molto bene, quindi i server non si preoccupano nemmeno. Lo sniffing dei contenuti (come alcuni browser e il filecomando Unix tentano di fare) può essere robusto se si è disposti a renderlo costoso, ma lo sniffing robusto è troppo costoso per essere pratico sul lato server e lo sniffing economico non è abbastanza robusto.

Il secondo problema è che analizzare un file è costoso, dal punto di vista computazionale . Questo si lega in qualche modo al primo, in quanto avresti bisogno di analizzare il file in diversi modi potenziali se volessi annusare il contenuto in modo robusto, ma si applica anche dopo aver identificato il tipo di file, perché è necessario per capire quali sono i riferimenti. Questo non è così male quando stai facendo solo pochi file alla volta, come fa il browser, ma un server Web deve gestire centinaia o migliaia di richieste contemporaneamente. Ciò si somma e, se va troppo lontano, può effettivamente rallentare le cose più di quanto farebbero più richieste. Se hai mai visitato un link da Slashdot o siti simili, solo per scoprire che il server è incredibilmente lento a causa dell'uso elevato, hai visto questo principio in azione.

Il terzo problema è che il server non ha modo di sapere cosa si intende fare con il file . Un browser potrebbe richiedere che i file vengano referenziati nell'HTML, ma potrebbe non esserlo, a seconda del contesto esatto in cui il file viene eseguito. Sarebbe abbastanza complesso, ma c'è molto di più sul Web oltre ai semplici browser: tra spider, aggregatori di feed e mashup di scraping delle pagine, ci sono molti tipi di user-agent che non hanno bisogno dei file a cui si fa riferimento nell'HTML : essi interessa solo l'HTML stesso. L'invio di questi altri file a tali user-agent sprecherebbe solo la larghezza di banda.

La linea di fondo è che capire queste dipendenze sul lato server è più un problema di quanto valga la pena . Quindi, invece, lasciano che il cliente capisca di cosa ha bisogno.


Se svilupperemo un nuovo protocollo o ne ripareremo uno già esistente, possiamo occuparci di tutti questi problemi in un modo o nell'altro! E il server Web analizzerà i file per una sola volta, quindi potrà classificarli in base a regole definite in modo da poter stabilire la priorità dei file da inviare prima ... ecc. E il server Web non deve sapere cosa sono destinato a fare con quei file, deve solo sapere cosa inviare, quando fare e in base a quali regole .. (web bot e spider non sono un problema, il comportamento sarà diverso con loro - hanno intestazioni user-agent uniche- ..)
Ahmed,

@AhmedElsobky: ciò di cui stai parlando sembra più un'implementazione specifica che un protocollo di rete. Ma deve davvero sapere cosa intendi fare con i file prima di poter determinare cosa inviare: altrimenti invierà inevitabilmente file che molti utenti non vogliono. Non puoi fidarti delle stringhe User-Agent, quindi non puoi usarle per determinare quale sia l'intento dell'utente.
The Spooniest,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.