Melhor Performance com PHP-FPM

O PHP-FPM (ou Fast Process Manager) oferece várias vantagens sobre o mod_php, por ser mais flexível e por ser uma implementação alternativa de PHP FastCGI amplamente usada e de alto desempenho. No entanto, se você estiver usando as configurações padrão do gerenciador de pacotes, provavelmente não aproveitará ao máximo.

Apresentaremos aqui uma breve visão geral sobre como melhorar o desempenho do PHP-FPM, discutindo os três tipos de gerenciadores de processos do PHP-FPM e qual é o melhor para usar em qual circunstância.
O PHP-FPM pode usar um dos três tipos de gerenciamento de processos :

  • estático
  • dinâmico
  • sob demanda

Vamos ver o que cada um é em um pouco de detalhe.

Estático ( static )

Estático garante que um número fixo de processos filho esteja sempre disponível para lidar com as solicitações do usuário. Isso é definido com pm.max_children. Nesse modo, as solicitações não precisam esperar pela inicialização de novos processos, o que a torna a abordagem mais rápida.

Supondo que você queira usar a configuração estática com 10 processos filhos sempre disponíveis, você a configuraria /etc/php/8.1/fpm/pool.d/www.conf (assumindo que você está usando o arquivo de configuração padrão do PHP-FPM do Debian/Ubuntu) da seguinte forma:

pm = static
pm.max_children = 10

Para ver se a mudança de configuração foi efetivada, após reiniciar o PHP-FPM, execute pstree -c -H <PHP-FPM process id> -S <PHP-FPM process id>. Isso mostrará que existem dez processos disponíveis, como no exemplo abaixo.

php-fpm8.1-+-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1 |-php-fpm8.1

Dinâmico (Dynamic)

Nesse modo, o PHP-FPM gerencia dinamicamente o número de processos filho disponíveis e garante que pelo menos um processo filho esteja sempre disponível.

Esta configuração usa cinco opções de configuração que são:

  • pm.max_children: O número máximo de processos filho que podem ser gerados.
  • pm.start_servers: O número de processos filhos a serem iniciados quando o PHP-FPM for iniciado.
  • pm.min_spare_servers: O número mínimo de processos filho inativos que o PHP-FPM criará. Mais são criados se menos do que esse número estiver disponível.
  • pm.max_spare_servers: O número máximo de processos filho inativos que o PHP-FPM criará. Se houver mais processos filho disponíveis do que esse valor, alguns serão eliminados.
  • pm.process_idle_timeout: O tempo ocioso, em segundos, após o qual um processo filho será eliminado.

E como você calcula os valores para cada configuração? O artigo de Sebastian Buckpesch, oferece a seguinte fórmula:



Contexto

Valor

max_children

(Total RAM – Memória usada para Linux, DB, etc.) / tamanho do processo

start_servers

Número de núcleos de CPU x 4

min_spare_servers

Número de núcleos de CPU x 2

max_spare_servers

Igual a start_servers



Também precisamos definir pm.process_idle_timeout, que é o número de segundos após o qual um processo ocioso será eliminado.

Digamos que nosso servidor tenha quatro núcleos (4 vCPU) e 8 GB de RAM. Se assumirmos que o Linux e daemons relacionados estão usando cerca de 2 GB (use free -hl para obter um valor mais específico), isso nos deixa em torno de 6192 MB.

Agora, quanta memória cada processo está usando? Para calcular isso, existe um script Python chamado ps_mem.py. Crie uma arquivo com ele na sua maquina com mesmo nome e depois de executá-lo, usando sudo python ps_mem.py | grep php-fpm, você obterá uma saída semelhante à seguinte:

# 28.4 MiB + 33.8 MiB = 62.2 MiB php-fpm8.1 (11)
A primeira coluna é a memória privada. A segunda coluna é a memória compartilhada. A terceira coluna é a RAM total usada. A quarta coluna é o nome do processo.

De todo modo, você pode ver que o tamanho do processo é de 62,2 MiB. Então, alimentando todas essas informações em nossa fórmula, chegamos ao seguinte:

# Round the result up. (8192 – 2000) / 62.2
Com base nisso, chegamos aos seguintes valores de configuração:

Contexto

Valor

max_children

100

start_servers

32

min_spare_servers

16

max_spare_servers

32

Vamos deixar pm.process_idle_timeout o padrão de10s. Supondo que estejamos satisfeitos com essas configurações, configuraríamos da seguinte forma:

pm = dynamic

pm.max_children = 100

pm.start_servers = 32

pm.min_spare_servers = 16

pm.max_spare_servers = 32

pm.max_requests = 200

Você também pode usar ferramentas de monitoramento de memória regularmente para monitorar quanta memória seu aplicativo está usando. Existem várias opções disponíveis para PHP, incluindo php-memprof

Sob demanda (ondemand)

Ondemand tem processos de fork PHP-FPM quando os pedidos são recebidos. Para configurar o PHP-FPM para usá-lo, precisamos definir pm como ondemand e fornecer valores para:

  • max_children
  • process_idle_timeout
  • max_requests

max_requests define o número de solicitações que cada processo filho deve executar antes de reaparecer. A documentação sugere que essa configuração é útil para contornar vazamentos de memória.

O gerenciador de processos mais ideal para a maioria das aplicações é o esquema ondemand, em que nenhum processo filho é criado na inicialização, mas sim gerado sob demanda. Os processos filhos são bifurcados apenas quando novas solicitações se conectarão com base em pm.max_children e pm.process_idle_timeout,
que define o número de segundos após os quais um processo inativo será eliminado .

Supondo que tenha as mesmas configurações de hardware que usou para configurar o modo dynamic
acima, nós o configuraríamos da seguinte forma com base nos cálculos:

pm = ondemand

pm.max_children = 100

pm.process_idle_timeout = 10s

pm.max_requests = 200

Qual configuração é ideal para você?

A resposta é: “depende”, pois sempre depende do tipo de aplicativo que você está executando. No entanto, aqui estão algumas sugestões sobre qual configuração escolher.

Aplicações de Baixo tráfego ou poucas requisições.

Se você tiver um site ou aplicativo de baixo tráfego/requisições, como um que hospeda um painel de controle de back-end, como cPanel, use ondemand. A memória será salva, pois os processos filhos só serão gerados quando forem necessários e eliminados quando não forem mais necessários. Como é um back-end, os usuários podem esperar um momento ou dois a mais enquanto um thread é gerado para lidar com sua solicitação.

Aplicações de Alto tráfego ou muitas requisições

Se você tiver um site de alto tráfego, use estático e ajuste as configurações com base em suas necessidades ao longo do tempo e nos recursos de hardware disponíveis. Pode parecer um exagero ter um grande número de processos filhos sempre prontos para receber solicitações.

No entanto, sites de alto tráfego precisam responder o mais rápido possível. Portanto, é essencial usar static para que um número suficiente de processos filho esteja pronto para isso.

Ao usar o ondemand, os processos filhos provavelmente consumirão muita memória sendo gerados e eliminados, e o atraso de inicialização terá um impacto no desempenho.

Usar dinâmico provavelmente não será tão ruim, dependendo da configuração. No entanto, você pode acabar com uma configuração que espelha efetivamente a estática.

Recomendações

Essa foi uma rápida introdução ao ajuste do PHP-FPM para melhor desempenho. Examinamos as três configurações diferentes do gerenciador de processos, suas configurações relacionadas e discutimos quando cada configuração faz sentido.

Recomendamos que cada administrador avalie a melhor configuração do e memória RAM para uso no seu servidor, e portanto, realize as configurações mais adequadas pra atender as necessidades de seus usuários.

Como dica deixo aqui um link interessante para melhorar o desempenho configurando pools para o seu php-fpm: https://www.nginx.com/blog/thread-pools-boost-performance-9x/