"Chacal": espreme o frontend

Oi Sou Vanya, líder da equipe de plataforma da Tinkoff Business.

Meu passatempo favorito é abrir a guia DevTools e verificar a quantidade de artefatos do site. Neste artigo, mostrarei como reduzimos o peso do aplicativo em 30% com a ajuda da equipe de front-end da plataforma em um dia sem alterar o código do site. Não há truques e registros - apenas nginx, docker e node.js (opcional).




Porque


Agora, os aplicativos front-end pesam muito. Os artefatos coletados podem pesar de 2 a 3 MB ou mais. No entanto, algoritmos de compactação ajudam os usuários.

Até recentemente, usamos apenas o Gzip, que foi introduzido no mundo em 1992. Esse é provavelmente o algoritmo de compactação mais popular da Web. Ele é suportado por todos os navegadores acima do IE 6.

Deixe-me lembrá-lo de que o nível de compactação do Gzip varia no intervalo de 1 a 9 (mais é mais eficiente) e você pode compactá-lo em tempo real ou estaticamente.

  • “On the fly” (dinamicamente) - os artefatos são armazenados no formulário recebido após a montagem, sua compactação ocorre durante a entrega ao cliente. No nosso caso, no nível nginx.
  • Estático - os artefatos são compactados após a montagem e o servidor HTTP os envia ao cliente "como estão".

Obviamente, a primeira opção requer mais recursos do servidor para cada solicitação. O segundo está na fase de montagem e preparação da aplicação.

Nosso frontend foi compactado dinamicamente pelo quarto nível. Vou demonstrar a diferença entre um artefato compactado e o original:
Nível de compressão
Peso do Artefato, Kb
Tempo de compressão, ms
0 0
2522
-
1
732
42.
2
702
44
3
683
48.
4
636
55
5
612
65
6
604
77
7
604
80
8
603
104
9
601
102

Você pode perceber que até o quarto nível reduz o tamanho do artefato em 4 vezes! E a diferença entre o quarto nível e o nono é de 35 Kb, ou seja, 1,3% do original, mas o tempo de compactação é 2 vezes maior.

E, recentemente, pensamos: por que não mudar para Brotli? Sim, e no nível mais poderoso de compactação!

A propósito, esse algoritmo foi introduzido pelo Google em 2015 e possui 11 níveis de compactação. Ao mesmo tempo, o quarto nível de Brotli é mais eficaz que o nono no Gzip. Fiquei motivado e rapidamente joguei código para compactar artefatos com o algoritmo Brotli. Os resultados são apresentados abaixo:
Nível de compressão
Peso do Artefato, Kb
Tempo de compressão, ms
0 0
2522
-
1
662
128
2
612
155
3
601
156
4
574
202
5
526
227
6
512
249
7
501
303
8
496
359
9
492
420
10
452
3708
11
446
8257

No entanto, a tabela mostra que mesmo o primeiro nível de compactação de Brotli demora mais que o nono nível no Gzip. E o último nível - até 8,3 segundos! Isso me alertou.

Por outro lado, o resultado é claramente impressionante. Em seguida, tentei transferir a compactação para o nginx - pesquise a documentação no Google . Tudo acabou sendo extremamente simples:

brotli on; brotli_comp_level 11; brotli_types text/plain text/css application/javascript; 

Ele montou a imagem do docker, lançou o contêiner e ficou terrivelmente surpreso:



O tempo de download do meu arquivo aumentou dez vezes - de 100 ms para 5 segundos! O aplicativo tornou-se impossível de usar.

Tendo estudado a documentação mais profundamente, percebi que você pode distribuir estaticamente. Usei um script escrito anteriormente, compactou os mesmos artefatos, coloquei em um contêiner e o iniciei. Os tempos de download voltam ao normal - vitória! No entanto, é muito cedo para se alegrar, porque a proporção de navegadores que suportam esse tipo de compactação é de cerca de 80% .

Isso significa que você deve manter a compatibilidade com versões anteriores, além de querer usar o nível mais eficaz do Gzip. Então surgiu a idéia de criar um utilitário de compactação de arquivos, que mais tarde recebeu o nome de "Jackal".



Do que precisamos?


Nginx, Docker e Node.js, embora você também possa usar o bash, se desejar.
Com o Nginx, quase tudo fica claro:

 brotli off; brotli_static on; gzip_static on; 

Mas o que fazer com aplicativos que ainda não conseguiram atualizar a imagem do docker? Certo, adicione compatibilidade com versões anteriores:

 gzip on; gzip_level 4; gzip_types text/plain text/css application/javascript; 

Vou explicar o princípio de operação:


A cada solicitação, o cliente envia um cabeçalho Accept-Encoding, que lista os algoritmos de compactação suportados, separados por vírgulas. Geralmente é deflate, gzip, br.

Se o cliente tiver br na linha, o nginx procurará arquivos com a extensão .br, se não houver esses arquivos e o cliente oferecer suporte ao Gzip, procurará .gz. Se não houver esses arquivos, ele tremerá "em movimento" e retornará com o quarto nível de compactação.

Se o cliente não suportar nenhum tipo de compactação, o servidor emitirá artefatos em sua forma original.

No entanto, surgiu um problema: nossa imagem do docker nginx não suporta o módulo Brotli. Como base, tirei a imagem da janela de encaixe finalizada .

Arquivo de encaixe para "empacotar" nginx em um projeto
 FROM fholzer/nginx-brotli #      RUN rm -rf /usr/share/nginx/html/ #      COPY app/nginx /etc/nginx/conf.d/ #      COPY dist/ /usr/share/nginx/html/ #  CMD nginx -c /etc/nginx/conf.d/nginx.conf 


Descobrimos o balanceamento de tráfego, mas de onde obter artefatos? É aqui que o Chacal vem em socorro.

O Chacal


Este é um utilitário para compactar as estáticas do seu aplicativo.

Agora, esses são três scripts node.js. agrupados em uma imagem do docker com o nó: alpine. Vamos revisar os scripts.

compressor de base - um script que implementa a lógica básica de compactação.

Argumentos de entrada:

  1. Função de compactação - qualquer função javascript, você pode implementar seu próprio algoritmo de compactação.
  2. Parâmetros de compressão - um objeto com parâmetros necessários para a função transferida.
  3. Extensão - expansão de artefatos de compactação. Deve ser especificado começando com um caractere de ponto.

gzip.js - um arquivo com uma chamada de compressor base com a função Gzip passada do pacote zlib e indicando o nono nível de compactação.

brotli.js - arquivo com uma chamada de compressor base com a função Brotli passada do mesmo pacote npm e indicando o 11º nível de compactação.

Dockerfile criando a imagem Jackal
 FROM node:8.12.0-alpine #     COPY scripts scripts #  package.json  package-lock.json   COPY package*.json scripts/ #      WORKDIR scripts #    #    node_modules/   #  ,     RUN npm ci #     CMD node gzip.js | node brotli.js 


Descobrimos como funciona, agora você pode executar com segurança:

 docker run \ -v $(pwd)/dist:/scripts/dist \ -e 'dirs=["dist/"]' \ -i mngame/shakal 

  • -v $ (pwd) / dist: / scripts / dist - especifique qual diretório local considerar o diretório no contêiner (link para a montagem). É necessário especificar o diretório de scripts, pois está funcionando dentro do contêiner.
  • -e 'dirs = ["dist /"]' - especifique o parâmetro do ambiente dirs - uma matriz de linhas que descreve os diretórios dentro dos scripts / que serão compactados.
  • -i mngame / shakal - especificando uma imagem com o docker.io.

Nos diretórios especificados, o script compacta recursivamente todos os arquivos com as extensões especificadas .js, .json, .html, .css e salva os arquivos com as extensões .br e .gz ao lado. Em nosso projeto, esse processo leva cerca de dois minutos com o peso de todos os artefatos de cerca de 6 MB.

Nesse ponto, e talvez até mais cedo, você pode ter pensado: “Que janela de encaixe? Qual nó? Por que não adicionar dois pacotes a si mesmo no package.json do projeto e ligar diretamente para o postbuild? ”

É muito doloroso para mim ver quando, para fins de execução de linters no CI, o projeto instala mais de 100 pacotes para si, dos quais precisa no máximo de 10 na fase de espera.

No caso da janela de encaixe, obtemos uma imagem pré-montada na qual tudo o necessário para a compactação está instalado. Se você não precisar compactar nada agora - não compactar. Precisa de um cotão - execute apenas, precisa de testes - execute apenas eles. Além disso, temos uma boa versão do Jackal: não precisamos atualizar suas dependências em cada projeto - basta lançar uma nova versão e usar a tag mais recente para o projeto.

Resultado:


  • O tamanho dos artefatos mudou de 636 Kb para 446 Kb.
  • O tamanho da porcentagem diminuiu 30%.
  • O tempo de download diminuiu de 10 a 12%.
  • O tempo de descompressão, com base no artigo , permanece o mesmo.

Total


Você pode ajudar seus usuários agora mesmo, no próximo PR: adicione uma etapa após a montagem - compressão "Chacal" e entregue artefatos ao seu contêiner. Após meia hora, seus usuários se sentem um pouco melhor.

Conseguimos reduzir o peso do front-end em 30% - você terá sucesso! Todos os sites fáceis.

Referências:


Source: https://habr.com/ru/post/pt474632/


All Articles