...

MPM Event do Apache vs MPM Worker: um «turbo» moderno para servidores web com cargas elevadas

Vou explicar, em duas frases, por que razão a escolha do Apache MPM influencia visivelmente o débito, a latência e a estabilidade sob carga elevada. Para tal, comparo concretamente o Event MPM e o Worker MPM no que diz respeito a ligações Keep-Alive prolongadas, HTTP/2 e elevado paralelismo, e deduzo daí recomendações claras de otimização.

Pontos centrais

Para que possas compreender imediatamente as ideias mais importantes, vou resumir brevemente os pontos-chave e destacar as palavras-chave decisivas a negrito. A partir destes pontos, deduzo mais abaixo passos práticos e configurações, que explico de forma prática. Avalio ambos os MPMs de forma consistente, utilizando perfis de carga realistas com muitas ligações. Assim, poderás perceber rapidamente qual o módulo que se destaca na tua pilha. A lista fornece um atalho para decisões fundamentadas no funcionamento diário.

  • Evento Desacopla o Idle-Keep-Alive dos threads de pedidos e adapta-se a um grande número de ligações.
  • Trabalhador Destaca-se em pedidos curtos, mas consome threads em casos de Keep-Alive prolongado.
  • HTTP/2 beneficia-se de forma mensurável do Event graças a um tratamento eficiente do multiplexing.
  • Recursos: O evento mantém os consumos de RAM e CPU por pedido ativo mais baixos.
  • Compatibilidade: Os módulos seguros para threads são obrigatórios; o mod_php continua a ser um ambiente Prefork.

Por que é que o Worker e o Event estão na frente

Numa empresa moderna, aposto claramente em Tópicos, porque ocupam menos RAM por ligação do que os processos. O Prefork oferecia anteriormente segurança com módulos não seguros para threads, mas tem dificuldade em escalar quando há muitas ligações. Atualmente, os modelos «Worker» e «Event» dominam, pois gerem de forma eficiente um grande número de utilizadores simultâneos. Isto compensa especialmente com o Keep-Alive ativo e o HTTP/2, onde as ligações permanecem abertas durante muito tempo. É precisamente aí que se verifica Evento os seus pontos fortes, uma vez que não ocupa threads de pedidos importantes com ligações inativas.

MPM Apache Worker: arquitetura e limitações

Defino os «workers» como um híbrido entre processos e Tópicos, em que cada processo filho possui um thread de escuta e vários threads de servidor. Um pedido é encaminhado para um thread, é respondido e, em seguida, o thread é libertado. Se a ligação permanecer aberta, a mesma thread permanece ligada a essa ligação. Isto causa inatividade quando muitos clientes esperam durante muito tempo ou enviam apenas pequenas solicitações esporádicas. Quem utiliza o Worker deve, por isso, dimensionar conscientemente os conjuntos de threads e os limites e pode consultar o meu breve Otimização do pool de threads utilizar como ponto de partida.

MPM Event do Apache: explicação do ciclo de eventos

Descrevo o Event como um «Worker mais Event Loop», ou seja, Ouvinte-Threads que colocam as ligações inativas em espera. O «listener» aceita novas ligações, encaminha os pedidos ativos para threads de trabalho livres e, em seguida, recupera a ligação. Desta forma, as threads de pedido só funcionam quando há fluxo de dados. Centenas ou milhares de clientes podem, por isso, permanecer abertos sem bloquear as threads. É precisamente isto que Estacionamento é o que torna o Event tão eficiente em cargas de trabalho típicas do HTTP/1.1 e do HTTP/2.

Evento vs. Trabalhador: diferenças sob carga

Avalio sempre ambos os MPMs em condições reais Carga com tempos de Keep-Alive prolongados. O Worker atinge rapidamente o limite, porque as ligações inativas ocupam threads que, por sua vez, deixam de estar disponíveis para novos pedidos. O Event mantém os conjuntos de threads livres e encaminha as ligações inativas para o ciclo de eventos. Desta forma, o número de utilizadores que podem ser atendidos simultaneamente aumenta significativamente, enquanto as latências se mantêm estáveis. Quem precisar de bases para a tomada de decisões deve, de preferência, comparar casos concretos Modelos de servidor orientados por eventos com conjuntos de threads em testes de carga.

Compatibilidade: módulos e configurações típicas

Primeiro verifico o Módulos, uma vez que o Worker e o Event exigem segurança de threads. As pilhas clássicas do mod_php não se adequam, razão pela qual o Prefork continua a fazer sentido neste caso. Por outro lado, se o PHP for executado através do PHP-FPM ou do FastCGI, opto claramente pelo Event. O mesmo se aplica a proxies reversos para servidores de aplicações, microsserviços ou back-ends Go/Node. Nessas configurações, o «Worker» e, sobretudo, Evento a sua resistência sem comprometer a compatibilidade.

Configuração: As diretivas mais importantes

Apresento-te as diretrizes-chave de forma concisa, para que as possas compreender bem e personalizado. O MaxRequestWorkers limita o número de pedidos processados em simultâneo; no caso do Event, muitas vezes é possível aumentar esse valor, uma vez que as ligações inativas não bloqueiam o sistema. O ThreadsPerChild define o número de threads por processo; um valor demasiado baixo reduz o rendimento, enquanto um valor demasiado alto sobrecarrega a CPU. O `ServerLimit` define o limite de processos e, consequentemente, o limite máximo de pedidos paralelos no conjunto. Com o `KeepAliveTimeout`, controla-se durante quanto tempo as ligações permanecem abertas; quanto maior for o valor, maior será o benefício Evento.

Comparação tabular: Worker vs. Event

Vou resumir as características mais importantes numa forma concisa Tabela juntos, para que possas ver as diferenças imediatamente. Não substitui um teste de carga, mas ajuda a estruturar a tua visão sobre as características essenciais. Lê os pontos da esquerda para a direita e associa-os ao teu perfil de tráfego. Assim, encontrarás rapidamente o MPM adequado para a tua arquitetura. O foco está claramente na escalabilidade, nos requisitos de recursos e no comportamento com Manter em permanência.

Critério Worker MPM Evento MPM efeito
Gestão do Keep-Alive O thread permanece associado à ligação As ligações inativas são colocadas em espera pelo ciclo de eventos O evento mantém as threads de pedidos livres
Utilização de recursos Mais threads ocupados em modo inativo Menos threads ocupados em estado de inatividade Menor consumo de RAM/CPU por pedido ativo
Latência sob carga Sai mais cedo Mantém-se estável durante mais tempo Melhor resposta
Compatibilidade com HTTP/2 Organizado Muito eficiente Vantagens da multiplexação
Configuração MaxRequestWorkers, ThreadsPerChild, ServerLimit Iguais, mais otimização do ciclo de eventos O evento permite uma maior utilização da capacidade
Compatibilidade São necessários módulos à prova de threads Da mesma forma, de preferência com o PHP-FPM O Prefork continua a ser uma opção do mod_php

Na prática: fluxo de trabalho de afinação e medição

Começo sempre com uma linha de base limpa Monitorização e dados de registo. Em seguida, vou variando gradualmente os parâmetros `MaxRequestWorkers` e `ThreadsPerChild` e medo a latência, a taxa de erros e a carga da CPU. Testo o `KeepAliveTimeout` em etapas, porque o tempo ideal depende muito do comportamento do cliente. A partir daqui, vale a pena comparar Event vs. Worker com ferramentas como ab, wrk ou JMeter. Só quando as métricas parecerem corretas é que fixo o Perfis e registo os indicadores.

Quando é que o Prefork continua a fazer sentido

Recorro ao Prefork quando não é absolutamente necessário utilizar código seguro para threads Módulos têm de ser executados. Nesse caso, o isolamento por processo é mais importante do que a escalabilidade. Em contrapartida, aceito um consumo de RAM significativamente mais elevado por ligação. Para aplicações legadas sem possibilidade de adaptação, esta continua a ser, muitas vezes, a opção mais realista. No entanto, assim que utilizo o PHP-FPM ou outros servidores de aplicações externos, opto por Evento de forma clara.

Contexto da hospedagem web e escolha do fornecedor

No contexto da hospedagem, presto atenção aos perfis MPM, porque, muitas vezes, existe um grande número de hosts virtuais numa mesma máquina correr. O Event permite aqui a utilização mais eficiente dos recursos, especialmente com HTTP/2 e TLS. Se a minha pilha de tecnologias exigir o PHP-FPM, defino o Event como padrão. Para contextualização e verificação técnica, é útil um breve Comparação entre Prefork, Worker e Event antes da escolha final. Quem fizer estes trabalhos de casa obtém resultados visivelmente melhores Tempos de resposta por euro.

Compacto de boas práticas

Utilizo sempre PHP-FPM ou outros servidores de aplicações externos, para que o Event possa atingir o seu pleno potencial. Em seguida, ajusto os parâmetros MaxRequestWorkers e ThreadsPerChild de acordo com os núcleos da CPU e a RAM, e verifico os limites máximos do sistema. No caso de muitos clientes inativos, opto pelo Event, defino deliberadamente o KeepAliveTimeout para um valor mais elevado e monitorizo as latências. Para cargas de trabalho com pedidos muito curtos e um Keep-Alive moderado, o Worker é suficiente, desde que os módulos continuem a ser seguros em termos de threads. Sem uma monitorização contínua da utilização dos threads, dos erros e Latências não tomo decisões definitivas.

Exemplos concretos de configuração para o Event e o Worker

Apresento dois perfis minimalistas que utilizo como ponto de partida e que, posteriormente, aperfeiçoo com base nos valores medidos. É fundamental: MaxRequestWorkers = ServerLimit × ThreadsPerChild. Faço o cálculo a partir do orçamento de RAM e das necessidades por thread (incluindo módulos, TLS e buffers) e vou aumentando gradualmente.

Exemplo #: Evento MPM (HTTP/2, PHP-FPM)
ServerLimit 16
ThreadLimit 256
ThreadsPerChild 64
MaxRequestWorkers     1024
StartServers 4
MaxConnectionsPerChild 10000

KeepAlive On
MaxKeepAliveRequests  100
KeepAliveTimeout 15

# Opcional e ajustar apenas após medição:
# ListenBacklog 1024
# ThreadStackSize     1048576   # 1 MB, apenas se os módulos o permitirem
# AsyncRequestWorkerFactor 2    # Ajuste fino do ciclo de eventos, na maioria das vezes mantém-se o valor por predefinição

# HTTP/2
Protocolos h2 http/1.1
# H2MaxSessionStreams  100-200  # ajustar com precisão consoante a capacidade do backend
Exemplo #: Worker MPM (pedidos curtos, Keep-Alive moderado)
ServerLimit 8
ThreadLimit 256
ThreadsPerChild 50
MaxRequestWorkers     400
StartServers 4
MaxConnectionsPerChild 5000

KeepAlive On
MaxKeepAliveRequests  100
KeepAliveTimeout 3
Protocols http/1.1

Eu seguro MaxConnectionsPerChild (Alias: MaxRequestsPerChild) diferente de 0, para detetar fugas de memória graduais. Tempo de espera de manutenção de conexão Defino-o deliberadamente mais alto no «Event», porque as ligações inativas são económicas; no «Worker», mantenho-o baixo para não bloquear os threads.

Ajuste fino do HTTP/2 com o Event

Tenho em conta ao HTTP/2, que os navegadores abrem poucas ligações e muitas Streams multiplexar. Desta forma, o gargalo passa do número de ligações para uma distribuição justa de threads e para a capacidade do backend. Com o Event, os threads permanecem livres enquanto um stream estiver em espera; isto atenua os picos de latência. Ferramentas práticas:

  • H2MaxSessionStreams: Normalmente, movo-me na faixa dos 50–200. Um valor demasiado elevado gera efeitos «head-of-line» no backend; um valor demasiado baixo desperdiça o paralelismo.
  • MaxRequestWorkers: Com o Event, posso aumentar a carga, desde que a RAM e a CPU o permitam. Estou a monitorizar os percentis 95 e 99 da latência à medida que o paralelismo aumenta.
  • TLS: Com o ALPN e conjuntos de encriptação modernos, reduzo os custos do handshake; o evento beneficia ainda mais, uma vez que as fases de inatividade entre os picos de transmissão são geridas de forma eficiente.

Limites do sistema operativo e backlogs de sockets

Antes de cada teste de carga, verifico os limites do sistema; caso contrário, não é o MPM que impõe restrições, mas sim o kernel. Para um número elevado de ligações, procuro, em particular, escalar:

  • Descritores de ficheiros: ulimit -n e systemd LimiteNOFILE Aumento, por exemplo, para 65536 ou mais; o Apache precisa de FD por socket, log e pipe.
  • Atraso: net.core.somaxconn e tcp_max_syn_backlog Defino um valor adequado (por exemplo, 1024–4096), para que a fila de aceitação não fique sobrecarregada.
  • Área de portos (no caso de um proxy reverso): ip_local_port_range aumentaria (por exemplo, 10 000–65 000) se houvesse muitas ligações de saída simultâneas para os back-ends.
  • FIN/Intervalos: Cuidado com tcp_fin_timeout: se for demasiado agressivo, pode causar interrupções na ligação; só faço alterações com base nas medições.

Registo cada ajuste no kernel, juntamente com a justificação, e verifico-o através de uma nova medição da carga. Sem comprovação, a configuração predefinida é, na maioria das vezes, a correta.

Monitorização e resolução de problemas no dia-a-dia

Eu ativo ExtendedStatus e utilizo o «server-status» para verificar o Placar-ler os estados. Na secção „Event», vejo muitos sockets inativos/keep-alive, sem que os threads de trabalho estejam a ser utilizados na totalidade. No registo de erros, aparece «server reached MaxRequestWorkers Quando aparece a mensagem “setting, consider raising the MaxRequestWorkers setting», o servidor já está a funcionar no limite; aumentei o valor com cautela e observei a utilização da RAM e da CPU, bem como a taxa de erros.

  • Campos de medição: Nos registos de acesso, registo os tempos de resposta (por exemplo, %D/%T), os códigos de estado e os bytes; estabeleço uma correlação entre os picos e a CPU/IO.
  • Sintomas no Worker: Muitas ligações Keep-Alive inativas, threads ocupados no 100 %, latência crescente, 503/504 – indício de threads bloqueados.
  • Sintomas durante o evento: Os threads de escuta estão sobrecarregados, mas os threads de trabalho estão livres – na maioria das vezes, trata-se de um limite da rede ou do backend, e não do MPM.
  • Graceful-Reload: Estou a implementar as alterações apachectl -k graceful para que as ligações existentes possam escoar corretamente.

Planeamento de capacidade: dos núcleos e da RAM ao MaxRequestWorkers

Faço as contas de forma pragmática: quanta RAM por thread, mais o buffer, pretendo atribuir? No caso do TLS, dos filtros e dos módulos mais comuns, faço um cálculo conservador de alguns MB por thread. Depois, defino MaxRequestWorkers de forma a que os picos de carga nos percentis 95 e 99 sejam geridos sem swap. Ao nível da CPU, aplica-se o seguinte: os threads que excedem o número de núcleos só ajudam desde que não sejam constantemente exigentes em termos de tempo de execução. No caso do Event, atrevo-me a utilizar valores mais elevados, porque as fases de inatividade quase não têm custo.

  • Regras gerais: Iniciar com 32–64 threads por processo, 4–16 processos; em seguida, proceder à medição e ao ajuste.
  • ThreadStackSize: Se a RAM estiver escassa e os módulos o permitirem, reduzo o tamanho da pilha (com cuidado, realizando um teste de carga).
  • Máximo de pedidos mantidos ativos: Normalmente mantenho a configuração predefinida; no caso de clientes «chatty», um valor mais elevado pode reduzir a sobrecarga.

Cenários de proxy inverso e ligações ao backend

Gosto particularmente de utilizar o Event em back-ends de aplicações, porque Tomadas frontais estaciona de forma eficiente, enquanto o trabalho propriamente dito decorre no backend. O que é decisivo, então, é a agregação dos Ligações ao backend (mod_proxy):

  • Keep-Alive para o backend: Manter ativo para poupar handshakes; tamanho dos pools (max (por destino), de acordo com a capacidade do backend.
  • Tempos de espera do proxy: Definir claramente os tempos de espera, para que os backends bloqueados não ocupem threads do frontend.
  • HTTP/2 para o backend: Sempre que possível, utilizo o H2 (por exemplo, o h2c interno) para reduzir o número de ligações e aumentar o número de fluxos – o Event funciona bem com isso.

Analiso especificamente as percentagens de latência entre o front-end e o back-end; se apenas o tempo do back-end aumentar, o ajuste do MPM por si só não é suficiente – nesse caso, tenho de ajustar os tamanhos dos pools, os tempos de espera ou os recursos do back-end.

Estratégia de implementação e migração do «Worker» para o «Event»

Estou a fazer a migração em etapas claras: primeiro, verifico o Lista de módulos (apachectl -M) para verificar a segurança de threads. Tudo o que não for seguro para threads (como o mod_php clássico) tem de ser removido ou isolado. Depois, ativo o Event, defino valores iniciais conservadores e realizo testes de carga no ambiente de staging. Na implementação, começo com uma parte do tráfego (Canary), comparo as métricas e só depois procedo à implementação em grande escala.

  • comandos: Conforme é habitual nesta distribuição, alternar entre os módulos MPM (por exemplo, a2dismod/a2enmod) e reiniciar corretamente.
  • Plano de contingência: Tenho um perfil de worker preparado, para o caso de algum módulo apresentar um comportamento anómalo na secção «Event».
  • Documentação: Documento todas as alterações aos limites, aos parâmetros HTTP/2 e aos valores do kernel com medições «antes» e «depois».

Segurança e desempenho do TLS em destaque

No que diz respeito ao TLS, tenho em conta que os handshakes consomem muitos recursos da CPU e podem aumentar a latência em condições de carga elevada. Com Reinício da sessão e, através da seleção de algoritmos de encriptação modernos, reduzo os custos, ao mesmo tempo que otimizo de forma eficiente os períodos de inatividade. Em combinação com o HTTP/2 e o ALPN, evito viagens de ida e volta adicionais. Importante: os buffers TLS e os parâmetros do OpenSSL fazem parte da pegada de RAM por thread – tenho-os em conta no planeamento da capacidade.

Tolerância a falhas e degradação gradual

Estou a planear para situações de sobrecarga: se a CPU estiver saturada ou se o Apache atingir MaxRequestWorkers, não quero uma avalanche de tentativas de repetição. Defino tempos de espera claros, páginas de erro informativas e limites de taxa nos proxies a montante. Com o Event, mantêm-se mais sob pressão Tópicos disponíveis para trabalho efetivo, enquanto as ligações inativas ficam em espera – é precisamente esta reserva que mantém o sistema operacional por mais tempo, até que a carga volte a diminuir ou o dimensionamento automático entre em ação.

Brevemente resumido

Na minha atividade atual, aposto em Evento, assim que a minha pilha utilizar módulos seguros para threads e o PHP-FPM. Esta abordagem reduz o número de threads ocupadas em ligações inativas, mantém o tempo de resposta estável e aumenta o número de utilizadores atendidos em paralelo. O «Worker» continua a ser uma opção sólida para pedidos curtos com «Keep-Alive» moderado, quando o «Event» não é adequado por motivos organizacionais. Reservo o «Prefork» para configurações com módulos não seguros para threads ou código antigo. Com testes de carga claros, um ajuste preciso das diretivas e um Monitorização Consegui fazer com que o Apache atingisse a velocidade máxima de forma reproduzível.

Artigos actuais