...

Ligações dos Workers do NGINX – Escalabilidade de milhares de pedidos para um desempenho máximo de alojamento

Estou a dimensionar o nginx worker de forma específica para lidar com milhares de pedidos simultâneos com baixo Latência para utilizar. A chave reside numa combinação equilibrada entre worker_processes, worker_connections, descritores de ficheiros e Eventos.

Pontos centrais

  • Capacidade = worker_processes × worker_connections; no caso de um proxy inverso, frequentemente através da ligação do cliente e da ligação upstream duplicado.
  • Descritores de ficheiros (worker_rlimit_nofile, ulimit) de acordo com a carga de ligações prevista elevador.
  • Eventos-Bloqueio com epoll, multi_accept e backlogs do kernel em condições de carga elevada aparar.
  • Monitorização através do `stub_status` e de testes de carga para iterações Personalização.
  • Escalonamento Combinar na vertical e na horizontal, configuração dissociar.

Arquitetura do NGINX: Master, Worker e Eventos

O NGINX utiliza um processo principal que inicia vários processos de trabalho e os gere de forma eficiente com Eventos fornece. Em vez de processar threads por pedido, cada worker processa inúmeras ligações de forma não bloqueante através de um modelo baseado em eventos com baixo Despesas gerais. Defino a diretiva `worker_processes` como `auto`, para que o NGINX aproveite os núcleos da CPU e cada unidade tenha o seu próprio worker. Desta forma, distribuo melhor as ligações recebidas e mantenho a latência baixa durante os picos de tráfego baixo. Para uma abordagem mais aprofundada do planeamento dos processos, remeto para Otimizar os processos «Worker», pois uma paralelização correta determina a capacidade de ligação viável. É fundamental que o parâmetro «worker_connections» seja dimensionado de forma adequada para cada «worker», para que a multiplicação por número de processos resulte no valor esperado Carga de pico cobre.

Fórmula de capacidade: worker_processes × worker_connections

Calculo a capacidade aproximada com worker_processes × worker_connections, tendo em conta que os pedidos redirecionados ocupam frequentemente duas ligações por acesso de utilizador, o que reduz o número efetivo para metade pode. Muitas instalações padrão começam com 512 ligações por worker, o que, para cargas de trabalho produtivas, é frequentemente insuficiente é. Os valores iniciais práticos situam-se normalmente entre 1024 e 4096 e dependem do perfil de tráfego e do hardware. Planeio com margem de segurança, ou seja, pelo menos o dobro da carga de pico medida, para garantir a gestão segura dos picos de tráfego amortecer. Continua a ser importante a validação através de testes e métricas em tempo real, para que os números não se tornem um mero exercício teórico tornar-se.

Cenário processos_trabalhadores ligações_trabalhadores Teoricamente, no máximo. Efetivo (Proxy) FD por trabalhador
Pequeno site 2 1024 2048 ~1024 ≥1024
API com carga média 4 2048 8192 ~4096 ≥2048
Horários de maior afluência na loja 8 4096 32768 ~16384 ≥4096

HTTP/1.1, HTTP/2 e TLS: impacto nos workers e na latência

Os protocolos determinam o perfil da ligação. Com o HTTP/1.1, observo frequentemente muitas ligações TCP simultâneas por cliente, enquanto o HTTP/2 as reduz a um número reduzido de fluxos, que, por sua vez, são mais intensamente utilizados feixes. Isto poupa descritores de ficheiros, mas transfere a carga para os buffers e para a priorização. No TLS, presto atenção à reutilização de sessões, para que não seja necessário realizar handshakes dispendiosos em cada pedido abrandar. Uma cache de sessões partilhada e tempos de espera adequados reduzem os picos de utilização da CPU. Além disso, não defino o valor de keepalive_requests demasiado baixo, para que as ligações de longa duração possam proporcionar as suas vantagens jogar fora. No caso do HTTP/2, calculo uma maior simultaneidade por ligação e garanto que os buffers de envio/receção tenham dimensão suficiente, sem ocupar memória desperdiçar. No caso de tráfego misto, faço um planeamento conservador e verifico os efeitos por variante de protocolo no Teste.

Definir corretamente os descritores de ficheiros e o ulimit

Cada ligação necessita de, pelo menos, um descritor de ficheiro; no caso de um proxy reverso, são frequentemente necessários dois, razão pela qual valores baixos de ulimit podem causar sérios Limites definir. Aumento o valor de `worker_rlimit_nofile` de forma a que `worker_processes` × `worker_connections` seja viável e haja reservas para registos, sockets e caches. A nível do sistema, ajusto os ficheiros `limits.conf` e `fs.file-max` para que o sistema operativo permita o número planeado de ficheiros abertos e não termine prematuramente travões. Através do comando `ulimit -n` e dos parâmetros do Systemd (LimitNOFILE), verifico se a configuração se mantém e se está adequada ao NGINX. Quem ignorar este ajuste irá, apesar de um valor elevado de `worker_connections`, deparar-se subitamente com ligações rejeitadas e um aumento Latências.

Ajustar com precisão o bloco de eventos: epoll, multi_accept, backlogs

No Linux, utilizo o epoll, uma vez que este mecanismo lida de forma eficiente com um grande número de ligações através de Eventos gerencia. Com a opção «multi_accept» definida como «on», um trabalhador aceita várias novas ligações por evento, o que atenua os picos de carga e os atrasos na aceitação baixa. Aumento os parâmetros do kernel, como net.core.somaxconn e net.ipv4.tcp_max_syn_backlog, de forma adequada, para que as filas de aceitação não transbordem durante picos de tráfego. As otimizações TIME_WAIT, como o tcp_tw_reuse, reduzem os estrangulamentos nas portas e mantêm a curva de débito elevado. Para uma análise mais aprofundada sobre a multithreading e as filas, vale a pena consultar Otimização do pool de threads, mesmo que o NGINX funcione principalmente com base em eventos e, por isso, seja muito eficiente escalonado.

Distribuir corretamente os soquetes de lista: reuseport, backlog e accept_mutex

Quando há um grande número de ligações simultâneas, procuro dimensionar ativamente a via de receção. Com reutilização Cada worker recebe o seu próprio socket de escuta; assim, elimina-se a concorrência na função `accept` e a carga é distribuída de forma equilibrada por todos os núcleos. Defino explicitamente o backlog de escuta para absorver picos de tráfego de curta duração. O `accept_mutex` já não é necessário nesta configuração. Sem o `reuseport`, por outro lado, o `accept_mutex` pode ajudar, atenuar os efeitos de manada no «Accept». Importante: os tamanhos do backlog no NGINX e no kernel (somaxconn) devem encaixar, caso contrário, o efeito será nulo.

events {
    use epoll;
    worker_connections 4096;
    # accept_mutex on;   # com reuseport, na maioria das vezes, não é necessário
}

server {
    listen 443 ssl http2 reuseport backlog=65535;
    # ...
}

Além disso,, sempre que necessário, atribuo os workers a núcleos da CPU (worker_cpu_affinity), para que as linhas de cache e a carga de IRQ se mantenham estáveis. Em ambientes fortemente caracterizados por NUMA, isto reduz o Tráfego transversal na memória.

Proxy reverso, upstreams e Keep-Alive

Enquanto proxy reverso, o NGINX mantém frequentemente duas ligações por cada pedido: uma com o cliente e outra com o backend, o que torna o planeamento da capacidade mais realista duplo é importante. Ativo o Keep-Alive de forma adequada, para que as ligações upstream possam ser reutilizadas e a sobrecarga por pedido diminuições. Desta forma, reduzo a carga no PHP-FPM, no servidor de aplicações ou nos microsserviços e liberto slots para novas sessões de utilizador. O equilíbrio entre os tempos de espera, o tempo de inatividade e a reutilização determina a eficácia com que as ligações são recicladas tornar-se. Quem quiser ler mais sobre os conceitos básicos, pode consultar em Ligações persistentes conselhos práticos sobre a utilização da capacidade e a otimização da redeUse.

Grupos de upstream, tempos limite e tentativas de repetição

Para que os workers não tenham de esperar por backends lentos, mantenho tempos de espera reduzidos e tentativas de reenvio bem dosadas. Mantenho os conjuntos de keepalive a montante com dimensão suficiente para que as ligações se mantenham ativas, mas não tão grandes que os FD inativos ocupem memória e slots vincular. Limito as tentativas de repetição a um número reduzido e só mudo de conexão em caso de erros de transporte evidentes – assim evito o efeito «thundering herd» em caso de pequenas falhas no backend.

upstream app_backend {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
    keepalive 64;  Conexões upstream reutilizáveis #
}

server {
    location / {
 proxy_pass http://app_backend;
 proxy_connect_timeout   2s;
        proxy_read_timeout 15s;
 proxy_send_timeout 15s;
 proxy_next_upstream     error timeout http_502 http_503;
 proxy_next_upstream_tries 2;
    }
}

Ao mesmo tempo, ajusto os parâmetros de Keep-Alive (tempos de espera, pedidos por ligação) para libertar rapidamente os recursos dos clientes que raramente estão ativos autorizar.

Planear a escalabilidade de forma sensata: combinar a escalabilidade vertical e horizontal

Para volumes de tráfego elevados, prefiro combinar o escalonamento vertical e o horizontal em Considera. Faço a escalabilidade vertical através de mais núcleos de CPU, RAM, SSDs rápidos e uma configuração de rede otimizada, para que cada worker funcione de forma fluida obras. Em termos horizontais, faço a expansão com nós NGINX sem estado, configuração gerida centralmente e registo distribuído, para que a capacidade total aumente linearmente cresce. As caches locais e as políticas bem definidas através do Maps ou da API permitem implementar alterações rapidamente. Esta separação reduz os efeitos colaterais e ajuda a adaptar-se a novos padrões de tráfego sem necessidade de alterações em cada nó servir.

Perspetiva de alojamento: latência, taxas de erro e experiência

Um número insuficiente de «worker_connections» resulta em ligações rejeitadas, tempos de espera e um desempenho inferior Experiência do utilizador. As aplicações dinâmicas, como os CMS ou as lojas online, sentem isso imediatamente, porque cada visualização de uma página gera várias solicitações ao backend e os slots esgotam-se mais rapidamente curto serão. Por isso, começo com valores moderados, como 1024 ou 2048 por worker, e vou aumentando-os gradualmente com base em valores de medição reais. Paralelamente, mantenho os serviços a montante a funcionar de forma eficiente e garanto que haja descritores de ficheiros suficientes, para que não surjam Limites aplicar. Os testes de desempenho demonstram que as plataformas cuidadosamente otimizadas oferecem vantagens reais neste contexto e gerem o tráfego de pico de forma fiável interceção.

Memória, armazenamento em buffer e percursos de E/S

Cada ligação ocupa memória de trabalho para metadados e buffers. Defino os valores de proxy_buffers, client_body_buffer_size e large_client_header_buffers de forma a que as solicitações típicas caibam, sem alocar demasiada RAM de forma generalizada para o caso de valores atípicos vincular. No caso de conteúdos estáticos, as opções «sendfile» e «tcp_nopush» aceleram a entrega, enquanto a opção «tcp_nodelay» é indicada para respostas curtas em que a latência é um fator crítico Importante permanece. Se os ativos estiverem armazenados num suporte de armazenamento mais lento, o `aio threads` e o `thread_pool` ajudam a atenuar os efeitos de bloqueio. Com o `open_file_cache`, reduzo os acessos aos ficheiros e as chamadas ao `stat()`, mas tenho de ter em conta a necessidade adicional de FD. Gravo os registos em buffer (access_log … buffer=… flush=…), para que os picos de E/S não afetem os tempos de resposta influenciar.

Equilíbrio entre segurança e desempenho do TLS

Os handshakes TLS exigem um grande consumo de CPU. Combino a reutilização de sessões com parâmetros de chave moderados e ativo otimizações acumuláveis, como caches de sessão e tickets, desde que sejam operacionais apto. O equilíbrio ideal entre segurança e desempenho mantém as latências estáveis, sem comprometer a qualidade da encriptação. Sob cargas mais elevadas, analiso separadamente os percentis 95 e 99, uma vez que, de outra forma, os picos de TLS ficariam ocultos por trás dos valores médios esconder. O HTTP/2 reduz o número de ligações, mas exige cuidado no controlo de fluxo e na compressão de cabeçalhos, para manter sob controlo os perfis de CPU e de memória manter.

Resiliência sob sobrecarga: limites e libertação gradual

Para manter a latência, é necessário um Modelagem indispensável em picos de carga. Com o `limit_conn`, limito as ligações paralelas por chave (por exemplo, IP ou sessão); o `limit_req` reduz a carga de picos e protege os backends contra operações síncronas Tempestades. Isolamos os pontos críticos com regras mais rigorosas do que os recursos estáticos. Se ocorrer um pico de tráfego de curta duração, devolvemos códigos de erro 429/503 bem definidos com «Retry-After», em vez de distribuir todas as solicitações de forma uniforme morrer de fome deixar. Suspendo as ligações persistentes (lingering_close) para libertar recursos de forma controlada e evitar padrões do tipo Slowloris refutar. Este «shedding» ativo mantém a latência p95/p99 dentro dos limites aceitáveis, mesmo quando a procura total ultrapassa, pontualmente, a capacidade nominal mentiras.

Integração de contentores e sistemas: eliminar os limites lá onde surgem

Nos contentores, aplicam-se frequentemente limites mais restritos. Verifico os limites do cgroup (CPU, RAM), defino o `ulimit -n` adequadamente dentro do contentor e incorporo o `LimitNOFILE` na definição do serviço. Os parâmetros `sysctl`, como `somaxconn` e `tcp_max_syn_backlog`, têm de ser definidos no Anfitrião entrem em vigor; os namespaces nem sempre isolam estas configurações de forma transparente. Em plataformas orquestradas, planeio a capacidade por pod/nó, atribuo os workers a núcleos específicos e procuro garantir percursos de rede estáveis (por exemplo, sem saltos NAT desnecessários), para que a curva de latência calmo permanece. Acompanho as atualizações contínuas com o `worker_shutdown_timeout`, para que as ligações existentes sejam encerradas de forma correta chegar ao fim.

Monitorização e otimização iterativa

Sem visibilidade, as etapas de afinação continuam a ser Risco. Ativo o `stub_status` ou alternativas para monitorizar continuamente as ligações ativas, as taxas de aceitação e as rejeições. Nos testes de carga, simulo padrões de acesso realistas e identifico pontos de estrangulamento nas filas de aceitação, nas latências a montante ou na CPU-Saturação. Em seguida, ajusto cuidadosamente o `worker_connections`, os processos, os limites de ficheiros e os parâmetros TCP e verifico novamente o efeito. Este ciclo mantém a plataforma fiável e evita surpresas em momentos inoportunos Tempos.

Configuração de exemplo e método de cálculo

Supondo que, nos horários de pico, eu espere 2000 pedidos em curso simultâneos e utilize um proxy inverso, calculo, aproximadamente, 4000 slots de ligação mais Tampão. Se o NGINX estiver a funcionar em quatro núcleos de CPU, começo com, por exemplo, `worker_processes auto` e `worker_connections` entre 1000 e 2000 por processo de trabalho. Defino o limite de descritores de ficheiro por processo de trabalho num valor suficientemente elevado para que as ligações, os registos e os sockets internos tenham espaço suficiente Local tenho. Configurei o bloco de eventos para epoll, ativei o multi_accept e aumentei os backlogs do kernel de acordo com o meu tráfego de pico. Um trecho minimalista pode ter este aspeto, que depois ajusto com testes de desempenho Votação:

worker_processes  auto;
worker_rlimit_nofile  65535;

events {
    use epoll;
    worker_connections  2048;
    multi_accept on;
}

http {
    keepalive_timeout   65;
    sendfile on;
    # outras opções de proxy/cache ...
}

Além disso, estou a implementar otimizações nas listas e no upstream para aperfeiçoar o percurso de aceitação e o percurso do backend sob carga:

events {
    use epoll;
    worker_connections 4096;
    # worker_cpu_affinity auto;  # atribuir núcleos fixos, se necessário
}

http {
    # Otimizações de TLS/sessão a título de exemplo
    ssl_session_cache    shared:SSL:50m;
    ssl_session_timeout  1h;

 upstream app_backend {
 server 10.0.0.11:8080;
 server 10.0.0.12:8080;
 keepalive 64;
    }

    server {
 listen 443 ssl http2 reuseport backlog=65535;

 location / {
 proxy_pass http://app_backend;
 proxy_connect_timeout     2s;
            proxy_read_timeout 15s;
 proxy_next_upstream error timeout http_502 http_503;
 proxy_next_upstream_tries 2;
 }
    }
}

Em resumo: valores de referência concretos

Ajusto o valor de `worker_processes` de acordo com os núcleos da CPU e, normalmente, defino o valor de `worker_connections` entre 1024 e 4096. No caso do proxy reverso, prevejo duas ligações por pedido e mantenho, pelo menos, o dobro da margem em relação ao pico medido deCarga. Defino o `worker_rlimit_nofile`, bem como os limites a nível do sistema, a um valor suficientemente elevado para que os valores do ficheiro `nginx.conf` continuem a ser realmente utilizáveis. No bloco `Events`, limito-o ao `epoll` e ao `multi_accept`, enquanto os backlogs do kernel absorvem breves picos de tráfego amortecer. Através da monitorização e de ajustes graduais, transformo isso num motor de tráfego fiável, capaz de gerir de forma eficaz o aumento do número de visitas transporta.

Artigos actuais