DevSecOpsARTIGO

Inovando com servidor push HTTP 2.0

Se você já inseriu um recurso, "simulou" um servidor push. A diferença é que o HTTP 2.0 faz com que o padrão seja mais eficiente e poderoso!

O HTTP 2.0 permite que o servidor envie várias respostas (em paralelo) para uma única solicitação do cliente – isso é conhecido como servidor push. Mas por que nós sempre iríamos quer fazer uma coisa dessas? Bem, uma página comum requer dezenas de ativos adicionais, tais como JavaScript, CSS e imagens, e referências a todos esses ativos estão embedded no próprio HTML que o servidor está produzindo! Assim, em vez de esperar o cliente descobrir referências a esses ativos, e se servidor simplesmente enviar todos eles imediatamente? O servidor push pode eliminar roundtrips inteiros de latência de rede desnecessários.

Na verdade, se você já inseriu um recurso (CSS , JS ou uma imagem), você “simulou” um servidor push: um recurso inline é “pushed” como parte do documento pai. A única diferença é que o HTTP 2.0 faz com que esse padrão seja mais eficiente e muito mais poderoso!

Mãos à obra com servidor push HTTP 2.0

Um recurso inline, por definição, faz parte do documento pai. Por isso, não pode ser armazenado em cache de forma independente e pode precisar ser duplicado em várias páginas diferentes – isso é ineficiente. Por outro lado, os recursos pushed são armazenados em cache individualmente pelo navegador e podem ser reutilizado em várias páginas. Um exemplo:

spdy.createServer(options, function(req, res) {
  // push JavaScript asset (/main.js) to the client
  res.push('/main.js', {'content-type': 'application/javascript'}, function(err, stream) {
    stream.end('alert("hello from push stream!")');
  });

  // write main response body and terminate stream
  res.end('Hello World! <script src="/main.js"></script>');
}).listen(443);

O que temos acima é um servidor SPDY mínimo implementado com a ajuda do módulo node-SPDY, que responde a todas as solicitações recebidas ao escrever uma string “Hello world!”, seguida pela tag script. Também estamos fazendo algo inteligente: nós damos push ao arquivo main.js para o cliente, cujo corpo aciona um alerta de JavaScript.

Como resultado, no momento em que o navegador descobre a tag script na resposta HTML, o arquivo main.js já está em cache, e nenhum roundtrip extra é incorrido! Servidor push HTTP 2.0 torna inlining obsoleto. O melhor de tudo é que o servidor push já é suportado por todos os navegadores com capacidade SPDY  (Firefox, Opera e Chrome).

Em que mais podemos dar push?

Substituir recursos inline com servidor push é o exemplo canônico. No entanto, por que parar por aí, em que mais poderíamos dar push? Bem, qualquer resposta HTTP é um jogo justo. Poderíamos dar push em um redirecionamento? Sim, isso é uma tarefa simples:

spdy.createServer(options, function(req, res) {
  //push JavaScript asset (/newasset.js) to the client
  res.push('/newasset.js', {'content-type': 'application/javascript'}, function(err, stream) {
    stream.end('alert("hello from (redirected) push stream!")');
  });

  // push 301 redirect: /asset.js -> /newasset.js
  res.push('/asset.js', {':status': 301, 'Location': '/newasset.js'}, function(err, stream) {
    stream.end('301 Redirect');
  });

  // write main response body and terminate stream
  res.end('<script src="/asset.js"></script>');
}).listen(443);

Mesmo exemplo acima, exceto que nós mudamos o recurso asset.js para ser um redirecionamento 301 para o arquivo newasset.js. O navegador armazena em cache tanto o redirecionamento quanto o ativo, e executa o script sem roundtrips de rede extra. Aplicações? Eu vou deixar isso como um exercício para o leitor.

Poderíamos levar isso ainda mais longe? Que tal validações push em cache e revalidações para o cliente? Podemos marcar qualquer recurso existente no cache do cliente como obsoleto (ou seja, fazer push em um timestamp obsoleto) ou, inversamente, atualizar a sua vida útil, fazendo push em um 304 com um timestamp futuro. Em suma, o servidor pode gerenciar ativamente o cache do cliente!

Se o servidor é muito agressivo ou se comporta mal, então o cliente pode limitar o número de fluxos de push ou cancelar streams individuais como ele desejar. Encontrar a estratégia certa e o equilíbrio em ambos os lados será a chave para obter o melhor desempenho do servidor push – não é um desafio fácil, mas que poderia render retornos elevados.

Nota: fazer push de revalidações de cache não é suportado pelos navegadores atuais. Deveriam ser?

Notificações de cliente para servidor push

Servidor push HTTP 2.0 não é uma substituição para tecnologias como Server-Sent Events (SSE) ou WebSocket. Recursos entregues via servidor push HTTP 2.0 são processados pelo navegador, mas não se misturam com o código do aplicativo – não há API do JavaScript para receber notificações para esses eventos. No entanto, a solução para esse dilema é muito simples, porque podemos combinar um canal SSE com servidor push e obter o melhor de ambos:

spdy.createServer(options, function(req, res) {
  // set content type for SSE stream
  res.setHeader('Content-Type', 'text/event-stream');

  messageId = 1;
  setInterval(function(){
    // push a simple JSON message into client's cache
    var msg = JSON.stringify({'msg': messageId});
    var resourcePath = '/resource/'+messageId;
    res.push(resourcePath, {}, function(err, stream) { stream.end(msg) });

    // notify client that resource is available in cache
    res.write('data:'+resourcePath+'\n\n');
    messageId+=1;
  }, 2000);
}).listen(443);

Um exemplo bobo, mas que ilustra o ponto: o servidor gera uma mensagem em um intervalo de dois segundos, push em cache do cliente e, em seguida, envia uma notificação SSE para o cliente. Por sua vez, o cliente pode se inscrever para esses eventos no código do aplicativo e executar sua própria lógica para processar o evento:

<script>
  var source = new EventSource('/');

  source.onmessage = function(e) {
    document.body.innerHTML += "SSE notification: " + e.data + '<br />';

    // fetch resource via XHR... from cache!
    var xhr = new XMLHttpRequest();
    xhr.open('GET', e.data);
    xhr.onload = function() {
      document.body.innerHTML += "Message: " + this.response + '<br />';
    };

    xhr.send();
  };
</script>

A longa duração do stream de SSE, os recursos push e todos os outros fluxos HTTP 2.0 são eficientemente multiplexadas sobre uma única conexão TCP – não há sobrecarga extra de conexão ou roundtrips desnecessários. Ao combinar SSE e servidor push, podemos entregar recursos arbitrários (binário ou texto) para o cliente, receber o benefício de alavancar o cache do navegador nativo, e executar a lógica do aplicativo apropriado! O melhor de tudo: isso funciona hoje.

Inovando com servidor push

Servidor push abre todo um mundo de novas oportunidades de otimização. Os exemplos anteriormente abordados estão arranhando a superfície do que é possível, e há muitas questões adicionais a serem consideradas:

  • Que recursos devem ser push e quando? Eles já estão em cache?
  • O servidor pode automaticamente inferir que os recursos devem ser push?
  • Aplicações avançadas, como gerenciamento de cache ativo, devem ser suportadas?
  • Como podemos criar nossos aplicativos para tirar o máximo proveito do servidor push?

Com HTTP 2.0, os servidores têm a oportunidade de se tornarem muito, muito inteligentes, na forma de otimizar a entrega de ativos individuais, e, mais importante ainda, na entrega de todo o aplicativo. Da mesma forma, os navegadores podem expor recursos adicionais da API para tornar esse processo mais eficiente. Na verdade, a longo prazo, servidor push pode muito bem vir a ser o “recurso matador” do HTTP 2.0!

P.S.: Curioso para saber mais sobre HTTP 2.0? Confira a visualização gratuita do meu livro O’Reilly!

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/

 

É Developer Advocate no Google, trabalha com tudo o que está relacionado a desempenho na web, como melhores práticas, protocolos, padrões e desempenho do navegador.

Ver perfil