DevSecOps

25 abr, 2017

Push do servidor HTTP/2

Publicidade

Artigo de Jaana Burcu Dogan e Tom Bergan, publicado originalmente pelo The Go Blog. A tradução foi feita pela Redação iMasters com autorização.

HTTP/2 foi concebido para resolver muitas das falhas de HTTP/1.x. Páginas web modernas usam muitos recursos: HTML, folhas de estilo, scripts, imagens e assim por diante. Em HTTP/1.x, cada um desses recursos deve ser solicitado explicitamente. Isso pode ser um processo lento. O navegador inicia buscando o HTML, então aprende sobre mais recursos de forma incremental à medida que analisa e avalia a página. Uma vez que o servidor deve aguardar o navegador para fazer cada solicitação, a rede é muitas vezes ociosa e subutilizada.

Para melhorar a latência, o HTTP/2 introduziu o server push, que permite que o servidor envie recursos para o navegador antes que eles sejam solicitados explicitamente. Um servidor muitas vezes já conhece muitos dos recursos adicionais que uma página precisará e pode começar a enviar esses recursos conforme ele responde à solicitação inicial. Isso permite que o servidor utilize totalmente uma rede outrora ociosa e melhore os tempos de carregamento da página.

No nível de protocolo, o push de servidor HTTP/2 é conduzido por frames PUSH_PROMISE. Um PUSH_PROMISE descreve uma solicitação que o servidor prevê que o navegador fará em um futuro próximo. Assim que o navegador recebe um PUSH_PROMISE, ele sabe que o servidor irá entregar o recurso. Se o navegador mais tarde descobrir que ele precisa desse recurso, ele aguardará que o envio seja concluído em vez de enviar uma nova solicitação. Isso reduz o tempo que o navegador gasta esperando na rede.

Push do servidor em net/http

Go 1.8 introduziu suporte para enviar respostas de um http.Server. Esse recurso está disponível se o servidor em execução for um servidor HTTP/2 e a conexão de entrada usar HTTP/2. Em qualquer handler de HTTP, você pode afirmar se o http.ResponseWriter suporta o push do servidor verificando se ele implementa a nova interface http.Pusher.

Por exemplo, se o servidor souber que app.js será necessário para processar a página, o manipulador pode iniciar um push se http.Pusher estiver disponível:

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if pusher, ok := w.(http.Pusher); ok {
            // Push is supported.
            if err := pusher.Push("/app.js", nil); err != nil {
                log.Printf("Failed to push: %v", err)
            }
        }
        // ...
    })

A call do Push cria uma solicitação sintética para /app.js, sintetiza essa solicitação em uma frame PUSH_PROMISE e, em seguida, encaminha a solicitação sintética para o handler de solicitações do servidor, que gerará a resposta enviada. O segundo argumento para Push especifica cabeçalhos adicionais para incluir no PUSH_PROMISE. Por exemplo, se a resposta a /app.js varia em Accept-Encoding, então o PUSH_PROMISE deve incluir um valor Accept-Encoding:

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if pusher, ok := w.(http.Pusher); ok {
            // Push is supported.
            options := &http.PushOptions{
                Header: http.Header{
                    "Accept-Encoding": r.Header["Accept-Encoding"],
                },
            }
            if err := pusher.Push("/app.js", options); err != nil {
                log.Printf("Failed to push: %v", err)
            }
        }
        // ...
    })

Um exemplo totalmente funcional está disponível em:

$ go get golang.org/x/blog/content/h2push/server

Se você executar o servidor e carregar https://localhost:8080, as developer tools do seu navegador devem mostrar que app.js e style.css foram enviados pelo servidor.

Comece seus envios antes de responder

É uma boa ideia chamar o método Push antes de enviar quaisquer bytes da resposta. Caso contrário, é possível gerar respostas duplicadas acidentalmente. Por exemplo, suponha que você escreva parte de uma resposta HTML:

<html>
<head>
    <link rel="stylesheet" href="a.css">...

Em seguida, você chama Push (“a.css”, nil). O navegador pode analisar este fragmento de HTML antes de receber seu PUSH_PROMISE, caso em que ele enviará uma solicitação para a.css além de receber seu PUSH_PROMISE. Agora o servidor irá gerar duas respostas para a.css. Fazer o call do Push antes de escrever a resposta evita esta possibilidade inteiramente.

Quando usar o push do servidor

Considere o uso de push do servidor sempre que o seu link de rede estiver ocioso. Acabou de enviar o HTML para o seu aplicativo da web? Não perca tempo esperando, comece a enviar os recursos que seu cliente vai precisar. Você está fazendo inline recursos em seu arquivo HTML para reduzir a latência? Em vez de inline, tente fazer push. Os redirecionamentos são outro bom momento para usar o push porque quase sempre há uma viagem de ida e volta perdida enquanto o cliente segue o redirecionamento. Há muitos cenários possíveis para usar push – estamos apenas começando.

Seríamos negligentes se não mencionássemos algumas advertências. Primeiro, você só pode enviar recursos que o seu servidor esta autorizado a enviar – isso significa que você não pode enviar recursos que estão hospedados em servidores de terceiros ou CDNs. Em segundo lugar, não envie recursos a menos que você esteja confiante de que eles são realmente necessários para o cliente. Caso contrário, seu envio desperdiça largura de banda. Um corolário é evitar enviar recursos quando é provável que o cliente os já tenha em cache. Em terceiro lugar, a abordagem ingênua de enviar todos os recursos em sua página muitas vezes torna o desempenho pior. Quando em dúvida, avalie.

Os links a seguir fazem uma boa leitura complementar:

Conclusão

Com o Go 1.8, a biblioteca padrão oferece suporte prático para Push do Servidor HTTP/2, oferecendo maior flexibilidade para otimizar seus aplicativos web.

Vá para nossa página de demonstração do Server Push HTTP/2 para vê-lo em ação.

 

***

Este artigo é do The Go Blog. Ele foi escrito por Jaana Burcu Dogan e Tom Bergan. A tradução foi feita pela Redação iMasters com autorização. Você pode acessar o original em: https://blog.golang.org/h2push