Com nginx sendfile e tcp_nopush Entreguei ficheiros estáticos em modo «zero-copy» do sistema de ficheiros para o socket, reduzindo assim de forma percetível tanto a carga da CPU como o número de pacotes. Quando corretamente configuradas, ambas as diretivas aumentam a eficiência da transmissão, reduzem a sobrecarga e estabelecem as bases para uma otimização adequada do nginx no que diz respeito a recursos e transferências.
Pontos centrais
- Cópia zero através do `sendfile`: menos cópias, maior débito
- tcp_nopush armazena pacotes em buffer: quadros maiores, menos sobrecarga
- Combinação conta: sendfile + tcp_nopush + tcp_nodelay
- Casos de utilização priorizar: recursos estáticos, downloads de grande dimensão
- Testes No caso do NFS/SMB: avaliar o impacto; se necessário, desativar o `sendfile`
Por que é que o `sendfile` melhora tanto o desempenho do NGINX?
Eu ativo sendfile, porque o kernel consegue enviar ficheiros diretamente através da pilha de rede, sem ter de passar por operações de cópia adicionais no espaço do utilizador. Este percurso «zero-copy» reduz as mudanças de contexto e poupa ciclos da CPU, especialmente quando muitos clientes simultâneos acedem a conteúdos estáticos. Ficheiros de grande dimensão, como imagens, CSS, JavaScript ou arquivos, beneficiam disso, uma vez que a transferência de dados ocorre de forma mais uniforme e com menos sobrecarga. Os caches do sistema também funcionam de forma mais eficiente, pois ocorrem menos movimentos de memória e o kernel controla o percurso dos dados. É nos sistemas de ficheiros locais que a vantagem se torna mais evidente, razão pela qual faço as medições primeiro aí, antes de as aplicar a configurações mais exóticas.
O que o tcp_nopush faz exatamente e em que situações se destaca
Com tcp_nopush Peço ao sistema que só envie pacotes TCP quando estes estiverem devidamente preenchidos, em vez de enviar segmentos pequenos prematuramente. No Linux, isto corresponde a TCP_CORK; no FreeBSD, a TCP_NOPUSH; e, em ambos os casos, o número de pacotes diminui de forma mensurável. Esta diretiva não reduz a latência ao mínimo, mas visa uma melhor relação entre dados úteis e sobrecarga. Utilizo o `tcp_nopush` especificamente em ficheiros estáticos, porque é aí que os fluxos de dados contínuos proporcionam os maiores ganhos de eficiência. Sem o `sendfile`, o `tcp_nopush` fica sem efeito; por isso, integro sempre ambas as configurações em conjunto.
sendfile e tcp_nopush em conjunto: é assim que estabeleço as bases
A combinação de sendfile e o tcp_nopush reduz as cópias e agrupa os pacotes, permitindo que um servidor por núcleo de CPU consiga lidar com um número significativamente maior de transferências paralelas. Configuro ambos no nível de contexto http e, frequentemente, adiciono o tcp_nodelay para que o último resquício de um fluxo seja transmitido sem tempo de espera. Continua a ser importante realizar testes com tráfego real, uma vez que os tamanhos dos pacotes, o MTU e os clientes variam, e o melhor equilíbrio pode diferir ligeiramente consoante a carga de trabalho. Para diretórios estáticos, a ativação global é normalmente suficiente, enquanto que, no caso de rotas de resposta dinâmicas, presto atenção ao impacto. Esta combinação proporciona uma base sólida para futuras etapas de otimização do nginx, que serão implementadas posteriormente.
| diretiva | Objetivo | Efeito típico | Dependência |
|---|---|---|---|
| ativar o sendfile | Zero-Copy de ficheiro para socket | Menos carga na CPU, maior rendimento | Sistema de ficheiros local ideal |
| tcp_nopush ativado | Preencher as encomendas, reduzir os custos gerais | Menos segmentos por ficheiro | Funciona apenas com o sendfile |
| tcp_nodelay on | Enviar os últimos bytes sem esperar | Conclusão rápida da transferência | Adicionado tcp_nopush |
É assim que o tcp_nodelay interage com o tcp_nopush
Eu ativo tcp_nopush, para enviar o início de uma transferência em pacotes maiores e, ao mesmo tempo, ativar o tcp_nodelay para que a conclusão não fique bloqueada. Ambas as configurações atuam em fases diferentes do fluxo e não se interferem mutuamente quando o NGINX entrega ficheiros através do sendfile. Especialmente no caso de muitos ficheiros pequenos, o tcp_nodelay evita que o cliente tenha de esperar desnecessariamente devido a pequenos volumes de dados restantes. Testo primeiro esta combinação no ambiente de staging, observo os RTTs e os tamanhos dos segmentos e comparo-os com as métricas em produção. Desta forma, garanto eficiência no início e rapidez no final da transferência.
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
}
Cenários típicos de aplicação: onde as diretivas têm um forte impacto
Para grandes Transferências Tal como acontece com vídeos, arquivos ou imagens ISO, o percurso «zero-copy» do kernel reduz significativamente o tempo de CPU por transferência. Em configurações semelhantes a CDN, com muitos ficheiros CSS, JS e de tipos de letra, o tcp_nopush poupa segmentos e, assim, aumenta a largura de banda utilizável por socket. Em sites WordPress com um bom cache, a maioria dos pedidos diz respeito a recursos estáticos, pelo que noto o efeito muito rapidamente nesse contexto. Também os artefactos de compilação, as imagens de contentores ou os instaladores beneficiam desta funcionalidade, desde que se encontrem localmente e não sejam acedidos através de um sistema de ficheiros de rede instável. Quem prevê picos de carga consegue, com esta combinação, tirar o máximo partido da estabilidade do hardware disponível.
Exemplo prático: NGINX para WordPress com cache e recursos
Nas configurações do WordPress, defino sendfile, tcp_nopush e tcp_nodelay a nível global, forneço recursos estáticos diretamente e mantenho o PHP-FPM bem separado dos caminhos dinâmicos. Adiciono cabeçalhos de cache adequados para imagens, CSS e JavaScript, para que os navegadores façam menos idas e voltas. Quando forneço respostas do tipo streaming, tenho em conta a interação com o buffer e testo como os tamanhos dos blocos afetam a latência e a taxa de transferência; para tal, a visão geral sobre Transmissão de respostas em blocos. No caso de conteúdos baseados em texto, recorro à compressão, sem compactar arquivos binários desnecessariamente. Desta forma, o fluxo de pedidos mantém-se estável, a CPU fica menos sobrecarregada e o tempo até ao primeiro byte é curto.
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_types text/css application/javascript image/svg+xml;
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(jpg|jpeg|png|gif|css|js|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
}
}
}
Quando desativo deliberadamente a função `sendfile`
Eu troco sendfile quando os ficheiros se encontram em NFS, SMB ou sistemas de ficheiros distribuídos, que, no meu teste, apresentam taxas de transferência mais baixas. Alguns controladores ou latências no caminho de armazenamento anulam a vantagem do Zero-Copy, razão pela qual as medições são determinantes. Em caso de comportamentos esporádicos da rede, desativo primeiro o tcp_nopush para isolar os efeitos, antes de questionar o próprio sendfile. Também bugs invulgares do kernel ou pilhas mais antigas podem ser motivos para mudar temporariamente para o caminho clássico de leitura e escrita. O importante é implementar as alterações gradualmente e comprová-las com métricas.
Fontes de erro que tenho sempre em atenção
Primeiro verifico se tcp_nopush esteja ativado acidentalmente, enquanto o `sendfile` permanece desativado, pois, nesse caso, a configuração não surte efeito. No que diz respeito a caminhos dinâmicos, observo se o armazenamento em buffer adicional aumenta a latência e avalio os benefícios em relação ao tempo de resposta. Em redes de alta latência, avalio se pacotes maiores ajudam realmente ou se preciso ajustar os tamanhos dos segmentos e o keep-alive. A configuração do MTU e as funcionalidades de offloading da placa de rede também podem alterar visivelmente o resultado. Registos organizados, amostras pcap e métricas do sistema correlacionadas mostram-me rapidamente onde devo fazer ajustes.
Uma abordagem holística ao desempenho do NGINX: outros ajustes possíveis
Além de sendfile Vale a pena definir o número correto de `worker_processes` e `worker_connections`, para não limitar artificialmente os sockets. No Linux, utilizo o epoll e garanto que haja descritores de ficheiros suficientes, para que os picos de carga não provoquem estrangulamentos. Para conteúdos de texto, ativo o gzip ou o Brotli e verifico se o nível de compressão sobrecarrega a CPU de forma razoável. Ao nível da camada de transporte, mantenho as ligações abertas por mais tempo e otimizo o Keep-Alive, para o que serve o guia Ajuste do Keep Alive fornece orientações práticas. O TLS, a reutilização de sessões e o HTTP/2 ou HTTP/3 completam a configuração e permitem um elevado nível de paralelismo com uma latência moderada.
Limites e casos especiais: TLS, HTTP/2/3 e proxy
Tenho em conta que sendfile aplica-se tecnicamente apenas a percursos de ficheiros não encriptados ou a funções específicas do kernel. No TLS clássico, o NGINX encripta os bytes no espaço do utilizador, pelo que a vantagem do «zero-copy» deixa de existir; os kernels modernos podem, em parte, transferir a encriptação para o kernel, o que restabelece esse efeito, mas não está disponível em todas as configurações. No caso de HTTP/2 os dados estão em frames, várias respostas partilham uma ligação TCP e o NGINX reorganiza ativamente os bytes – neste caso, o sendfile é menos relevante. HTTP/3 baseia-se no UDP/QUIC e segue regras diferentes, pelo que consigo obter ganhos de eficiência principalmente através de buffers, controlo de congestionamento e tamanhos de blocos corretamente selecionados. Como Proxy invertido O sendfile só funciona se eu estiver efetivamente a servir ficheiros a partir do sistema de ficheiros local; respostas de proxy_pass ou fastcgi_pass passam, de qualquer forma, pelo espaço do utilizador. Por isso, separo rigorosamente os recursos do caminho dinâmico, para que a abordagem «zero-copy» seja aproveitada ao máximo.
Compreender corretamente a compressão: gzip/Brotli versus gzip_static
Sempre que o NGINX comprime conteúdos em tempo real, tem de ler o ficheiro, processá-lo e gravar o resultado – e, nesse processo, perde sendfile a sua vantagem. Por isso, no que diz respeito aos recursos estáticos, utilizo, sempre que possível, pré-comprimidos Ficheiros (por exemplo, .gz ou .br) e faço com que sejam entregues diretamente. Desta forma, mantém-se a rota «zero-copy», porque o NGINX pode transferir o ficheiro pré-comprimido tal como qualquer outro recurso. Para conteúdos com grande volume de texto e que raramente são alterados, consigo assim poupar recursos da CPU e obter um débito estável, sem comprometer o tempo de transmissão. No caso de ficheiros binários e formatos já comprimidos, evito qualquer compressão em tempo de execução – aqui, o que conta é a taxa de transferência pura de E/S, e o `sendfile` juntamente com o `tcp_nopush` revelam os seus pontos fortes.
AIO, directio e Page Cache: padrões para ficheiros pequenos e grandes
Eu combino sendfile com E/S assíncrona e acesso direto ao disco, para alcançar o melhor desempenho em função do tamanho do ficheiro. Os ficheiros pequenos a médios beneficiam da cache de páginas do kernel e permanecem no caminho do `sendfile`. Por outro lado, os ficheiros muito grandes podem esvaziar a cache; nesse caso, leio-os especificamente com direção fora da cache e utilizo threads AIO. Desta forma, alivio a carga na memória e mantenho baixa a latência para outras solicitações. Um padrão típico é o seguinte:
http {
# Caminho padrão: Zero-Copy a partir do cache de páginas
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Ficheiros grandes: ignorar o cache e ler de forma assíncrona
aio threads;
directio 4m; o # só se aplica a ficheiros >= 4 MiB
output_buffers 1 512k; Buffers # para percursos directio
sendfile_max_chunk 1m; Equilíbrio sob carga elevada
}
Com esta escalonagem, os ficheiros pequenos mantêm-se extremamente eficientes, enquanto as transferências muito grandes não sobrecarregam a memória de trabalho. Importante: o directio desativa a rota sendfile para os ficheiros em questão – exatamente como pretendo para o caso de utilização de ficheiros de grande dimensão.
Equidade e controlo do fluxo sob carga
Em períodos de elevada carga, pretendo evitar que um único fluxo monopolize a CPU ou o socket. Eu defino sendfile_max_chunk, para que o NGINX devolva o kernel após uma quantidade definida de bytes e dê espaço a outras ligações. Para o controlo da largura de banda, ajudam limit_rate e limit_rate_after, por exemplo, para limitar os downloads em massa, mantendo os recursos da interface de utilizador a funcionar rapidamente. Com postpone_output controlo a partir de que tamanho de resposta o NGINX começa a enviar – em combinação com o tcp_nopush, garanto assim um corte de pacotes adequado. Além disso, presto atenção a lingering_close, para que os pacotes restantes sejam transmitidos corretamente e o socket não seja encerrado abruptamente.
Sistemas de ficheiros, pré-leitura e caminhos de armazenamento
Porque sendfile Quando se utilizam caches de página, o sistema de ficheiros subjacente desempenha um papel importante. Verifico os valores de pré-leitura e mantenho-os de forma a que as sequências de leitura de ficheiros de grande dimensão não fiquem bloqueadas, sem, no entanto, suplantar os recursos mais pequenos. Em ext4 ou xfs Observo como o prefetching e o agendador de E/S se adaptam bem ao meu padrão de débito. Nos sistemas de ficheiros de rede (NFS/SMB), testo rigorosamente os parâmetros rsize/wsize, o cache e as latências, pois mesmo pequenos desvios neutralizam a vantagem do zero-copy. A minha regra continua a ser: primeiro, explorar ao máximo os caminhos locais; depois, ajustar cuidadosamente as pilhas externas – e dar sempre prioridade aos valores medidos em detrimento da intuição.
Ajustar de forma pragmática a pilha de rede e o offloading da placa de rede
Para um número elevado de ligações, confio no ajuste automático dos buffers das pilhas modernas, mas, se necessário, ajusto os buffers de envio e de receção. As funções de descarregamento da placa de rede (NIC), como TSO, GSO e GRO, reduzem sensivelmente a carga da CPU; no entanto, nas medições, procuro ter cuidado, pois as capturas de pacotes podem parecer distorcidas devido ao descarregamento (aparentemente, poucos segmentos muito grandes). Por isso, correlaciono pcap‑Traces com métricas do NGINX e do kernel, para distinguir os tamanhos reais dos dados transmitidos dos artefactos de offload. No caso de picos de latência, interrompo brevemente os testes com os offloads desativados, registo a diferença e, em seguida, decido o que traz mais benefícios em funcionamento contínuo.
Modelos de configuração por localização: ativar e desativar de forma seletiva
Deixo em aberto a possibilidade de, sendfile a ser substituído, dependendo do caminho ou do tipo de ficheiro. Para diretórios estáticos, mantém-se ativado; para caminhos de streaming ou dinâmicos, desativo-o seletivamente quando os buffers ou filtros (por exemplo, compressão) têm prioridade. Um pequeno exemplo:
server {
listen 80;
server_name static.example.com;
root /var/www/static;
# Recursos estáticos: Zero-Copy
location /assets/ {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
expires 7d;
}
# Conteúdo dinâmico ou streaming: flexibilidade em vez de Zero-Copy
location /api/ {
sendfile off;
proxy_pass http://app_upstream;
}
}
Esta separação impede que eu perca vantagens num dos lados, apenas porque outro caminho impõe requisitos específicos.
Intervalos, fatias e grandes catálogos
No caso de objetos de grandes dimensões, entra em jogo Gama- As solicitações tiram partido dos seus pontos fortes: o cliente carrega apenas as partes necessárias e as ligações mantêm-se estáveis. Em catálogos de conteúdo com ficheiros muito grandes, gosto de segmentar as transferências de forma lógica – a carga do servidor distribui-se de forma mais uniforme e os erros, como interrupções, custam menos tempo. Em cenários de cache, previno os „Thundering Herds“ ao armazenar as respostas de forma sensata, mas sem reter artificialmente pequenos blocos e dados residuais. A interação com o tcp_nopush continua a ser fundamental: mantenho os segmentos iniciais grandes, mas não deixo que o final fique à espera.
Estratégia de medição e ensaio: comprovar os efeitos de forma fiável
Comprovo as otimizações com testes reproduzíveis. No lado do servidor, analiso os perfis da CPU, $request_time, $bytes_enviados, ligações ativas e mudanças de contexto. Na rede, meço tamanhos de segmentos, retransmissões e distribuição de RTT; correlaciono as capturas de pacotes com as estatísticas de socket para ter em conta os efeitos do offload. Do lado do cliente, comparo o TTFB, o First Contentful Paint e os tempos de download sob RTTs e larguras de banda realistas. Vario o MTU, as definições de Keep-Alive e os tamanhos dos ficheiros, para não ver apenas curvas de cenário ideal. Por fim, com base em números concretos, decido se o `sendfile`/`tcp_nopush` proporciona a estabilidade e a eficiência desejadas na respetiva carga de trabalho – e faço ajustes finos até que o façam.
Detalhes HTTP que fazem a diferença: Range e Streaming
Eu uso Gama-Solicitações no caso de ficheiros de grande dimensão, para que os clientes apenas carreguem as partes necessárias e as ligações se mantenham estáveis. Especialmente no caso de avanços de vídeo e retomadas de atualizações, um suporte adequado aos intervalos de bytes ajuda a distribuir a taxa de transferência de forma eficiente; a página sobre este tema fornece informações adicionais Pedidos HTTP Range. Para respostas contínuas com corpo crescente, testo estratégias de streaming e garanto que os buffers não retenham os dados por demasiado tempo acidentalmente. Ao fazê-lo, respeito as caches e defino cabeçalhos adequados, para que os proxies e os navegadores funcionem corretamente. Tenho em conta a interação com o tcp_nopush, uma vez que o tamanho dos pacotes e o momento da limpeza têm uma influência direta na rapidez percebida.
Brevemente resumido
Com sendfile Encaminho os ficheiros de forma eficiente diretamente para o kernel e, com o tcp_nopush, faço com que os pacotes sejam preenchidos de forma adequada antes de sobrecarregarem a ligação. Ambas as diretivas complementam-se, enquanto o tcp_nodelay entrega o último byte restante sem atrasos. Verifico o efeito em tráfego real, presto atenção ao caminho de armazenamento, ao MTU, ao Keep-Alive e à compressão, e faço medições de forma consistente. Para cargas de trabalho do tipo WordPress e CDN, os benefícios tornam-se visíveis muito rapidamente, uma vez que muitas solicitações dizem respeito a recursos estáticos. Quem utilizar estas configurações de forma seletiva obtém maior débito por núcleo, reduz a sobrecarga e cria reservas para picos de crescimento reais.


