Posted on: January 04, 2024 12:47 AM
Posted by: Renato
Categories: nginx performance cache Dicas
Views: 242
8 dicas para aumentar performance do NGINX 10x
8 min read
Sempre quis escrever um título sensacionalista, hehe, mas para não ficar de graça vou colocar realmente 8 dicas de coisas que fazemos aqui no Terra para ter uma alta performance com o NGINX, mas antes um pouco de história.
Nossa história com o NGINX começou em meados de 2010, em um projeto ambicioso chamado Realtime, onde modificamos um módulo chamado Push Stream para realizar publicação de eventos ao vivo, onde ao mesmo tempo que se publica um conteúdo ele é exibido na tela do usuário. O sistema funciona até hoje com pequenas alterações, e chegamos a colocar mais de 500 mil usuários simultâneos em 1 só servidor, a partir de então começamos a usar o NGINX em vários pontos da nossa estrutura substituindo o Apache, o IIS e em alguns casos até o Varnish.
Mas por que ele mais rápido que os concorrentes? É principalmente o fato de ele trabalhar orientado a eventos, e não orientado a processos como eram a maioria dos outros servidores web em 2010. Isso da um grande ganho, já que a maior parte do tempo de um servidor web é gasto esperando ter algum retorno de algum lugar.
Hoje expandimos o uso de serviços web orientados a eventos aos mais diversos usos na nossa plataforma, não só com o NGINX mais também utilizando o Tornado que é um framework em Python para desenvolver serviços web assíncronos.
01 — Use Cache
Sim eu sei, eu sempre falo isso, mas é a coisa mais fácil que você pode fazer que vai dar um maior ganho. Por exemplo, uma página do WordPress em um servidor pequeno pode chegar a demorar 1 segundo ou mais para ser gerada, e é a mesma para todos os usuários. Usando um cache você pode mudar esse tempo para praticamente zero.
http { ... proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=webcache:1000m inactive=1y max_size=1000m; proxy_temp_path /var/cache/nginx/tmp; proxy_cache_min_uses 1; ... server { ... location / { add_header X-Cache-Status $upstream_cache_status; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_cache webcache; proxy_cache_valid any 15s; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_pass http://localhost:8080; ...
Algumas dicas sobre essa configuração, primeiro na configuração proxy_cache_path criamos um cache onde os objetos ficam ativos por 1 ano, e tem um tamanho de 1Gb. Depois adicionamos o header X-Cache-Status para nos ajudar a identificar o que esta acontecendo com aquele objeto, com o proxy_cache_valid colocamos um valor mínimo de cache de 15s para todas os status http, o parâmetro Cache-Control com o max-age sobrescreve essa opção.
O parâmetro proxy_cache_use_stale serve para caso o seu backend entregue um erro, o NGINX entrega a ultima versão funcionando que ele tem em cache, e acredite, isso funciona muito. Já deixei backends fora por 2 horas confiando no NGINX entregar o conteúdo que ele tinha em cache e funcionou como um relógio.
02 — Use HTTP/2
Essa esta entre as dicas mais moderninhas e que mais tem se falado no mundo da performance. Pensando bem agora, o http/2 merece um post todo a parte, e talvez eu faça um algum dia, mas vamos a um resumo agora.
A grande vantagem do HTTP/2 é que ele usa somente uma conexão por domínio em vez de múltiplas conexões. E esta conexão é multiplexada, ou seja ele pode solicitar e receber várias coisas ao mesmo tempo. O tempo que leva o handshaking de conexões principalmente para conexões SSL/TLS é feito somente uma vez, o que acelera bastante todo o processo.
É claro que existem outras vantagens, como por exemplo a compressão de cabeçalhos, que em dias como hoje, que as mais diversas informações trafegam em cookies gigantes é uma bela de uma economia de banda e tempo, além de priorização de requisições e em alguns casos um SSL sem ônus de performance.
A partir do NGINX 1.9.5 é bem fácil habilitar o HTTP/2. Você só vai precisar configurar os certificados no seu servidor e habilitar o HTTP/2.
server { listen 443 ssl http2; ssl_certificate server.crt; ssl_certificate_key server.key ... }
Agora uma dica para redirecionar todo o trafego para o http/2, use por conta e risco.
server { listen 80; location / { return 301 https://$host$request_uri; } }
03 — Use HTTP/1.1 quando usar um Proxy
Dica simples, porém muito valiosa, caso você use backends em servidores externos ou até em redes externas ao servidor do NGINX.
location / { proxy_set_header Connection keep-alive; proxy_set_header Keep-Alive timeout=30,max=100; proxy_http_version 1.1; ... }
Isso diminui bastante os tempos de handshake entre os servidores e servidores web. Ele vai aproveitar a mesma conexão por pelo menos 100 vezes antes de ter que abrir uma nova.
04 — Aumente o número de Worker Processes
Não existe um número certo de Worker Process a ser configurado, alguns diriam que é o numero de CPU cores que o servidor possui, ou que mesmo que o teu servidor tenha um core só, em alguns casos é aconselhável aumentar esse número.
O que eu recomendo nesse caso é pelo menos deixar worker_processes auto, e o NGINX vai colocar um processo por CPU core, o que vai balancear um pouco melhor os recursos do servidor.
Esse cara não vem sozinho, o adequado é conferir o valor do worker_connections e ver se seu servidor tem capacidade de atender mais requisições por processo que as 512 default, e ainda o worker_rlimit_nofile que verifica o limite de arquivos abertos por workers, que dependendo do sistema operacional em média é 1024, em casos de grande escala esses números podem ser aumentados.
05 — Use tcp_nodelay, tcp_nopush e sendfile
A maioria dos tutoriais de otimização do NGINX vai dizer simplesmente que essas opções tem que estar habilitadas, mas eu acredito que conhecimento é poder, e não vale nada você habilitar uma opção que não sabe o que ela esta fazendo, então vamos lá para a explicação individual.
tcp_nodelay
Por default o protocolo tcp armazena em buffer os dados que você esta enviando, e pode esperar por até 200 ms (limite default do sistema operacional) caso ele considere o tamanho do pacote muito pequeno para ser enviado. Se você tiver mais interesse em como isso funciona efetivamente pode procurar por Nagle’s algorithm.
Acontece que ficar aguardando para enviar um pacote mais "gordinho" fazia muito sentido no início da internet quando tudo era muito lento, e trafegar um simples comando implicava em uma quantidade de dados grande. Com a velocidade da internet hoje isso não faz mais sentido, a quantidade de dados que enviamos em cada transmissão é muito maior e ficar esperando para que eles fiquem maiores é contraprodutivo.
Um simples tcp_nodelay on; dentro do http {…} habilita a opção de parar de esperar e em alguns casos pode economizar até 200 ms.
tcp_nopush
Essa opção é exatamente o contrário da tcp_nodelay, ele ativa o parâmetro TCP_CORK na pilha de conexão TCP que é mais agressivo que o Nagle’s algorithm. A aplicação instrui o sistema operacional a não enviar nenhum pacote até que o MSS (maximum segment size que é o tamanho máximo de dados que pode ser enviado, em bytes, em um único pacote) seja atingido ou que a aplicação mande o sinal para enviar todos os dados pendentes.
Apesar de parecer que o uso do tcp_nopush e o tcp_nodelay sejam mutualmente excludentes, a partir do Linux 2.5.9 eles podem trabalhar em conjunto.
Essa opção só esta disponível caso usemos o sendfile.
sendfile
O sendfile é responsável por qualquer coisa que envolva arquivos no NGINX, em uma linguagem mais técnica ele é o responsável por copiar dados entre um file descriptor e outro. Só que ele faz isso a um nível de kernel o que o torna muito mais rápido que um read ou write.
Então esse parâmetro se torna totalmente essencial caso você trabalhe com a entrega de arquivos estáticos a partir de um storage, e não é tão útil caso você trabalhe com o NGINX como um proxy reverso trabalhando com unix socket.
Misturando tudo
Parece muito claro usar o sendfile e o tcp_nopush juntos, mas porque combinar ele com o tcp_nodelay?
O NGINX é muito "espertão", e o que ele basicamente faz é usar o sendfile e o tcp_nopush para garantir que os pacotes estejam cheios antes de enviar eles para o cliente, diminuindo problemas de delay de rede, porém quando chegar ao último pacote, e ele provavelmente vai ser um pacote parcialmente completo, então o NGINX remove o tcp_nopush e força aquele último pacote incompleto a ser enviado imediatamente e não esperar os 200ms.
06 — Aumentando os file descriptors
Os file descriptors são o recurso do sistema operacional para representar conexões e arquivos abertos. O NGINX usa pelo menos 2 file descriptors por requisição, e esse valor pode aumentar muito principalmente quando você usar requisições com keep alive.
A maioria dos sistemas operacionais limita isso por aplicação em um valor default de 1024, o que pode ser um grande problema para atender grandes volumes de requisições em servidores mais "parrudos".
Não vou fazer um passo a passo de como editar isso pois depende de cada distribuição linux ou sistema operacional, mas fica a seguinte dica:
- sys.fs.file_max — Limite do sistema para file descriptors, geralmente atualizavel pelo sysctl
- nofile — Limite por usuários de file descriptors, geralmente configurado em /etc/security/limits.conf
Você pode editar também o parâmetro worker_rlimit_nofile que vai aumentar esse limite para cada worker do NGINX, ultrapassando os valores que você colocou para o sistema operacional.
07 — Gzip
Uma dica simples que pode salvar muito do tráfego de rede que você faz, e assim melhor bastante a vida para o seu servidor e para os seus usuários.
gzip on; gzip_comp_level 6; gzip_proxied expired no-cache no-store private auth; gzip_types text/plain application/x-javascript text/xml text/css application/xml;
A dica vai para o gzip_comp_level, caso você tenha um computador que já esteja com problema de alto processamento, você pode regular esse valor para o NGINX fazer menos processamento na hora da compressão.
08 — Desabilite o access log
Se você não usar o access log para fazer algum tipo de métrica permanente, você pode facilmente desabilitar o access log pelo parâmetro "access_log off;". Quando for necessário é só habilitar novamente.
Dependendo do acesso ao teu servidor, essa configuração vai aliviar bastante o IO e ganhar algum tempo de entrega.
Finalizando
O que eu melhor posso recomendar para todos é que teste em seus ambientes cada uma dessas opções para testar o que faz sentido ou não em cada ambiente, tudo varia muito de um lugar para o outro.
Pensem em performance, os seus usuários agradecem!
Fonte:
- https://cirolini.medium.com/8-dicas-para-aumentar-performance-do-nginx-10x-909a46d8806b
Donate to Site
Renato
Developer