BLOG | NGINX

Apresentando HTTP/2 Server Push com NGINX 1.13.9

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Miniatura de Owen Garrett
Owen Garrett
Publicado em 20 de fevereiro de 2018

O suporte para envio de servidor HTTP/2 também está incluído no NGINX Plus R15 .

Temos o prazer de anunciar que o NGINX 1.13.9 , lançado em 20 de fevereiro de 2018 , inclui suporte para envio de servidor HTTP/2. Para usuários do NGINX Plus, o suporte a push do servidor HTTP/2 será incluído na próxima versão do NGINX Plus R15 , programada para abril de 2018.

O push do servidor, definido na especificação HTTP/2 , permite que um servidor envie recursos preventivamente para um cliente remoto, antecipando que o cliente poderá solicitar esses recursos em breve. Ao fazer isso, você pode reduzir potencialmente o número de RTTs (tempo de ida e volta – o tempo necessário para uma solicitação e resposta) em uma operação de carregamento de página em um RTT ou mais, fornecendo uma resposta mais rápida ao usuário.

O push do servidor pode ser usado para preparar um cliente com folhas de estilo, imagens e outros recursos necessários para renderizar uma página da web. Você deve tomar cuidado para enviar apenas os recursos necessários; não envie recursos que um cliente provavelmente já tenha armazenado em cache.

Nesta postagem do blog, descrevo:

Configurando o HTTP/2 Server Push

Para enviar recursos junto com o carregamento de uma página, use a diretiva http2_push da seguinte maneira:

server { # Certifique-se de que o HTTP/2 esteja habilitado para o servidor listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # sempre que um cliente solicitar demo.html, também envie # /style.css, /image1.jpg e /image2.jpg location = /demo.html { http2_push /style.css; http2_push /image1.jpg; http2_push /image2.jpg; } }

Verificando o envio do servidor HTTP/2

Você pode verificar facilmente se o push do servidor está em vigor usando um dos dois métodos:

  • As ferramentas do desenvolvedor no seu navegador da web
  • Um cliente HTTP/2 de linha de comando, como nghttp

Verificando com Ferramentas do Desenvolvedor (Google Chrome)

Veja como usar as ferramentas do desenvolvedor no seu navegador para verificar se o envio do servidor está em vigor, usando o Google Chrome como exemplo. Na figura, a coluna Iniciador na guia Rede das Ferramentas do desenvolvedor do Chrome indica que vários recursos foram enviados ao cliente como parte de uma solicitação para /demo.html .

A coluna Iniciador indica que o push do servidor foi usado para enviar recursos

Verificando com um cliente de linha de comando ( nghttp )

Além das ferramentas do navegador da Web, você pode usar o cliente de linha de comando nghttp do projeto nghttp2.org para verificar se o push do servidor está em vigor. Você pode baixar o cliente de linha de comando nghttp do GitHub ou instalar o pacote de sistema operacional apropriado, quando disponível. Para o Ubuntu, use o pacote nghttp2-client .

Na saída, o asterisco (*) marca os recursos que foram enviados pelo servidor.

$ nghttp -ans https://example.com/demo.html id responseEnd requestStart processo código tamanho caminho da solicitação 13 +84,25 ms +136 us 84,11 ms 200 492 /demo.html 2 +84,33 ms * +84,09 ms 246 us 200 266 /style.css 4 +261,94 ms * +84,12 ms 177,83 ms 200 40 K /image2.jpg 6 +685,95 ms * +84,12 ms 601,82 ms 200 173 K /image1.jpg

Enviando recursos automaticamente para clientes

Em muitas situações, é inconveniente – ou até mesmo impossível – listar os recursos que você deseja enviar no arquivo de configuração do NGINX. Por esse motivo, o NGINX também oferece suporte à convenção de interceptar cabeçalhos de pré-carregamento de Link e, em seguida, enviar os recursos identificados nesses cabeçalhos. Para habilitar o pré-carregamento, inclua a diretiva http2_push_preload na configuração:

server { # Certifique-se de que o HTTP/2 esteja habilitado para o servidor listen 443 ssl http2 ; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Intercepte o cabeçalho do link e inicie os pushes solicitados location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }

Por exemplo, quando o NGINX está operando como um proxy (para HTTP, FastCGI ou outros tipos de tráfego), o servidor upstream pode adicionar um cabeçalho Link como este à sua resposta:

Link: </style.css>; como=estilo; rel=preload

O NGINX intercepta esse cabeçalho e inicia um push do servidor de /style.css . O caminho no cabeçalho Link deve ser absoluto – caminhos relativos como ./style.css não são suportados. O caminho pode opcionalmente incluir uma string de consulta.

Para enviar vários objetos, você pode fornecer vários cabeçalhos Link ou, melhor ainda, incluir todos os objetos em uma lista separada por vírgulas:

Link: </style.css>; como=estilo; rel=pré-carregamento, </favicon.ico>; como=imagem; rel=pré-carregamento

Se você não quiser que o NGINX envie um recurso pré-carregado, adicione o parâmetro nopush ao cabeçalho:

# O recurso não é enviadoLink: </nginx.png>; as=image; rel=preload; nopush

Quando http2_push_preload estiver habilitado, você também poderá iniciar o push do servidor de pré-carregamento definindo o cabeçalho de resposta na sua configuração do NGINX:

add_header Link "</style.css>; como=estilo; rel=preload";

Empurrando Recursos Seletivamente para os Clientes

A especificação HTTP/2 não aborda o desafio de determinar se deve ou não enviar recursos. Claramente, é melhor enviar recursos somente aos clientes se você souber que eles provavelmente precisarão do recurso e que é improvável que já o tenham armazenado em cache.

Uma abordagem possível é enviar recursos aos clientes somente na primeira visita ao site. Você pode testar a presença de um cookie de sessão, por exemplo, e definir o cabeçalho Link condicionalmente, para que os recursos sejam pré-carregados somente se o cookie de sessão não estiver presente.

Supondo que os clientes se comportem bem e incluam o cookie em solicitações subsequentes, com a seguinte configuração, o NGINX envia os recursos aos clientes apenas uma vez por sessão do navegador:

servidor {
ouvir 443 ssl http2 default_server;

certificado_ssl ssl/certificado.pem;
chave_certificado_ssl ssl/chave.pem;

raiz /var/www/html;
http2_push_preload em;

localização = /demo.html {
adicionar_cabeçalho Definir-Cookie "sessão=1";
adicionar_cabeçalho Link $recursos;
}
}

mapa $http_cookie $recursos {
"~*sessão=1" "";
padrão "</style.css>; como=estilo; rel=pré-carregamento, </image1.jpg>; como=imagem; rel=pré-carregamento, </image2.jpg>; como=imagem; rel=pré-carregamento";
}

Medindo o efeito do push do servidor HTTP/2

Para medir o efeito do push do servidor, criamos uma página de teste simples, /demo.html , que faz referência a uma folha de estilo separada, /style.css . A folha de estilo também faz referência a duas imagens. Testamos os tempos de carregamento de página usando três configurações diferentes:

  • GETs sequenciais (sem otimização) – O navegador carregou recursos quando descobriu que eram necessários
  • Dicas de pré-carregamento – Dicas de pré-carregamento (cabeçalhos de link ) foram incluídas na primeira resposta para informar ao navegador para carregar as dependências
  • Server Push (somente HTTP/2) – As dependências foram enviadas preventivamente para o navegador
Três configurações foram testadas para medir o impacto do HTTP/2 com push do servidor

Fizemos vários testes de cada configuração usando HTTP, HTTPS ou HTTP/2. As duas primeiras configurações se aplicam a todos os três protocolos, e o servidor envia somente para HTTP/2.

O comportamento foi medido usando as ferramentas de desenvolvedor do Chrome. O comportamento mais comum de cada configuração foi avaliado e calculado, e os tempos foram correlacionados com o RTT do link (medido usando ping ) para ilustrar o efeito mecânico de cada método.

Os resultados dos testes mostram que muitas viagens de ida e volta foram realizadas com cada configuração

Algumas observações básicas

  • O DOM carregado é o momento de iniciar uma nova conexão e recuperar a página demo.html . Folha de estilo é o momento de recuperar o recurso CSS.
  • Estabelecer uma conexão via HTTP leva 1 RTT; para HTTPS e HTTP/2, leva 2 RTTs.
  • A carga útil dos recursos HTML e CSS é menor que o tamanho da unidade máxima de transmissão (MTU), portanto, uma operação GET é concluída em aproximadamente 1 RTT.

Interpretando os resultados: Dicas de pré-carregamento

  • As dicas de pré-carregamento têm efeito mínimo para o recurso CSS porque são referenciadas diretamente no recurso HTML, e o recurso HTML é entregue rapidamente. O navegador inicia a solicitação CSS assim que a página HTML é entregue.
  • As dicas de pré-carregamento têm o efeito de iniciar rapidamente o download de recursos declarados no recurso CSS (aqui, as duas imagens). Os downloads podem começar 1 RTT mais rápido quando dicas de pré-carregamento são usadas.
  • A economia líquida das dicas de pré-carregamento pode ser de 1 RTT ou mais. Ao baixar recursos em paralelo, o navegador deve abrir uma ou mais conexões adicionais. O desempenho depende se as solicitações lentas (respostas maiores) são agendadas primeiro ou são atrasadas enquanto novas conexões são abertas. Essa ordem imprevisível de solicitações é responsável pela aceleração de 1 RTT para HTTP e HTTP/2, e pela aceleração de 2 RTT para HTTPS.

Interpretando os resultados: Empurrão do servidor

  • O push do servidor melhorou o tempo de dicas de pré-carregamento em mais 1 RTT. As “respostas” push foram iniciadas ao mesmo tempo que a resposta à primeira solicitação, enquanto as respostas de dicas de pré-carregamento incorreram em 1 atraso RTT – 0,5 RTT para a resposta à primeira solicitação mais 0,5 RTT para a solicitação GET de pré-carregamento.

Notas de teste

  • Houve vários testes para cada configuração. Cada execução começava com o cache do navegador vazio e sem conexões keepalive estabelecidas com o servidor NGINX. As diretivas NGINX keepalive_timeout e http2_idle_timeout foram usadas para fechar rapidamente conexões keepalive.
  • Atualmente, não parece possível enviar recursos de fonte para o Chrome, talvez devido a uma complicação conhecida . O Chrome faz uma solicitação explícita de um recurso de fonte, mesmo que ele já tenha sido enviado.
  • Foi tomado cuidado para limpar explicitamente os caches do navegador antes de cada teste, e todo o conteúdo foi exibido com cabeçalhos de controle de cache expirados.
  • O Chrome armazena em cache recursos pré-carregados. Esses recursos armazenados em cache não são ignorados consistentemente quando você desabilita o cache e não são limpos consistentemente por uma operação explícita de “limpar cache do navegador”. (No Chrome, uma maneira de desabilitar o cache é marcar a caixa de seleção Desabilitar cache na guia Rede das Ferramentas do Desenvolvedor. Para limpar o cache do navegador, você pode clicar com o botão direito do mouse no botão de atualização do navegador com as Ferramentas do desenvolvedor abertas e selecionar Esvaziar cache e recarregar ).
  • Algumas tentativas de pré-carregar conteúdo fizeram com que o Chrome revalidasse sem sucesso as cópias em cache e depois baixasse o recurso normalmente. Essas tentativas não foram contabilizadas nas medições.
  • O Chrome adiciona um atraso desnecessário de 2 RTT a todas as novas conexões SSL que usam certificados autoassinados aceitos anteriormente. O teste foi conduzido usando certificados Let’s Encrypt assinados por CA para evitar esse atraso 2-RTT.
  • Os atrasos de DNS foram resolvidos editando o arquivo local /etc/hosts .
  • Esses testes simples não tentaram medir o efeito do push do servidor quando o cliente já tem uma cópia em cache do recurso. Nos testes, todos os caches foram limpos antes de cada teste, e a maioria dos pushes foram cancelados muito rapidamente.

Conclusão

Este teste foi deliberadamente simples, a fim de destacar a mecânica das dicas de pré-carregamento e do envio do servidor. O push do servidor oferece uma melhoria de 1 RTT em relação às dicas de pré-carregamento em situações simples e uma melhoria maior em comparação a solicitações GET sequenciais não otimizadas e à descoberta de recursos dependentes.

Casos de uso mais realistas têm muito mais variáveis: múltiplos recursos dependentes, múltiplas fontes e até mesmo a possibilidade de desperdício de largura de banda ao enviar recursos que já estão armazenados em cache ou não são necessários imediatamente. Inconsistências no navegador também afetam o desempenho. Sua quilometragem certamente variará a partir deste teste simples.

Por exemplo, a equipe do Chrome publicou algumas recomendações detalhadas sobre quando implantar o push do servidor e fez medições em sites mais complexos para comparar os efeitos de nenhuma otimização, dicas de pré-carregamento e push do servidor por HTTP/2. Vale a pena ler o relatório Rules of Thumb for HTTP/2 Push para qualquer um que esteja pensando em implantar o HTTP/2 server push em produção.

A conclusão pragmática é que se você puder identificar quais recursos são necessários com antecedência, há um benefício real em ter servidores upstream enviando uma dica de pré-carregamento. O benefício adicional de enviar esses recursos é pequeno, mas mensurável, mas pode resultar em desperdício de largura de banda e atrasos nos recursos necessários. Você deve testar e monitorar cuidadosamente todas as configurações de envio de servidor.

Apêndice: Como funciona o HTTP/2 Push?

As informações abaixo são baseadas em parte na pesquisa feita no post detalhado de Jake Archibald : "O HTTP/2 push é mais difícil do que eu pensava ".

O push do servidor HTTP/2 normalmente é usado para enviar recursos dependentes preventivamente quando o cliente solicita um recurso. Por exemplo, se um cliente solicita uma página da web, o servidor pode enviar folhas de estilo, fontes e imagens dependentes para o cliente.

Quando um cliente faz uma conexão HTTP/2, o servidor pode escolher iniciar uma ou mais respostas push do servidor pela conexão. Esses pushes enviam recursos que o cliente não solicitou explicitamente.

O cliente pode rejeitar um push (enviando um quadro RST_STREAM ) ou aceitá-lo. O cliente armazena o conteúdo enviado em um “cache de envio” local que está associado à conexão HTTP/2.

Mais tarde, quando o cliente faz uma solicitação de um recurso usando uma conexão HTTP/2 estabelecida, ele verifica o cache de push da conexão para uma resposta concluída ou em trânsito à solicitação. Ele usa o recurso armazenado em cache em vez de fazer uma nova solicitação HTTP/2 para o recurso.

Qualquer recurso enviado permanece no cache de envio por conexão até que (a) seja usado ou (b) a conexão HTTP/2 seja fechada:

  1. Se o recurso for usado, o cliente faz uma cópia e a entrada no cache de push é excluída. Se o recurso puder ser armazenado em cache, o cliente poderá armazenar sua cópia em cache no cache de sua página HTTP.
  2. Se a conexão HTTP/2 for fechada por qualquer motivo, seu cache de push local será excluído.

Isto tem várias implicações:

  • O conteúdo no cache da página HTTP no navegador é usado preferencialmente ao conteúdo no cache de push, mesmo que o conteúdo enviado seja mais recente.
  • Conexões HTTP/2 podem ser compartilhadas entre diferentes carregamentos de página. Um recurso que é enviado como resultado de um carregamento de página pode ser usado quando solicitado em um carregamento de página diferente.
  • Solicitações com credenciais usam conexões HTTP/2 diferentes de solicitações sem credenciais; por exemplo, um recurso enviado com uma solicitação de origem cruzada (credenciado) pode não ser encontrado se o navegador fizer uma solicitação não credenciada para o recurso.

Você pode rever uma lista muito mais detalhada de problemas na postagem do blog de Jake Archibald: HTTP/2 push is tougher than I thought .

O push do servidor HTTP/2 é um recurso interessante. Certifique-se de testar completamente a configuração de push do seu servidor HTTP/2 e esteja preparado para recorrer às dicas de pré-carregamento nos casos em que isso proporciona um comportamento mais previsível e com reconhecimento de cache.


"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."