...

Configurar de forma ideal os processos de trabalho do NGINX para obter o máximo desempenho

Eu configuro NGINX Worker de forma a que os parâmetros `worker_processes`, `worker_connections` e `worker_rlimit_nofile` estejam perfeitamente sincronizados e o Epoll funcione no ciclo de eventos. Assim, utilizo Núcleos de CPU seja eficiente, permita dimensionar de forma planeada as ligações simultâneas e mantenha as latências baixas nos picos de carga.

Pontos centrais

Os aspetos fundamentais que se seguem fornecem-te orientações imediatas para uma configuração robusta do NGINX Worker.

  • processos_trabalhadores associar ao número de núcleos lógicos, de preferência com „auto“.
  • ligações_trabalhadores ajustar de forma a que os picos reais sejam facilmente cobertos.
  • rlimit_nofile e aumentar os limites do sistema operativo de acordo com o volume de tráfego.
  • epoll e ativar o multi_accept para tirar partido do ciclo de eventos de forma eficiente.
  • Ensaios de carga avançar e ir ajustando com precisão, passo a passo.

Arquitetura do NGINX: Compreender os servidores «Master» e «Worker»

Separo as tarefas de Mestre e os Workers: o Master carrega as configurações, abre sockets e inicia processos, enquanto os Workers processam os pedidos no evento-loop. Cada Worker funciona de forma autónoma, reage a eventos e consegue gerir milhares de ligações sem causar bloqueios. Este modelo revela todo o seu potencial quando distribuo adequadamente os núcleos da CPU e utilizo o ciclo de eventos de forma otimizada através do epoll. Tenho em conta que cada salto adicional do proxy consome recursos de ligação, o que se reflete nos limites. Quem compreende as funções decide conscientemente sobre Recursos e evita gargalos numa fase precoce.

Combinar corretamente as três diretrizes fundamentais

Considero processos_trabalhadores, worker_connections e worker_rlimit_nofile nunca isoladamente, mas sim como um todo. O número total de ligações possíveis resulta da multiplicação do número de trabalhadores pelo número de ligações por trabalhador; a partir daí, deduzo os limites para os descritores de ficheiros. Se estes parâmetros não estiverem em sintonia, deparo-me com o erro „too many open files“ ou com timeouts severos. Para cargas elevadas, preciso de uma cadeia coerente: processos suficientes, ligações generosas, um rlimit_nofile devidamente aumentado e parâmetros do sistema operativo adequados. Assim, evito que um valor demasiado pequeno Limite castrou toda a capacidade.

worker_processes: selecionar o número de forma específica

Eu fixo processos_trabalhadores normalmente em „auto“, para que o NGINX detete o número de núcleos lógicos da CPU e possa utilizar cada núcleo. Um worker por núcleo evita mudanças de contexto desnecessárias e distribui a carga de forma eficiente, o que permite manter o tempo de resposta previsível. Em máquinas com um número muito elevado de núcleos, testo deliberadamente números mais baixos de workers para comparar os acertos de cache e a utilização dos núcleos. Se as métricas indicarem que os núcleos estão sobrecarregados ou que as falhas de TLB estão a aumentar, ajusto o número de workers gradualmente. Primeiro medir, depois alterar – é assim que garanto resultados fiáveis Resultados.

worker_connections: aumentar as ligações de forma programável

Eu escolho o ligações_trabalhadores dependendo do tráfego de destino e da combinação de protocolos, começando frequentemente em 2048 ou 4096. Para APIs com elevado tráfego, considero 8192, desde que os limites do sistema operativo e a memória RAM o permitam. Verifico cada aumento com testes de carga, porque as ligações abertas ocupam memória e influenciam o comportamento a montante. Quando os handshakes SSL ou uploads de grande volume predominam, oriento-me mais pelos perfis de CPU e E/S, e não apenas pelos números brutos de ligações. Desta forma, garanto que o número definido por trabalhador Capacidade e que continue a ser realmente utilizável.

Sincronizar o `worker_rlimit_nofile` com os limites do sistema operativo

Vou garantir que rlimit_nofile cobre, pelo menos, a capacidade total teórica e é frequentemente configurado com uma margem de segurança. Para cenários de proxy reverso, calculo um segundo descritor para o upstream por cada ligação de cliente. Assim, costumo definir o rlimit_nofile com um valor duas vezes superior ao número esperado de ligações simultâneas. Aumento os limites do kernel e do utilizador (ulimit -n, fs.file-max) de forma a que o NGINX consiga efetivamente atingir esses valores. Se surgirem mensagens relativas a ficheiros abertos no registo de erros, aumentei rapidamente os limites e observo a Latência novamente sob carga.

Bloco de eventos: utilizar eficazmente o epoll e o multi_accept

Ativo no bloco «Eventos» epoll e defino o multi_accept como „on“, para que os workers aceitem as ligações em espera de uma só vez. O epoll reduz a sobrecarga quando há muitos sockets simultâneos e está em sintonia com o design não bloqueante do NGINX. Estas opções revelam-se vantajosas em picos de tráfego, pois aceleram a fase de aceitação e permitem avançar mais rapidamente para o processamento propriamente dito. No Linux, esta é a minha configuração padrão, que só alterei em casos especiais raros. Quem quiser aprofundar o assunto pode comparar o modelo de loop de eventos com Threadpool vs. Evento-Loop e conclui que conclusões para o próprio ambiente.

Afinidade da CPU: associar os workers aos núcleos

Eu fixo worker_cpu_affinity de forma direcionada, quando as cargas de trabalho são constantes e dependentes da CPU. Distribuo o esquema de atribuição através de máscaras de bits, para evitar mudanças de contexto e promover a localidade da cache. Com quatro núcleos, atribuo as máscaras de forma a que cada worker receba um núcleo próprio. Em seguida, analiso as taxas de falhas de cache, as latências medianas e os percentis de 99, para observar claramente o efeito. Encontrará explicações mais detalhadas sobre afinidade e NUMA de forma resumida em Afiliação da CPU na prática, o que, no ajuste fino de Trabalhador- Os layouts ajudam.

Planear a capacidade: margem de segurança e testes de carga

Quando se trata de ligações, pretendo fazer uma Tampão que seja significativamente superior aos picos observados, para que os picos de tráfego de curta duração não ultrapassem diretamente os limites. Se duplicar a carga de pico como ponto de partida, tenho uma margem de manobra sólida em muitos cenários. Em caso de tráfego altamente variável, aumentei ainda mais a margem de segurança até que os percentis de 99% se comportassem de forma estável. Em seguida, verifico os pontos de estrangulamento com ferramentas como o wrk ou o k6, observo as taxas de erro e analiso as ligações abertas no estado. Só quando as métricas estiverem consistentes é que aumentei ou reduzi de forma específica cada Valores.

Configuração e exemplos de cálculos

Calculo a capacidade de ligação multiplicando o número de workers pelo número de ligações por worker e, a partir daí, defino limites mais elevados. Com quatro núcleos de CPU, a configuração «auto» e 4096 ligações por worker, chego, matematicamente, a 16 384 ligações simultâneas. Em cenários de proxy, costumo definir o rlimit_nofile para 32 768 ou mais, de modo a incluir os sockets upstream. Para máquinas pequenas com dois núcleos, 2048 ligações por trabalhador são frequentemente suficientes, desde que a proporção de uploads e de tráfego TLS se mantenha moderada. A tabela seguinte ajuda a classificar os Valores iniciais:

Núcleos de CPU processos_trabalhadores worker_connections (Início) Valor mínimo de rlimit_nofile (valor de referência) Nota
2 auto (≈2) 2048 ≥ 4096 Reserva Planear para TLS/Proxy
4 auto (≈4) 4096 ≥ 16 384 No caso do proxy, frequentemente um fator de 2 nos FDs
8 auto (≈8) 4096-8192 ≥ 32 768 Ensaio de carga decide sobre o aumento
16+ carro, ou menos, se for o caso 8192+ ≥ 65535 Testar com afinidade e discrição

NGINX Worker e Upstreams: ponderar corretamente os cenários

Faço a distinção entre entrega estática, funcionamento do proxy inverso e carga do gateway de API, porque estes são os Trabalhador-A configuração varia consoante as necessidades. Os conteúdos estáticos consomem menos recursos, enquanto o TLS, a compressão e as ligações upstream exigem mais da CPU e dos FDs. Quanto maiores forem as chaves SSL e quanto mais handshakes houver, mais vantajosa se torna a configuração „um worker por núcleo“. Uploads de grande dimensão deslocam o foco para a E/S, o que me leva a prestar mais atenção ao `rlimit_nofile` e aos buffers de rede. Quando se verificam tempos de espera percetíveis na aceitação ou nas respostas do backend, a visão geral ajuda-me a Filas de espera e latência, para evitar pontos de estrangulamento direcionado resolver.

Fluxo de trabalho prático: passo a passo para um servidor mais rápido

Começo por fazer um levantamento da situação atual de todos os elementos relevantes Valores No ficheiro nginx.conf, verifico os núcleos da CPU, o ulimit e os parâmetros do kernel. Depois, defino o `worker_processes` para «auto», defino o `worker_connections`, por exemplo, para 4096 e aumentei generosamente o `rlimit_nofile`. No bloco «Events», ativo o epoll e o multi_accept e verifico os registos através de uma atualização. Seguem-se testes de carga em condições reproduzíveis, durante os quais observo os tempos de resposta, as taxas de erro e as ligações abertas. No ajuste fino, altero sempre apenas uma variável, documento cada passo e verifico os efeitos nos Métricas.

Ambiente de alojamento: recursos, kernel, rede

Procurarei garantir que haja CPU-núcleos, memória RAM suficiente, SSDs ou NVMe rápidos e um kernel Linux atualizado. Só assim é que o epoll, as pilhas TCP modernas e as funcionalidades de offload úteis funcionam de forma fiável. Ajusto parâmetros de rede como o somaxconn e o tcp_max_syn_backlog de acordo com o número pretendido de ligações, para manter as filas de aceitação curtas. Um fornecedor com desempenho de E/S robusto e configuração do sistema de acesso livre compensa claramente neste contexto. Em comparações, verifica-se que os serviços com Recursos Aumentar significativamente as capacidades do NGINX.

Estratégia de keepalive: ligações do cliente e a montante

Utilizo o Keepalive deliberadamente como um instrumento para gerir a capacidade e a latência. Do lado do cliente, configuro tempo de espera de keepalive não demasiado elevado, para que os sockets inativos não sejam desnecessariamente ligações_trabalhadores bloquear. Valores entre 10 e 30 s costumam proporcionar-me um bom equilíbrio entre reutilização e consumo de recursos. Com keepalive_requests Limito o número de pedidos por ligação, para cortar as ligações de longa duração e evitar a sobrecarga de memória. No lado upstream (proxy inverso), mantenho ligações persistentes com keepalive no bloco upstream, de modo a eliminar os handshakes e a configuração do TCP. Para tal, defino o número por backend de forma conservadora, em função da capacidade do backend (max_conns), caso contrário, eu próprio crio filas no upstream. Importante: cada socket de keepalive conta como uma ligação aberta e necessita de FDs; tenho isso em conta em rlimit_nofile e o meu planeamento de headroom.

Otimização de listas: reuseport, backlog e estratégia de aceitação

Distribuo a carga de aceitação de forma uniforme, ao SO_REUSEPORT ativar (listar … reuseport). Cada worker dispõe assim da sua própria fila de aceitação, o que reduz os „thundering herds“ e evita pontos de congestionamento. Em combinação com multi_accept acelero sensivelmente a fase de aceitação. A lista deatraso (listen … backlog=) e os parâmetros correspondentes do kernel (somaxconn, tcp_max_syn_backlog) defino-os com uma margem generosa, para que os picos não se percam na entrada do socket. A opção diferido adiar o `Accept` até que os dados estejam disponíveis – no caso de muitas solicitações de curta duração, isso pode ajudar; caso contrário, comparo nos testes. Se eu accept_mutex O que preciso, determino no benchmark: com o reuseport, na maioria das vezes é dispensável; sem o reuseport, pode melhorar a equidade, mas implica um esforço de coordenação. Aqui, tomo decisões com base nos dados, nunca por intuição.

Configurar os tempos limite e as filas de forma estável

Eu fixo Intervalos de forma a que os clientes mais lentos não sobrecarreguem os workers: client_header_timeout e tempo limite do corpo do cliente Considero-o suficientemente conciso para evitar travamentos, mas suficientemente abrangente para os utilizadores reais. tempo_limite_de_envio protege contra respostas bloqueadas destinadas ao cliente. No contexto do proxy, defino tempo limite de ligação do proxy, tempo_limite_de_leitura_proxy e proxy_send_timeout rigoroso, para que os back-ends em espera não paralisem o front-end. No caso de back-ends com paralelismo limitado, utilizo fila no bloco upstream com um tempo de espera, para atenuar picos de tráfego e sinalizar o erro 503 de forma controlada, em vez de vincular todos os workers a sockets upstream em espera. Além disso, estabilizo com limite_req (Burst/Delay) e limite_conexão caminhos sensíveis, para que os clientes ou bots individuais não consumam recursos de forma desproporcionada.

Buffering, sendfile e AIO: escolher deliberadamente os métodos de E/S

Eu fixo sendfile para ficheiros estáticos e combina-o com tcp_nopush/tcp_nodelay dependendo da carga de trabalho, para agrupar pacotes de forma eficiente ou reduzir as latências interativas. Para ficheiros grandes, utilizo direção a partir de um determinado limiar, para evitar a poluição da cache e para que a cache de páginas não seja substituída. No modo proxy, sou eu que decido se proxy_buffering ajuda (transferência rápida para o cliente, leitura upstream desacoplada) ou se, no caso de cargas de streaming, é preferível que eu proxy_request_buffering reduza, para iniciar os uploads antecipadamente. Os tamanhos de proxy_buffers, proxy_buffer_size e buffers_de_cabeçalho_de_cliente_grandes controlo isso de forma consciente, para que o consumo de memória por ligação não dispare. Para um acesso aos ficheiros que não sobrecarregue a CPU, estou a considerar aio (nativo ou threads), mas faz testes exaustivos, porque as características do ciclo de eventos e das E/S influenciam-se mutuamente.

HTTP/2/HTTP/3 e TLS: impacto na capacidade dos workers

Tenho em conta que HTTP/2 e HTTP/3 Alterar a dinâmica das ligações: Muitas solicitações são executadas como Streams através de um número reduzido de ligações TCP ou QUIC. Isto reduz o número de ligações, mas aumenta os requisitos de CPU e memória por ligação (multiplexação, compressão de cabeçalhos, TLS/QUIC). A minha ligações_trabalhadores Por isso, não interpreto isso cegamente como „o mesmo número de pedidos“. Observo fluxos simultâneos por ligação e por peça tempo de espera de keepalive e, se necessário,. http2_max_concurrent_streams . No que diz respeito ao TLS, ganho com a retomada de sessão (tickets/cache) e o OCSP Stapling; assim, evito handshakes dispendiosos e mantenho as latências baixas. O reverso da medalha: os keepalives mais longos ocupam FDs e RAM – por isso, planeio rlimit_nofile e quotas de memória com reservas realistas. Para algoritmos de encriptação que exigem muito da CPU, vale a pena testar a afinidade e a aceleração criptográfica moderna.

Monitorização: estado, registos e métricas

Consigo garantir a transparência com um sistema simplificado Estado-Endpoint (por exemplo, stub_status), para ver as ligações ativas, os estados de leitura/gravação/espera e os pedidos aceites. Nos registos, procuro manter o ruído ao mínimo: um registo compacto log_format com a hora, o estado, os tempos de upstream e os bytes é suficiente para a maioria das análises. Em caso de QPS muito elevado, desativo o registo de acesso de forma seletiva (com base na localização) ou armazeno os registos em buffer de forma assíncrona, para que a E/S não cause atrasos. Defino o registo de erros para avisar ou erro e, apenas para análises específicas, mude temporariamente para depurar. Correlaciono continuamente as latências (mediana/95.º/99.º percentil), as ligações abertas, as taxas de erro do backend e a carga da CPU por worker – a partir daí, deduzo ajustes nas três diretivas-chave e deteto atempadamente efeitos de saturação.

Contentores e ambientes virtuais: passar os limites de forma clara

Estou a verificar, em contentores, os cgroup-Defina os limites para a CPU, a RAM e os PIDs e ajuste-os de acordo com as configurações do NGINX. ulimit -n tem de ser suficientemente elevado dentro do contentor, caso contrário, os meus ajustes no rlimit_nofile perdem o efeito. No caso de quotas de CPU (por exemplo, 2 vCPUs), defino processos_trabalhadores Consequentemente, para que a programação não force artificialmente o tráfego. Perto da rede, beneficio de uma menor latência de overhead nos modos de rede „host“, enquanto as sobreposições implicam saltos adicionais. Em hosts Multi-NUMA, presto atenção à afinidade e aos soquetes de memória, para que os workers não operem em nós diferentes. O mesmo se aplica à afinidade de IRQ e RPS/XPS: se os caminhos desde a NIC, passando pela IRQ, até ao núcleo do worker estiverem corretos, os picos de latência diminuem de forma mensurável.

Ciclo de vida da ligação: portas efémeras, TIME_WAIT e reservas

Tenho a intenção de... Portas efémeras (ip_local_port_range), quando o NGINX atua como cliente ativo em relação aos servidores upstream. Em caso de um débito de ligações muito elevado, evito a flutuação excessiva de portas através do „Upstream-Keepalive“, o que reduz as pilhas de TIME_WAIT. Recorro às opções do kernel para «Reuse» apenas com cautela; as pilhas modernas já otimizam grande parte do processo internamente. É mais estável controlar a duração das ligações através de valores adequados de keepalive e timeout e reutilização para garantir uma distribuição justa. No cálculo da capacidade, além dos clientes, tenho sempre em conta o lado a montante – muitas vezes, são os FD que constituem o verdadeiro fator limitante, e não a porta de entrada.

Reinicializações e implementações suaves, sem interrupções

Utilizo o modelo Master/Worker para recarregamentos suaves: O Master carrega novas configurações; os antigos Workers são desativados, enquanto os novos assumem o funcionamento de forma contínua. Com worker_shutdown_timeout Dou tempo às solicitações para terminarem corretamente, sem bloquear recursos. As implementações sem tempo de inatividade no upstream associo-as a verificações de integridade e proxy_next_upstream-Regras para garantir que os backends com falhas não aumentem a latência global. Quando faço alterações na configuração, altero sempre apenas um parâmetro e verifico os efeitos nos registos e nas métricas – assim, evito erros decorrentes de confusão e mantenho o desempenho reprodutível.

Resumo conciso

Estou a ligar processos_trabalhadores Defina o número de núcleos (idealmente em modo automático), ajuste o `worker_connections` de acordo com a carga de pico e aumente generosamente o `rlimit_nofile`, juntamente com os limites do sistema operativo. No bloco de eventos, utilizo o `epoll` e o `multi_accept`, verifico tudo com testes de carga reproduzíveis e, em seguida, faço ajustes em pequenos passos. Para cargas de trabalho de proxy, prevejo descritores adicionais e testo a afinidade da CPU quando as cargas de trabalho são constantes. Uma pilha bem configurada, com um kernel adequado, E/S rápida e parâmetros de rede sensatos, faz toda a diferença. É assim que consigo NGINX confiável no que diz respeito ao desempenho exigido pelas páginas e APIs mais exigentes.

Artigos actuais