Esta página foi traduzida pela comunidade e pode não estar atualizada. Consulte a versão de referência.

Esta página foi traduzida pela comunidade e pode não estar atualizada. Consulte a versão de referência.

Esta página foi traduzida pela comunidade e pode não estar atualizada. Consulte a versão de referência.

Desenvolvido com
Desempenho

Desempenho

Por padrão, o FrankenPHP tenta oferecer um bom equilíbrio entre desempenho e facilidade de uso. No entanto, é possível melhorar substancialmente o desempenho usando uma configuração apropriada.

# Número de Threads e Workers

Por padrão, o FrankenPHP inicia 2 vezes mais threads e workers (no modo worker) do que o número de núcleos de CPU disponíveis.

Os valores apropriados dependem muito de como sua aplicação foi escrita, do que ela faz e do seu hardware. Recomendamos fortemente alterar esses valores. Para melhor estabilidade do sistema, recomenda-se ter num_threads x memory_limit < available_memory.

Para encontrar os valores corretos, é melhor executar testes de carga simulando tráfego real. k6 e Gatling são boas ferramentas para isso.

Para configurar o número de threads, use a opção num_threads das diretivas php_server e php. Para alterar o número de workers, use a opção num da seção worker da diretiva frankenphp.

# max_threads

Embora seja sempre melhor saber exatamente como será o seu tráfego, aplicações reais tendem a ser mais imprevisíveis. A configuração max_threads permite que o FrankenPHP crie threads adicionais automaticamente em tempo de execução até o limite especificado. max_threads pode ajudar você a descobrir quantas threads são necessárias para lidar com seu tráfego e pode tornar o servidor mais resiliente a picos de latência. Se definido como auto, o limite será estimado com base no memory_limit em seu php.ini. Se não for possível fazer isso, auto assumirá como padrão 2x num_threads. Lembre-se de que auto pode subestimar bastante o número de threads necessárias. max_threads é semelhante ao pm.max_children do PHP FPM. A principal diferença é que o FrankenPHP usa threads em vez de processos e as delega automaticamente entre diferentes worker scripts e o ‘classic mode’, conforme necessário.

# Modo worker

Habilitar o modo worker melhora drasticamente o desempenho, mas sua aplicação precisa ser adaptada para ser compatível com este modo: você precisa criar um worker script e garantir que a aplicação não esteja com vazamento de memória.

# Não use musl

A variante Alpine Linux das imagens oficiais do Docker e os binários padrão que fornecemos usam a biblioteca C musl.

O PHP é conhecido por ser mais lento ao usar esta biblioteca C alternativa em vez da biblioteca GNU tradicional, especialmente quando compilado no modo ZTS (thread-safe), necessário para o FrankenPHP. A diferença pode ser significativa em um ambiente com muitas threads.

Além disso, alguns bugs só acontecem ao usar musl.

Em ambientes de produção, recomendamos o uso do FrankenPHP vinculado à glibc, compilado com um nível de otimização apropriado.

Isso pode ser feito usando as imagens Docker do Debian, usando nossos pacotes .deb, .rpm ou .apk dos mantenedores, ou compilando o FrankenPHP a partir do código-fonte.

Para contêineres mais leves ou seguros, você pode considerar uma imagem Debian reforçada em vez de Alpine.

# Configuração do runtime do Go

O FrankenPHP é escrito em Go.

Em geral, o runtime do Go não requer nenhuma configuração especial, mas em certas circunstâncias, configurações específicas melhoram o desempenho.

Você provavelmente deseja definir a variável de ambiente GODEBUG como cgocheck=0 (o padrão nas imagens Docker do FrankenPHP).

Se você executa o FrankenPHP em contêineres (Docker, Kubernetes, LXC…) e limita a memória disponível para os contêineres, defina a variável de ambiente GOMEMLIMIT para a quantidade de memória disponível.

Para mais detalhes, a página da documentação do Go dedicada a este assunto é uma leitura obrigatória para aproveitar ao máximo o runtime.

# file_server

Por padrão, a diretiva php_server configura automaticamente um servidor de arquivos para servir arquivos estáticos (assets) armazenados no diretório raiz.

Este recurso é conveniente, mas tem um custo. Para desativá-lo, use a seguinte configuração:

php_server {
    file_server off
}

# try_files

Além de arquivos estáticos e arquivos PHP, php_server também tentará servir o arquivo de índice da sua aplicação e os arquivos de índice de diretório (/path/ -> /path/index.php). Se você não precisa de arquivos de índice de diretório, pode desativá-los definindo explicitamente try_files assim:

php_server {
    try_files {path} index.php
    root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache
}

Isso pode reduzir significativamente o número de operações desnecessárias com arquivos. Um equivalente no modo worker da configuração anterior seria:

route {
    php_server { # use "php" em vez de "php_server" se você não precisar do servidor de arquivos
        root /root/to/your/app
        worker /path/to/worker.php {
            match * # envia todas as requisições diretamente para o worker
        }
    }
}

Uma abordagem alternativa com 0 operações desnecessárias no sistema de arquivos seria usar a diretiva php e dividir os arquivos estáticos dos arquivos PHP por caminho. Essa abordagem funciona bem se toda a sua aplicação for servida por um arquivo de entrada. Um exemplo de configuração que serve arquivos estáticos atrás de uma pasta /assets poderia ser assim:

route {
    @assets {
        path /assets/*
    }

    # tudo o que está em /assets é gerenciado pelo servidor de arquivos
    file_server @assets {
        root /root/to/your/app
    }

    # tudo o que não está em /assets é gerenciado pelo seu arquivo de índice ou worker PHP
    rewrite index.php
    php {
        root /root/to/your/app # adicionar explicitamente o root aqui permite um melhor cache
    }
}

# Placeholders

Você pode usar placeholders nas diretivas root e env. No entanto, isso impede o armazenamento em cache desses valores e acarreta um custo significativo de desempenho.

Se possível, evite placeholders nessas diretivas.

# resolve_root_symlink

Por padrão, se o diretório raiz for um link simbólico, ele será resolvido automaticamente pelo FrankenPHP (isso é necessário para o funcionamento correto do PHP). Se o diretório raiz não for um link simbólico, você pode desativar esse recurso.

php_server {
    resolve_root_symlink false
}

Isso melhorará o desempenho se a diretiva root contiver placeholders. O ganho será insignificante em outros casos.

# Logs

O logging é obviamente muito útil, mas, por definição, requer operações de E/S e alocações de memória, o que reduz consideravelmente o desempenho. Certifique-se de definir o nível de logging corretamente e registrar em log apenas o necessário.

# Desempenho do PHP

O FrankenPHP usa o interpretador PHP oficial. Todas as otimizações de desempenho usuais relacionadas ao PHP se aplicam ao FrankenPHP.

Em particular:

Para mais detalhes, leia a entrada dedicada na documentação do Symfony (a maioria das dicas é útil mesmo se você não usa o Symfony).

# Dividindo o Pool de Threads

É comum que aplicações interajam com serviços externos lentos, como uma API que tende a ser instável sob alta carga ou que consistentemente leva mais de 10 segundos para responder. Nesses casos, pode ser benéfico dividir o pool de threads para ter pools “lentos” dedicados. Isso impede que os endpoints lentos consumam todos os recursos/threads do servidor e limita a concorrência de requisições direcionadas ao endpoint lento, semelhante a um pool de conexões.

example.com {
    php_server {
        root /app/public # a raiz da sua aplicação
        worker index.php {
            match /slow-endpoint/* # todas as requisições com caminho /slow-endpoint/* são tratadas por este pool de threads
            num 1 # mínimo de 1 thread para requisições que correspondem a /slow-endpoint/*
            max_threads 20 # permite até 20 threads para requisições que correspondem a /slow-endpoint/*, se necessário
        }
        worker index.php {
            match * # todas as outras requisições são tratadas separadamente
            num 1 # mínimo de 1 thread para outras requisições, mesmo que os endpoints lentos comecem a travar
            max_threads 20 # permite até 20 threads para outras requisições, se necessário
        }
    }
}

Geralmente, também é aconselhável lidar com endpoints muito lentos de forma assíncrona, usando mecanismos relevantes como filas de mensagens.

Editar esta página