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/







