Cuando los servidores web envían una página, ¿por qué no envían todos los CSS, JS e imágenes requeridos sin que se les solicite?

45

Cuando una página web contiene un solo archivo CSS y una imagen, ¿por qué los navegadores y servidores pierden tiempo con esta ruta tradicional que consume mucho tiempo?

  1. el navegador envía una solicitud GET inicial para la página web y espera la respuesta del servidor.
  2. el navegador envía otra solicitud GET para el archivo css y espera la respuesta del servidor.
  3. el navegador envía otra solicitud GET para el archivo de imagen y espera la respuesta del servidor.

¿Cuándo podrían utilizar esta ruta corta, directa y que ahorra tiempo?

  1. El navegador envía una solicitud GET para una página web.
  2. Los servidores web responde con ( index.html seguido de style.css y imagen.jpg )
Ahmed
fuente
2
No se puede hacer ninguna solicitud hasta que la página web se recupere, por supuesto. Después de eso, las solicitudes se realizan en orden a medida que se lee el HTML. Pero esto no significa que solo se haga una solicitud a la vez. De hecho, se realizan varias solicitudes, pero a veces hay dependencias entre las solicitudes y algunas deben resolverse antes de que la página se pueda pintar correctamente. Los navegadores a veces se detienen cuando se satisface una solicitud antes de que parezca manejar otras respuestas, haciendo que parezca que cada solicitud se maneja una por una. La realidad es más del lado del navegador, ya que tienden a ser intensivos en recursos.
closetnoc
20
Me sorprende que nadie haya mencionado el almacenamiento en caché. Si ya tengo ese archivo, no necesito que me lo envíen.
Corey Ogburn
2
Esta lista podría ser cientos de cosas largas. Aunque es más corto que enviar los archivos, aún está lejos de ser una solución óptima.
Corey Ogburn
1
En realidad, nunca he visitado una página web que tenga más de 100 recursos únicos ...
Ahmed
2
@AhmedElsoobky: el navegador no sabe qué recursos se pueden enviar como encabezado de recursos en caché sin recuperar primero la página. También sería una pesadilla de privacidad y seguridad si recuperar una página le dice al servidor que tengo otra página almacenada en caché, que posiblemente esté controlada por una organización diferente a la página original (un sitio web de múltiples inquilinos).
Lie Ryan

Respuestas:

63

La respuesta corta es "Porque HTTP no fue diseñado para eso".

Tim Berners-Lee no diseñó un protocolo de red eficiente y extensible. Su único objetivo de diseño era la simplicidad. (El profesor de mi clase de redes en la universidad dijo que debería haber dejado el trabajo a los profesionales). El problema que usted describe es solo uno de los muchos problemas con el protocolo HTTP. En su forma original:

  • No había una versión de protocolo, solo una solicitud de un recurso
  • No hubo encabezados
  • Cada solicitud requería una nueva conexión TCP
  • No hubo compresión

El protocolo fue revisado posteriormente para abordar muchos de estos problemas:

  • Las solicitudes fueron versionadas, ahora las solicitudes se ven como GET /foo.html HTTP/1.1
  • Se agregaron encabezados para metainformación con la solicitud y la respuesta
  • Se permitió reutilizar las conexiones con Connection: keep-alive
  • Se introdujeron respuestas fragmentadas para permitir que las conexiones se reutilicen incluso cuando el tamaño del documento no se conoce con anticipación.
  • Se agregó compresión Gzip

En este punto, HTTP se ha llevado lo más lejos posible sin romper la compatibilidad con versiones anteriores.

No es la primera persona en sugerir que una página y todos sus recursos se envíen al cliente. De hecho, Google diseñó un protocolo que puede hacerlo llamado SPDY .

Hoy, tanto Chrome como Firefox pueden usar SPDY en lugar de HTTP para los servidores que lo admiten. Desde el sitio web SPDY, sus características principales en comparación con HTTP son:

  • SPDY permite al cliente y al servidor comprimir encabezados de solicitud y respuesta, lo que reduce el uso de ancho de banda cuando los encabezados similares (por ejemplo, cookies) se envían una y otra vez para múltiples solicitudes.
  • SPDY permite múltiples solicitudes multiplexadas simultáneamente en una sola conexión, ahorrando viajes de ida y vuelta entre el cliente y el servidor y evitando que los recursos de baja prioridad bloqueen las solicitudes de mayor prioridad.
  • SPDY permite que el servidor envíe recursos activamente al cliente que sabe que el cliente necesitará (por ejemplo, archivos JavaScript y CSS) sin esperar a que el cliente los solicite, permitiendo que el servidor haga un uso eficiente del ancho de banda no utilizado.

Si desea servir su sitio web con SPDY a los navegadores que lo admiten, puede hacerlo. Por ejemplo, Apache tiene mod_spdy .

SPDY se ha convertido en la base de HTTP versión 2 con tecnología push de servidor.

Stephen Ostermiller
fuente
2
Dang buena e informada respuesta! Los navegadores web son seriales por naturaleza y las solicitudes se pueden hacer con bastante rapidez. Una mirada a un archivo de registro mostrará que las solicitudes de recursos se realizan con bastante rapidez una vez que se ha analizado el HTML. Es lo que es. No es un mal sistema, simplemente no es tan eficiente en cuanto a código / recursos como podría ser.
closetnoc
66
Solo para que conste, SPDY no es el santo grial. Hace bien algunas cosas, pero presenta otros problemas. Aquí hay un artículo que contiene algunos puntos hablando contra SPDY.
Jost
3
Recomiendo encarecidamente que cualquier persona interesada en esto lea las críticas en el enlace de @Jost. Le da una pista de la complejidad involucrada en descubrir cómo hacer una cosa implementada muy comúnmente, no solo incrementalmente mejor, sino mucho mejor para que todos comiencen a usarla . Es fácil pensar en una mejora que mejore un poco las cosas para un subconjunto relativamente grande de casos de uso. Mejorar las cosas de tal manera que todos comiencen a usar su nuevo protocolo porque es mucho mejor que valga la pena el costo de cambiarlo es algo completamente diferente y no es fácil de hacer.
msouth
11
debería haber dejado el trabajo a los profesionales : si lo hubiera hecho, les habría llevado seis años llegar a un estándar que hubiera quedado obsoleto el día en que salió, y pronto habría aparecido una docena de estándares competitivos. Además, ¿los profesionales necesitaban permiso de alguien? ¿Por qué no lo hicieron ellos mismos?
Shantnu Tiwari
2
Para ser sincero, no hay profesionales calificados en ese entonces. Nadie sabe cómo construir una red mundial, porque nadie había construido una. El concepto de hipermedia no fue inventado por Tim, tenía experiencia con varios sistemas de hipermedia de área local diez años antes de escribir la propuesta de una "Gestión de la información" para resolver el problema de "perder información" en el CERN.
Lie Ryan
14

Su navegador web no conoce los recursos adicionales hasta que descarga la página web (HTML) del servidor, que contiene los enlaces a esos recursos.

Quizás se pregunte, ¿por qué el servidor no analiza su propio HTML y envía todos los recursos adicionales al navegador web durante la solicitud inicial de la página web? Esto se debe a que los recursos pueden estar distribuidos en varios servidores y es posible que el navegador web no necesite todos esos recursos, ya que algunos de ellos ya están almacenados en caché o pueden no ser compatibles.

El navegador web mantiene una memoria caché de recursos para que no tenga que descargar los mismos recursos una y otra vez desde los servidores que los alojan. Al navegar por diferentes páginas en un sitio web que usan la misma biblioteca jQuery, no desea descargar esa biblioteca cada vez, solo la primera vez.

Entonces, cuando el navegador web obtiene una página web del servidor, verifica qué recursos vinculados NO tiene en la caché, luego realiza solicitudes HTTP adicionales para esos recursos. Bastante simple, muy flexible y extensible.

Un navegador web generalmente puede realizar dos solicitudes HTTP en paralelo. Esto no es diferente a AJAX: ambos son métodos asincrónicos para cargar páginas web: carga asincrónica de archivos y carga de contenido asincrónico. Con keep-alive , podemos hacer varias solicitudes usando una conexión, y con la canalización podemos hacer varias solicitudes sin tener que esperar las respuestas. Ambas técnicas son muy rápidas porque la mayoría de los gastos generales generalmente provienen de abrir / cerrar conexiones TCP:

mantener viva

tubería

Un poco de historia web ...

Las páginas web comenzaron como correo electrónico de texto sin formato, con sistemas informáticos diseñados en torno a esta idea, formando una plataforma de comunicación algo libre para todos; los servidores web todavía eran propietarios en ese momento. Más tarde, se agregaron más capas a la "especificación de correo electrónico" en forma de tipos MIME adicionales, como imágenes, estilos, scripts, etc. Después de todo, MIME significa Extensión de correo de Internet multipropósito . Tarde o temprano tuvimos lo que es esencialmente comunicación de correo electrónico multimedia, servidores web estandarizados y páginas web.

HTTP requiere que los datos se transmitan en el contexto de mensajes tipo correo electrónico, aunque los datos con mayor frecuencia no son realmente correos electrónicos.

A medida que evoluciona una tecnología como esta, debe permitir a los desarrolladores incorporar progresivamente nuevas características sin romper el software existente. Por ejemplo, cuando se agrega un nuevo tipo MIME a la especificación, digamos JPEG, los servidores web y los navegadores web tardarán un tiempo en implementarlo. No solo fuerza repentinamente JPEG en la especificación y comienza a enviarlo a todos los navegadores web, sino que permite que el navegador web solicite los recursos que admite, lo que mantiene contentos a todos y la tecnología avanza. ¿Un lector de pantalla necesita todos los archivos JPEG en una página web? Probablemente no. ¿Debería verse obligado a descargar un montón de archivos Javascript si su dispositivo no es compatible con Javascript? Probablemente no. ¿Googlebot necesita descargar todos sus archivos Javascript para indexar su sitio correctamente? No.

Fuente: he desarrollado un servidor web basado en eventos como Node.js. Se llama Rapid Server .

Referencias

Otras lecturas:

sidra de pera
fuente
Bueno, en realidad, podemos ocuparnos de todos esos problemas secundarios (cosas como: caché, encabezado de tipo de contenido ... etc.), hay soluciones para resolver estos problemas. Y como sugerí en los comentarios en la publicación anterior, podemos usar algo como este encabezado> Recursos en caché: image.jpg; style.css; para resolver el problema de almacenamiento en caché ... (Si tienes tiempo, entonces puedes echar un vistazo a los comentarios anteriores ...)
Ahmed
Sí, esa idea se me había pasado por la cabeza antes, pero es simplemente demasiada sobrecarga para HTTP y no resuelve el hecho de que los recursos se pueden distribuir en varios servidores. Además, no creo que su método de ahorro de tiempo propuesto realmente ahorre tiempo porque los datos se enviarán como una transmisión sin importar cómo lo mire, y con Keep-Live, 100 solicitudes HTTP simultáneas se convierten esencialmente en 1 solicitud. La tecnología y la capacidad que propone parecen existir de alguna manera. Ver en.wikipedia.org/wiki/HTTP_persistent_connection
perry
@perry: ¿Qué pensaría de la idea de una alternativa https://para enviar grandes archivos distribuidos públicamente que necesitan ser autenticados pero no confidenciales: incluya en la URL un hash de ciertas partes del encabezado de una respuesta legítima, que a su vez podría incluye una firma o un hash de la carga útil de datos, y ¿los navegadores validan los datos recibidos contra el encabezado? Tal diseño no solo salvaría algunos pasos de protocolo de enlace SSL, sino que más importante aún permitiría el almacenamiento en caché de servidores proxy. Obtenga la URL a través de un enlace SSL, y los datos podrían alimentarse desde cualquier lugar.
supercat
11

Porque no saben cuáles son esos recursos. Los activos que requiere una página web están codificados en el HTML. Solo después de que un analizador determine cuáles son esos activos, el agente de usuario puede solicitarlo.

Además, una vez que se conocen esos activos, deben servirse individualmente para que se puedan servir los encabezados adecuados (es decir, el tipo de contenido) para que el agente de usuario sepa cómo manejarlos.

John Conde
fuente
2
Especialmente si usas algo como require.js. El navegador solo pide lo que necesita. Imagine tener que cargar todo de una vez ...
Aran Mulholland
2
Esta es la respuesta correcta, y una que la mayoría de los comentaristas parecen estar ausentes: para que el servidor envíe los recursos de forma proactiva, necesita saber cuáles son, lo que significa que el servidor tendría que analizar el HTML.
1
Pero la pregunta pregunta por qué el servidor web no envía los recursos, no por qué el cliente no puede solicitarlos al mismo tiempo. Es muy fácil imaginar un mundo en el que los servidores tengan un paquete de activos relacionados que se envíen todos juntos y que no dependa del análisis HTML para construir el paquete.
David Meister
@DavidMeister Debido a que el servidor no siempre sabe lo que quiere el cliente: un webcrawler para un motor de búsqueda podría no preocuparse por el CSS / JS, y hay muchos otros recursos vinculados en un documento más allá de esos, no hay necesidad de enviar todo La alimentación de RSS en el paquete a un navegador web (la mayor parte del contenido probablemente ya esté en el HTML), mientras que un lector de alimentación podría analizar el <head>elemento buscando los enlaces alternativos de RSS para encontrar exactamente eso: el cliente podría enviar una lista de lo que le interesa, pero luego necesita saber qué hay disponible y volvemos al principio
Zhaph - Ben Duguid
@ Zhaph-BenDuguid Estoy hablando de un mundo alternativo, para resaltar que la respuesta tiene tanto que ver con el funcionamiento del protocolo como con cualquier otra cosa. Además, puede ser más rápido que un servidor envíe todos los datos a la vez, incluso si es innecesario. Básicamente, está intercambiando problemas de latencia con el uso de ancho de banda.
David Meister
8

Porque, en su ejemplo, el servidor web siempre enviaría CSS e imágenes independientemente de si el cliente ya las tiene, lo que desperdiciará mucho el ancho de banda (y por lo tanto la conexión será más lenta , en lugar de hacerlo más rápido, reduciendo la latencia, que presumiblemente era su intención). Tenga en cuenta que los archivos CSS, JavaScript e imágenes generalmente se envían con tiempos de caducidad muy largos por exactamente ese motivo (ya que cuando necesita cambiarlos, simplemente cambia el nombre del archivo para forzar una nueva copia que nuevamente se almacenará en caché durante mucho tiempo).

Ahora, puede intentar evitar ese desperdicio de ancho de banda diciendo " OK, pero el cliente podría indicar que ya tiene algunos de esos recursos, por lo que el servidor no lo volvería a enviar ". Algo como:

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"

Y luego, solo los archivos que no han cambiado se envían a través de una conexión TCP (usando la canalización HTTP a través de una conexión persistente). ¿Y adivina qué? Es la forma en que ya funciona (también se puede utilizar If-Modified-Since en lugar de Si-None-Match ).


Pero si realmente desea reducir la latencia desperdiciando mucho ancho de banda (como en su solicitud original), puede hacerlo hoy usando HTTP / 1.1 estándar al diseñar su sitio web. La razón por la que la mayoría de las personas no lo hacen es porque no creen que valga la pena.

Para hacerlo, no necesita tener CSS o JavaScript en un archivo separado, puede incluirlos en el archivo HTML principal mediante el uso de <style>y <script>etiquetas (probablemente ni siquiera necesite hacerlo manualmente, su motor de plantillas probablemente pueda hacerlo automáticamente) . Incluso puede incluir imágenes en el archivo HTML utilizando URI de datos , como este:

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

Por supuesto, la codificación base64 aumenta ligeramente el uso del ancho de banda, pero si no le importa el ancho de banda desperdiciado, eso no debería ser un problema.

Ahora, si realmente le importaba, incluso podría hacer que sus scripts web fueran lo suficientemente inteligentes como para obtener lo mejor de ambos mundos: a primera solicitud (el usuario no tiene una cookie), envíe todo (CSS, JavaScript, imágenes) incrustado solo en un solo HTML archivo como se describió anteriormente, agregue un enlace rel = "prefetch" para copias externas de los archivos y agregue una cookie. Si el usuario ya tiene una cookie (por ejemplo, la ha visitado antes), envíele un HTML normal con <img src="example.jpg">, <link rel="stylesheet" type="text/css" href="style.css">etc.

Entonces, en la primera visita, el navegador solicitará solo un archivo HTML y obtendrá y mostrará todo. Entonces (cuando está inactivo) precargaría CSS, JS, imágenes externas especificadas. La próxima vez que el usuario visite, el navegador solicitará y solo obtendrá recursos modificados (probablemente solo HTML nuevo).

Los datos adicionales de las imágenes CSS + JS + solo se enviarían dos veces, incluso si hace clic cientos de veces en el sitio web. Mucho mejor que cientos de veces como sugirió su solución propuesta. Y nunca (ni la primera vez ni las próximas veces) usaría más de un viaje de ida y vuelta que aumenta la latencia.

Ahora, si eso suena como demasiado trabajo, y no desea usar otro protocolo como SPDY , ya hay módulos como mod_pagespeed para Apache, que pueden hacer automáticamente algo de ese trabajo por usted (fusionando múltiples archivos CSS / JS en uno, alineando automáticamente un CSS pequeño y minimizándolos, haga pequeñas imágenes en línea de marcador de posición mientras espera que se carguen los originales, cargue imágenes diferidas, etc.) sin requerir que modifique una sola línea de su página web.

Matija Nalis
fuente
3
Creo que esta es la respuesta correcta.
el.pescado
7

HTTP2 se basa en SPDY y hace exactamente lo que sugiere:

En un nivel alto, HTTP / 2:

  • es binario, en lugar de textual
  • está completamente multiplexado, en lugar de ordenado y bloqueado
  • por lo tanto, puede usar una conexión para paralelismo
  • usa compresión de encabezado para reducir los gastos generales
  • permite a los servidores "empujar" respuestas de forma proactiva en cachés de clientes

Más información está disponible en HTTP 2 Faq

Mabu
fuente
3

Porque no supone que estas cosas sean realmente necesarias .

El protocolo no define ningún manejo especial para ningún tipo particular de archivo o agente de usuario. No conoce la diferencia entre, digamos, un archivo HTML y una imagen PNG. Para hacer lo que está pidiendo, el servidor web tendría que identificar el tipo de archivo, analizarlo para averiguar a qué otros archivos hace referencia, y luego determinar qué otros archivos son realmente necesarios, dado lo que pretende hacer con el archivo . Hay tres grandes problemas con esto.

El primer problema es que no existe una forma estándar y sólida de identificar los tipos de archivos en el servidor . HTTP se gestiona a través del mecanismo de tipo de contenido, pero eso no ayuda al servidor, que tiene que resolver esto por sí mismo (en parte para que sepa qué poner en el tipo de contenido). Las extensiones de nombre de archivo son ampliamente compatibles, pero frágiles y fáciles de engañar, a veces con fines maliciosos. Los metadatos del sistema de archivos son menos frágiles, pero la mayoría de los sistemas no lo admiten muy bien, por lo que los servidores ni siquiera se molestan. El rastreo de contenido (como algunos navegadores y el filecomando Unix intentan hacer) puede ser robusto si está dispuesto a hacerlo costoso, pero el rastreo robusto es demasiado costoso para ser práctico en el lado del servidor, y el rastreo barato no es lo suficientemente robusto.

El segundo problema es que analizar un archivo es costoso, computacionalmente hablando . Esto se relaciona con el primero de alguna manera, ya que necesitaría analizar el archivo de muchas maneras diferentes si quisiera rastrear el contenido de manera sólida, pero también se aplica después de haber identificado el tipo de archivo, porque necesita para averiguar cuáles son las referencias. Esto no es tan malo cuando solo está haciendo unos pocos archivos a la vez, como lo hace el navegador, pero un servidor web tiene que manejar cientos o miles de solicitudes a la vez. Esto se suma, y ​​si va demasiado lejos, en realidad puede ralentizar las cosas más de lo que lo harían múltiples solicitudes. Si alguna vez ha visitado un enlace de Slashdot o sitios similares, solo para descubrir que el servidor es extremadamente lento debido al alto uso, ha visto este principio en acción.

El tercer problema es que el servidor no tiene forma de saber qué piensa hacer con el archivo . Un navegador puede necesitar que se haga referencia a los archivos en el HTML, pero puede que no, según el contexto exacto en el que se ejecute el archivo. Eso sería lo suficientemente complejo, pero hay más en la Web que solo navegadores: entre las arañas, los agregadores de feeds y los mashups de raspado de página, hay muchos tipos de agentes de usuario que no necesitan los archivos a los que se hace referencia en el HTML : solo se preocupan por el HTML en sí. Enviar estos otros archivos a dichos agentes de usuario solo desperdiciaría el ancho de banda.

La conclusión es que descubrir estas dependencias en el lado del servidor es más problemático de lo que vale . Entonces, en cambio, dejan que el cliente descubra lo que necesita.

El más cuchara
fuente
Si vamos a desarrollar un nuevo protocolo o corregir uno ya existente, ¡podemos encargarnos de todos estos problemas de una forma u otra! Y el servidor web analizará los archivos solo una vez y luego puede clasificarlos según las reglas definidas para que pueda priorizar qué archivos enviar primero ... etc. y el servidor web no tiene que saber lo que estoy destinado a hacer con esos archivos, solo tiene que saber qué enviar, cuándo hacerlo y dependiendo de qué reglas ... (los robots web y las arañas no son un problema, el comportamiento será diferente con ellos -tienen encabezados únicos de agente de usuario- ..)
Ahmed
@AhmedElsobky: lo que estás hablando parece más una implementación específica que un protocolo de red. Pero realmente tiene que saber qué piensa hacer con los archivos antes de poder determinar qué enviar: de lo contrario, inevitablemente enviará archivos que muchos usuarios no desean. No puede confiar en las cadenas de User-Agent, por lo que no puede usarlas para determinar cuál es la intención del usuario.
The Spooniest