Quando os servidores da web enviam uma página, por que eles não enviam todos os CSS, JS e imagens necessários sem serem solicitados?

45

Quando uma página da Web contém um único arquivo CSS e uma imagem, por que navegadores e servidores perdem tempo com esta rota tradicional que consome tempo:

  1. O navegador envia uma solicitação GET inicial para a página da Web e aguarda a resposta do servidor.
  2. O navegador envia outra solicitação GET para o arquivo css e aguarda a resposta do servidor.
  3. O navegador envia outra solicitação GET para o arquivo de imagem e aguarda a resposta do servidor.

Quando, em vez disso, eles poderiam usar essa rota curta, direta e economizadora de tempo?

  1. O navegador envia uma solicitação GET para uma página da web.
  2. O servidor da web responde com ( index.html seguido por style.css e image.jpg )
Ahmed
fonte
2
Qualquer solicitação não pode ser feita até que a página da Web seja buscada, é claro. Depois disso, os pedidos são feitos em ordem à medida que o HTML é lido. Mas isso não significa que apenas uma solicitação é feita por vez. De fato, várias solicitações são feitas, mas às vezes existem dependências entre solicitações e algumas precisam ser resolvidas antes que a página possa ser pintada corretamente. Às vezes, os navegadores pausam quando uma solicitação é satisfeita antes de parecer manipular outras respostas, fazendo parecer que cada solicitação é tratada uma por vez. A realidade está mais no lado do navegador, pois eles tendem a consumir muitos recursos.
closetnoc
20
Estou surpreso que ninguém tenha mencionado o cache. Se eu já tenho esse arquivo, não preciso que ele seja enviado para mim.
Corey Ogburn
2
Essa lista pode ter centenas de coisas. Embora seja mais curto do que realmente envia os arquivos, ainda está longe de ser uma solução ideal.
quer
1
Na verdade, eu nunca visitou uma página web que tem mais de 100 recursos únicos ..
Ahmed
2
@AhmedElsoobky: o navegador não sabe quais recursos podem ser enviados como cabeçalho de recursos em cache sem primeiro recuperar a própria página. Também seria um pesadelo de privacidade e segurança se a recuperação de uma página disser ao servidor que eu tenho outra página em cache, que é possivelmente controlada por uma organização diferente da página original (um site com vários inquilinos).
Lie Ryan

Respostas:

63

A resposta curta é "Porque o HTTP não foi projetado para isso".

Tim Berners-Lee não projetou um protocolo de rede eficiente e extensível. Seu único objetivo de design era a simplicidade. (O professor da minha turma de redes na faculdade disse que deveria ter deixado o trabalho para os profissionais.) O problema que você descreve é ​​apenas um dos muitos problemas com o protocolo HTTP. Na sua forma original:

  • Não havia versão de protocolo, apenas uma solicitação de recurso
  • Não havia cabeçalhos
  • Cada solicitação exigia uma nova conexão TCP
  • Não houve compressão

O protocolo foi posteriormente revisado para resolver muitos desses problemas:

  • Os pedidos foram versionados, agora os pedidos parecem GET /foo.html HTTP/1.1
  • Foram adicionados cabeçalhos para informações meta, com solicitação e resposta
  • Foi permitido reutilizar as conexões com Connection: keep-alive
  • As respostas em blocos foram introduzidas para permitir a reutilização das conexões, mesmo quando o tamanho do documento não é conhecido antecipadamente.
  • Compressão Gzip foi adicionada

Nesse ponto, o HTTP foi levado o mais longe possível, sem quebrar a compatibilidade com versões anteriores.

Você não é a primeira pessoa a sugerir que uma página e todos os seus recursos sejam enviados ao cliente. De fato, o Google criou um protocolo que pode ser chamado de SPDY .

Hoje, o Chrome e o Firefox podem usar o SPDY em vez do HTTP para os servidores que o suportam. No site do SPDY, seus principais recursos em comparação ao HTTP são:

  • O SPDY permite que o cliente e o servidor compactem cabeçalhos de solicitação e resposta, o que reduz o uso da largura de banda quando os cabeçalhos semelhantes (por exemplo, cookies) são enviados repetidamente para várias solicitações.
  • O SPDY permite várias solicitações simultaneamente multiplexadas em uma única conexão, economizando em viagens de ida e volta entre cliente e servidor e impedindo que recursos de baixa prioridade bloqueiem solicitações de alta prioridade.
  • O SPDY permite que o servidor envie ativamente recursos ao cliente que ele sabe que ele precisará (por exemplo, arquivos JavaScript e CSS) sem esperar que o cliente os solicite, permitindo que o servidor faça uso eficiente da largura de banda não utilizada.

Se você deseja veicular seu site com SPDY em navegadores compatíveis, é possível fazê-lo. Por exemplo, o Apache possui mod_spdy .

O SPDY se tornou a base do HTTP versão 2 com a tecnologia push do servidor.

Stephen Ostermiller
fonte
2
Dang resposta boa e informada! Os navegadores da Web são seriais por natureza e as solicitações podem ser feitas rapidamente. Uma olhada em um arquivo de log mostrará que as solicitações de recursos são feitas rapidamente, uma vez que o HTML foi analisado. É o que é. Não é um sistema ruim, apenas não é tão eficiente quanto o código / recurso.
closetnoc
6
Apenas para constar, o SPDY não é o Santo Graal. Faz algumas coisas bem, mas apresenta outros problemas. Aqui está um artigo que contém alguns pontos sobre o SPDY.
Jost
3
Eu recomendo que qualquer pessoa interessada nisso leia as críticas no link do @Jost. Ele fornece uma dica da complexidade envolvida em descobrir como fazer uma coisa muito comumente implementada, não apenas incrementalmente melhor, mas tão melhor que todos começam a usá-lo . É fácil pensar em uma melhoria que torne as coisas um pouco melhores para um subconjunto relativamente grande de casos de uso. Melhorar as coisas de tal maneira que todo mundo comece a usar seu novo protocolo, porque é muito melhor que valha a pena o custo da mudança é outra questão inteiramente, e não é fácil de fazer.
msouth
11
ele deveria ter deixado o trabalho para os profissionais : se ele tivesse feito isso, levariam seis anos para criar um padrão que ficaria obsoleto no dia em que saiu, e logo uma dúzia de padrões concorrentes apareceria. Além disso, os profissionais precisavam da permissão de alguém? Por que eles não fizeram isso sozinhos?
Shantnu Tiwari
2
Para ser franco, não há profissionais qualificados na época. Ninguém sabe como construir uma rede mundial de computadores, porque ninguém jamais construiu uma. O conceito de hipermídia não foi inventado por Tim, ele tinha experiência com vários sistemas de hipermídia local dez anos antes de escrever a proposta de um "Gerenciamento de Informações" para resolver o problema de "perda de informações" no CERN.
Lie Ryan
14

Seu navegador da Web não conhece os recursos adicionais até fazer o download da página da Web (HTML) do servidor, que contém os links para esses recursos.

Você pode estar se perguntando: por que o servidor não apenas analisa seu próprio HTML e envia todos os recursos adicionais para o navegador durante a solicitação inicial da página? Isso ocorre porque os recursos podem estar espalhados por vários servidores e o navegador da Web pode não precisar de todos esses recursos, pois já possui alguns deles em cache ou pode não os suportar.

O navegador da Web mantém um cache de recursos para não precisar baixar os mesmos recursos repetidamente dos servidores que os hospedam. Ao navegar em páginas diferentes em um site que usam a mesma biblioteca jQuery, você não deseja fazer o download dessa biblioteca todas as vezes, apenas na primeira vez.

Portanto, quando o navegador obtém uma página da Web do servidor, verifica quais recursos vinculados ele ainda não possui no cache e faz solicitações HTTP adicionais para esses recursos. Muito simples, muito flexível e extensível.

Um navegador da web geralmente pode fazer duas solicitações HTTP em paralelo. Isso não é diferente do AJAX - ambos são métodos assíncronos para carregar páginas da Web - carregamento assíncrono de arquivos e carregamento assíncrono de conteúdo. Com o keep-alive , podemos fazer várias solicitações usando uma conexão e, com o pipelining , podemos fazer várias solicitações sem ter que esperar pelas respostas. Ambas as técnicas são muito rápidas, porque a maioria das despesas gerais geralmente vem da abertura / fechamento de conexões TCP:

mantenha vivo

tubulação

Um pouco da história da web ...

As páginas da Web começaram como e-mail em texto sem formatação, com os sistemas de computador sendo projetados em torno dessa idéia, formando uma plataforma de comunicação um tanto livre para todos; servidores da web ainda eram proprietários na época. Posteriormente, mais camadas foram adicionadas à "especificação de e-mail" na forma de tipos MIME adicionais, como imagens, estilos, scripts etc. Afinal, MIME significa Extensão de E- mail da Internet com Propósitos Múltiplos . Cedo ou tarde, tivemos o que é essencialmente comunicação por e-mail multimídia, servidores web padronizados e páginas da web.

O HTTP exige que os dados sejam transmitidos no contexto de mensagens semelhantes a email, embora na maioria das vezes os dados não sejam realmente email.

À medida que a tecnologia evolui, ela precisa permitir que os desenvolvedores incorporem progressivamente novos recursos sem interromper o software existente. Por exemplo, quando um novo tipo MIME é adicionado à especificação - digamos JPEG - levará algum tempo para os servidores e navegadores da Web implementarem isso. Você não apenas força o JPEG de repente nas especificações e começa a enviá-lo para todos os navegadores da Web, mas permite que o navegador solicite os recursos que ele suporta, o que mantém todos felizes e a tecnologia avançando. Um leitor de tela precisa de todos os JPEGs em uma página da web? Provavelmente não. Você deve ser obrigado a baixar um monte de arquivos Javascript se o seu dispositivo não suportar Javascript? Provavelmente não. O Googlebot precisa baixar todos os seus arquivos Javascript para indexar seu site corretamente? Não.

Fonte: Desenvolvi um servidor Web baseado em eventos como o Node.js. Chama-se Rapid Server .

Referências:

Leitura adicional:

perada
fonte
Bem, na verdade, podemos cuidar de todos esses problemas colaterais (coisas como: cache, cabeçalho do tipo de conteúdo ... etc), existem soluções alternativas para resolver esses problemas. E como sugeri nos comentários no post acima, podemos usar algo como este cabeçalho> Recursos em cache: image.jpg; style.css; para resolver o problema de cache .. (Se você tem tempo, então você pode dar uma olhada nos comentários acima ..)
Ahmed
Sim, essa ideia já passou pela minha cabeça antes, mas é simplesmente uma sobrecarga demais para HTTP e não resolve o fato de que os recursos podem estar espalhados por vários servidores. Além disso, não acho que seu método de economia de tempo proposto economize tempo, porque os dados serão enviados como um fluxo, não importa como você os veja, e com o keep-alive, 100 solicitações HTTP simultâneas se tornam essencialmente uma solicitação. A tecnologia e a capacidade que você propõe parecem já existir de certa forma. Veja en.wikipedia.org/wiki/HTTP_persistent_connection
perry
@perry: O que você acha da idéia de uma alternativa https://para o envio de grandes arquivos distribuídos publicamente que precisam ser autenticados, mas não mantidos em sigilo: inclua no URL um hash de certas partes do cabeçalho de uma resposta legítima, que por sua vez poderia incluir uma assinatura ou um hash da carga útil dos dados e os navegadores validam os dados recebidos no cabeçalho? Esse design não apenas salvaria algumas etapas do handshake SSL, mas permitiria, o mais importante, o proxy em cache. Obtenha o URL por um link SSL, e os dados podem ser alimentados de qualquer lugar.
Supercat
11

Porque eles não sabem quais são esses recursos. Os ativos exigidos por uma página da web são codificados no HTML. Somente depois que um analisador determinar quais são esses ativos, eles poderão ser solicitados pelo agente do usuário.

Além disso, uma vez que esses ativos são conhecidos, eles precisam ser veiculados individualmente para que os cabeçalhos adequados (por exemplo, tipo de conteúdo) possam ser veiculados para que o agente usuário saiba como lidar com isso.

John Conde
fonte
2
Especialmente se você usar algo como require.js. O navegador apenas pede o que precisa. Imagine ter de carregar tudo de uma vez ...
Aran Mulholland
2
Essa é a resposta certa e que a maioria dos comentaristas parece estar ausente - para que o servidor envie os recursos de maneira proativa, ele precisa saber o que são, o que significa que o servidor precisaria analisar o HTML.
1
Mas a pergunta é por que o servidor da Web não envia os recursos, não por que o cliente não pode solicitá-los ao mesmo tempo. É muito fácil imaginar um mundo em que os servidores tenham um pacote de ativos relacionados, todos enviados juntos, que não dependem da análise de HTML para compilar o pacote.
David Meister
@DavidMeister Como o servidor nem sempre sabe o que o cliente deseja - um webcrawler para um mecanismo de pesquisa pode não se importar com o CSS / JS, e existem muitos outros recursos vinculados em um documento além desses - não há necessidade de enviar todo O feed de RSS é baixado no pacote para um navegador da Web (a maioria do conteúdo provavelmente já está no HTML), enquanto um leitor de feeds pode apenas analisar o <head>elemento procurando os links alternativos do RSS para encontrar exatamente isso - o cliente pode enviar uma lista de no que está interessado, mas é preciso saber o que está disponível e estamos de volta no início.
Zhaph - Ben Duguid 19/03/2015
@ Zhaph-BenDuguid Estou falando de um mundo alternativo, para destacar que a resposta tem tanto a ver com a forma como o protocolo funciona quanto qualquer outra coisa. Além disso, pode ser mais rápido para um servidor enviar todos os dados de uma só vez, mesmo que desnecessários. Você está basicamente trocando problemas de latência pelo uso da largura de banda.
David Meister
8

Como, no seu exemplo, o servidor da Web sempre enviava CSS e imagens, independentemente de o cliente já as ter, desperdiçando muito a largura de banda (e, assim, tornando a conexão mais lenta , em vez de mais rápida, reduzindo a latência, que era provavelmente sua intenção). Observe que os arquivos CSS, JavaScript e de imagem geralmente são enviados com tempos de expiração muito longos exatamente por esse motivo (como quando você precisa alterá-los, basta alterar o nome do arquivo para forçar uma nova cópia, que será novamente armazenada em cache por um longo tempo).

Agora, você pode tentar solucionar esse desperdício de largura de banda dizendo " OK, mas o cliente pode indicar que ele já possui alguns desses recursos, para que o servidor não o envie novamente ". 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"

E, em seguida, apenas os arquivos que não foram alterados são enviados por uma conexão TCP (usando pipelining HTTP por conexão persistente). E adivinha? É assim que ele funciona (você também pode usar If-Modified-Since em vez de If-None-Match ).


Mas se você realmente deseja reduzir a latência, desperdiçando muita largura de banda (como em sua solicitação original), pode fazer isso hoje usando o HTTP / 1.1 padrão ao criar seu site. A razão pela qual a maioria das pessoas não faz isso é porque não acha que vale a pena.

Para fazer isso, você não precisa ter CSS ou JavaScript em um arquivo separado, pode incluí-los no arquivo HTML principal usando tags <style>e <script>(você provavelmente nem precisa fazê-lo manualmente, o mecanismo de modelos provavelmente o faz automaticamente) . Você pode até incluir imagens no arquivo HTML usando o URI de dados , assim:

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

Obviamente, a codificação base64 aumenta um pouco o uso da largura de banda, mas se você não se importa com a largura de banda desperdiçada, isso não deve ser um problema.

Agora, se você realmente se importa, pode até criar scripts da web inteligentes o suficiente para obter o melhor dos dois mundos: na primeira solicitação (o usuário não possui um cookie), envie tudo (CSS, JavaScript, imagens) incorporados em apenas um único HTML como descrito acima, adicione um link rel = "prefetch" para cópias externas dos arquivos e adicione um cookie. Se o usuário já tem um cookie (ex. Que visitou antes), em seguida, enviar-lhe apenas um HTML normal com <img src="example.jpg">, <link rel="stylesheet" type="text/css" href="style.css">etc.

Portanto, na primeira visita, o navegador solicita apenas um único arquivo HTML e obtém e mostra tudo. Em seguida, ele (quando ocioso) pré-carregava imagens CSS, JS externas especificadas. Na próxima vez que o usuário visitar, o navegador solicitará e obterá apenas recursos alterados (provavelmente apenas um novo HTML).

Os dados extras das imagens CSS + JS + seriam enviados apenas duas vezes, mesmo se você clicar centenas de vezes no site. Muito melhor do que centenas de vezes, conforme sugerido pela solução proposta. E nunca (nem na primeira vez nem nas próximas) usaria mais de uma viagem de ida e volta com aumento de latência.

Agora, se isso parecer muito trabalhoso, e você não quiser seguir outro protocolo como o SPDY , já existem módulos como mod_pagespeed para Apache, que podem fazer parte desse trabalho automaticamente para você (mesclando vários arquivos CSS / JS em um, autoinlinando CSS pequeno e minimizando-o, crie pequenas imagens embutidas de espaço reservado enquanto aguarda o carregamento de originais, carregamento lento de imagens etc.) sem exigir que você modifique uma única linha da sua página da web.

Matija Nalis
fonte
3
Eu acho que essa é a resposta correta.
el.pescado
7

O HTTP2 é baseado no SPDY e faz exatamente o que você sugere:

Em um nível alto, HTTP / 2:

  • é binário, em vez de textual
  • é totalmente multiplexado, em vez de ordenado e bloqueado
  • portanto, pode usar uma conexão para paralelismo
  • usa compactação de cabeçalho para reduzir a sobrecarga
  • permite que os servidores “enviem” respostas proativamente para os caches do cliente

Mais informações sobre o HTTP 2 Faq

MaBu
fonte
3

Porque não assume que essas coisas sejam realmente necessárias .

O protocolo não define nenhum tratamento especial para nenhum tipo específico de arquivo ou agente de usuário. Ele não sabe a diferença entre, digamos, um arquivo HTML e uma imagem PNG. Para fazer o que você está pedindo, o servidor da Web precisa identificar o tipo de arquivo, analisá-lo para descobrir quais outros arquivos estão referenciando e determinar quais outros arquivos são realmente necessários, considerando o que você pretende fazer com o arquivo . Existem três grandes problemas com isso.

O primeiro problema é que não há uma maneira padrão e robusta de identificar os tipos de arquivo no servidor . O HTTP gerencia por meio do mecanismo Content-Type, mas isso não ajuda o servidor, que precisa descobrir isso sozinho (parcialmente para que ele saiba o que colocar no Content-Type). As extensões de nome de arquivo são amplamente suportadas, mas frágeis e fáceis de enganar, às vezes para fins maliciosos. Os metadados do sistema de arquivos são menos frágeis, mas a maioria dos sistemas não os suporta muito bem, portanto os servidores nem se incomodam. O sniffing de conteúdo (como alguns navegadores e o filecomando Unix tentam fazer) pode ser robusto, se você quiser torná-lo caro, mas o sniffing robusto é muito caro para ser prático no lado do servidor, e o sniffing barato não é robusto o suficiente.

O segundo problema é que analisar um arquivo é caro, falando em termos computacionais . Isso se encaixa no primeiro, porque você precisa analisar o arquivo de várias maneiras possíveis, se quiser farejar o conteúdo com robustez, mas isso também se aplica depois de identificar o tipo de arquivo, porque você precisa para descobrir quais são as referências. Isso não é tão ruim quando você está apenas executando alguns arquivos por vez, como o navegador, mas um servidor da Web precisa lidar com centenas ou milhares de solicitações ao mesmo tempo. Isso aumenta e, se for longe demais, pode realmente atrasar as coisas mais do que várias solicitações. Se você já visitou um link do Slashdot ou de sites semelhantes, apenas para descobrir que o servidor é extremamente lento devido ao alto uso, você já viu esse princípio em ação.

O terceiro problema é que o servidor não tem maneira de saber o que você pretende fazer com o arquivo . Um navegador pode precisar que os arquivos sejam referenciados no HTML, mas talvez não, dependendo do contexto exato em que o arquivo está sendo executado. Isso seria complexo o suficiente, mas há mais na Web do que apenas navegadores: entre aranhas, agregadores de feed e mashups de captura de página, existem muitos tipos de user-agents que não precisam dos arquivos referenciados no HTML : eles só se preocupam com o próprio HTML. Enviar esses outros arquivos para esses user-agents apenas desperdiçaria largura de banda.

O ponto principal é que descobrir essas dependências no lado do servidor é mais problemático do que vale a pena . Então, em vez disso, eles deixam o cliente descobrir o que ele precisa.

The Spooniest
fonte
Se vamos desenvolver um novo protocolo ou corrigir um já existente, podemos resolver todos esses problemas de uma maneira ou de outra! E o servidor da Web analisará os arquivos por apenas uma vez e, em seguida, poderá classificá-los, dependendo das regras definidas, para que possa priorizar os arquivos a serem enviados primeiro ... etc, e o servidor da Web não precisará saber o que devo fazer com esses arquivos, basta saber o que enviar, quando fazer e dependendo de quais regras .. (bots e spiders da web não são um problema, o comportamento será diferente com eles - eles têm cabeçalhos de agente de usuário exclusivos - ..)
Ahmed
@ AhmedElsobky: O que você está falando parece mais uma implementação específica do que um protocolo de rede. Mas ele realmente precisa saber o que você pretende fazer com os arquivos antes que possa determinar o que enviar: caso contrário, inevitavelmente enviará arquivos que muitos usuários não desejam. Você não pode confiar nas seqüências User-Agent, portanto não pode usá-las para determinar qual é a intenção do usuário.
The Spooniest