A ascensão do PHP híbrido: combinando PHP com Go e Rust
(yekdeveloper.com)- Recentemente, vem ganhando atenção uma abordagem híbrida que integra Go e Rust como linguagens de extensão dentro de uma arquitetura monolítica em PHP
- Antes, a combinação de microsserviços em Go com um monólito em PHP 8.3 permitia alcançar um equilíbrio entre produtividade e alto desempenho
- Seguindo a lei de Pareto (80% do tráfego concentrado em 20% das APIs), otimizar endpoints de hotspot era essencial, e no passado isso era tratado com cache e separação de serviços em Go, mas a complexidade aumentava
- Com a evolução recente do ecossistema PHP, surgiram técnicas como FFI, extensões em Rust e extensões em Go (FrankenPHP), tornando possível elevar bastante o desempenho dentro do próprio monólito
- Extensões em Rust oferecem ao mesmo tempo segurança de memória e velocidade, e o FrankenPHP mostra melhorias de desempenho de mais de 4x com worker mode e extensões baseadas em Go
- Sem assumir o custo e o risco de reescrever tudo em Go/Rust, a abordagem de PHP híbrido permite garantir produtividade e velocidade ao mesmo tempo
Contexto e arquitetura existente
- Antes, o foco era um aplicativo monolítico com DDD (mother), enquanto microsserviços baseados em Go (children) eram desenvolvidos separadamente para otimizar funcionalidades específicas
- Os microsserviços em Go cuidavam do processamento de tráfego de alto desempenho, enquanto o monólito em PHP 8.3 oferecia desenvolvimento rápido de recursos e confiabilidade na implantação em um contexto de equipe de backend pequena
- Essa estrutura oferecia um ponto de equilíbrio capaz de garantir velocidade, estabilidade e produtividade
Gargalos de desempenho e formas tradicionais de resposta
- O princípio de Pareto, em que 80% do tráfego se concentra em 20% dos endpoints de API, é observado com frequência
- Para esses 20% mais críticos em termos de desempenho, foram adotadas várias abordagens, como código otimizado, adição de camadas de cache e separação em microsserviços Go
- No entanto, havia limitações em termos de complexidade e carga operacional
Opções híbridas no ecossistema PHP atual
- Recentemente, aumentou o número de tecnologias capazes de melhorar diretamente o desempenho dentro de um monólito em PHP
-
1. FFI (Foreign Function Interface)
- Com o recurso FFI do PHP, é possível chamar código em C diretamente a partir do PHP
- Assim, também é possível implementar no próprio projeto PHP lógica de nível de sistema ou crítica para desempenho
- Ainda assim, é recomendável usar isso apenas nos cenários adequados, considerando o custo de context switching
-
2. Extensões baseadas em Rust
- É possível desenvolver extensões para PHP com Rust (ou Zig)
- Ao descarregar áreas de alto volume para extensões em Rust, que oferecem segurança de memória e desempenho compilado, é possível garantir ao mesmo tempo confiabilidade e alta velocidade
-
3. Extensões baseadas em Go: FrankenPHP
- Após a migração recente para o FrankenPHP, ao operar em worker mode, foi confirmado um desempenho mais de 4x superior ao anterior
- Um lançamento recente também passou a permitir a escrita de extensões PHP em Go
- Com isso, tornou-se possível aproveitar diretamente o desempenho de API do Go dentro de um monólito PHP, combinando produtividade e velocidade sem dividir a aplicação por linguagem
Por que não migrar tudo completamente para Go ou Rust
- O custo de uma reescrita completa e o risco são altos
- Trocar completamente por Go ou Rust uma aplicação já grande e estável implica muito risco e consumo de recursos
- O próprio PHP ainda tem pontos fortes
- Na maioria dos casos, o desenvolvimento rápido em PHP, seu ecossistema amigável e sua velocidade suficientemente boa continuam sendo competitivos
- Se apenas algumas áreas que realmente exigem o limite máximo de desempenho forem compostas de forma híbrida com Go ou Rust, a necessidade de uma migração total pode ser eliminada
Conclusão: o valor do PHP híbrido
- O ecossistema PHP moderno oferece, junto com alta produtividade no desenvolvimento, opções de integração com extensões de alto desempenho em (C, Rust, Go)
- Com essa estrutura híbrida, é possível garantir velocidade e produtividade ao mesmo tempo
- Mantendo o desenvolvimento centrado em PHP, surge um novo paradigma de arquitetura que permite expandir seletivamente a linguagem quando necessário
5 comentários
Parece que está ficando meio como se o JavaScript estivesse mudando.
Mesmo que fosse
nodejscom Rust, vai lá;; no meu caso, como no PHP fica usando$o tempo todo, parece desconfortável de digitar código. Quem usa bem costuma não sentir muito esse incômodo?Mesmo que você sinta desconforto, não acaba se acostumando rápido depois de usar por um tempo?
Os seres humanos são animais que se adaptam.
Se eu já senti incômodo, foi com o próprio conceito de variáveis/funções do PHP, mas nunca senti absolutamente nenhum incômodo com a sintaxe usando
$.Dizer que não consegue usar por causa do cifrão, ~~que quem usa símbolo de dólar ganha muito dinheiro, que não é dólar americano e sim dólar do Zimbábue, então não ganha tanto assim~~, não eram só piadas?...
Comentários do Hacker News
Estou cada vez mais pegando antipatia por frameworks generalistas (Spring, Laravel, Phoenix etc.); no começo eles são realmente produtivos, mas em projetos legados os mesmos problemas sempre se repetem Cada projeto tem um ambiente de infraestrutura e condições de negócio diferentes, então nem sempre dá para seguir só o caminho recomendado pelo framework, e começam a surgir remendos extras e gambiarras de dependência por todo lado Quando se tenta atualizar para uma nova linguagem ou versão, todas essas partes customizadas quebram, então no fim ninguém atualiza nada até chegar o momento em que a infraestrutura já não consegue mais rodar aquilo, e aí vem a grande migração às lágrimas Montar sua própria abstração combinando várias bibliotecas pode levar mais tempo, mas permite atualizar partes de forma flexível e mudar de direção rapidamente Passei a achar o ecossistema Go ideal nesse sentido; no começo pareceu estranho, mas hoje acabei gostando mais desse modelo
Com frameworks web, a sensação forte é de que “no começo é ótimo, e depois de certo ponto começa a incomodar” Quando você vai fazer um app simples, coisas como “crie um blog em 15 minutos”, no estilo Rails, parecem magia, mas quando a complexidade cresce o framework acaba virando um obstáculo Pessoalmente, me sinto mais confortável com configurações HTTP de “nível intermediário”, como Express + Node.js ou Vert.x + Java
Em Python, dá para separar entre microframeworks (tipo Flask) e macroframeworks (Django) Eu sempre escolho Django; como o Flask praticamente não sugere nada, cada projeto vira um floco de neve montado do zero Surge fadiga de decisão para escolher entre N opções de autenticação, templates, cookies, email etc. Especialmente porque a maioria dessas bibliotecas é mantida por uma pessoa só, então a qualidade de manutenção e segurança varia bastante Já o Django faz com que a maioria dos projetos tenha uma cara parecida e entrega quase todas as funcionalidades básicas de imediato Só vejo motivo para usar bibliotecas de extensão quando há requisitos realmente especiais; como muita coisa é gerida e validada diretamente, acho que a confiabilidade do código e a segurança também ficam maiores
A razão de não existirem frameworks gigantes em Go é que o sistema de tipos da linguagem ainda é bastante incompleto É difícil criar bibliotecas complexas que se encaixem bem entre si Esperei 9 anos até poder usar genéricos para finalmente criar meu primeiro toolkit de banco de dados para Go Foi um sucesso, mas ainda assim chego a achar que, quando fazia esse tipo de coisa em Java, era melhor Se desse para fazer map/filter/reduce de tipos de resultado para outros tipos genéricos, seria um mundo completamente diferente Se houvesse apenas suporte a tipos union, eu nem precisaria usar o tipo
anySe ao menos houvesse suporte a overload, o código ficaria muito mais limpo; o sistema de tipos de Go ainda precisa evoluir maisNa minha área, só Go e Rust têm utilidade A cultura de frameworks muito opinativos não combina comigo Acho Rails, Laravel e Django excelentes quando a situação é claramente CRUD sobre banco de dados relacional
Nos últimos 5 anos, usei apenas componentes compatíveis com PSR (Php Standards Recommendation), sem framework nenhum O motivo é que frameworks grandes acabam deixando de servir com o tempo Têm muitas restrições, e gerenciar e atualizar vira um pesadelo Seja em projetos grandes de empresa ou em serviços pessoais, sinto que uma arquitetura centrada em componentes PSR é melhor
Entendo que, em um codebase grande, quando é impossível reescrever tudo, uma abordagem híbrida (PHP junto com linguagens de performance como C, Rust, Go etc.) pode fazer sentido Mas, se não houver necessidade real disso, minha experiência é que uma API em C# entrega ao mesmo tempo velocidade de desenvolvimento e performance em execução Quase nunca precisei ir até C++ ou Rust PHP também é bom, mas ainda não tem coisas como arrays tipados Por exemplo, é uma linguagem que não rejeita uma string dentro de um array de datas
Usei C# por muito tempo e tenho experiência com vários frameworks web/API Quando você se aprofunda um pouco em PHP, vê que ele é ótimo por ter muitas funções embutidas para desenvolvimento web Tem seus defeitos, mas quando é preciso construir algo rápido, na minha opinião PHP vence
É verdade que o PHP permite tipos errados em arrays (por exemplo, uma string em um array de datas) Às vezes aparecem comportamentos estranhos ou bugs Recentemente me deparei com um bug em que, ao decodificar com
json_decode(), as chaves numéricas viravaminte as demais viravamstring, misturando os tipos de chave Esses cantos do PHP podem ser meio esquisitos, mas ainda assim a linguagem em si é um Frankenstein realmente atraenteNa prática, se você usa um analisador estático, esse tipo de erro costuma ser bloqueado Também é bem provável que o PHP ganhe suporte a genéricos em breve Dá para ver novidades relacionadas no blog da thephp.foundation
Sou o autor do texto, obrigado por ler Na prática, não há necessidade de reescrever tudo; se você rodar PHP em um runtime baseado em workers, como swoole ou frankenphp, dá para chegar a uma performance de nível Node A questão de arrays tipados e genéricos também é coberta por analisadores estáticos como phpstan; usando anotações de tipo, dá para aumentar bastante a segurança de tipos
Desde o fim do suporte ao VB6, decidi não usar mais nenhuma linguagem da Microsoft Só linguagens open source fazem bem para a saúde mental
Quando entrei na {{company}}, o ambiente inteiro da empresa ainda era PHP 5.4, e naquela época havia muita rejeição ao PHP Mas, depois de experimentar o PHP moderno, sinto que justamente agora, quando estamos na reta final de sair do PHP, essa migração parece até um retrocesso Muita gente ainda julga PHP pelos tempos do 5.x, mas hoje é uma realidade completamente diferente
Vejo isso de forma um pouco diferente; olhando para o mercado de trabalho, ainda existe uma percepção sobre PHP (boa ou ruim), e isso limita a contratação de desenvolvedores excelentes Mesmo que tecnicamente o PHP já não seja tão ruim quanto antes, do ponto de vista de branding da empresa ainda faz sentido sair dele
Não concordo com a frase “PHP agora é awesome”; sem dúvida melhorou, mas chamar de awesome é exagero
O PHP está melhorando cada vez mais; acho que em breve vou olhar para ele com mais profundidade
Nós também tivemos exatamente essa mesma dúvida 4 anos atrás e, no fim, atualizamos para PHP 8 e continuamos usando Para a nossa equipe, essa escolha funcionou bem nos últimos anos
O Pasir é parecido com o frankenphp, mas baseado em Rust; é muito promissor, mas ainda está nos estágios iniciais de desenvolvimento
Pasir github O Pasir usa uma conversão para Rust dos bindings da Zend API
Bindings Rust da Zend API Também existem experimentos interessantes como o ngx-php, que embute PHP dentro do binário do nginx via Zend API
ngx-php github O workerman implementa um runtime muito rápido usando um backend híbrido com asio
workerman github
Fico curioso se Pasir, frankenphp e afins também suportam módulos PHP já existentes
Obrigado pela recomendação, parece realmente muito legal Mas concordo com você que ainda está longe de estar pronto para produção Nós acabamos escolhendo o frankenphp, que é apoiado pela php foundation
Muitas vezes a complexidade de depuração e manutenção é ignorada; se houver escolha, acho melhor evitar esse tipo de combinação
Eu também reescrevi meu app de PHP para Go, e isso foi um investimento bem-sucedido para a empresa Reduzi um código PHP de 20 mil linhas para 4 mil linhas em Go e ainda aumentei muito a eficiência Se a empresa de vocês é de PHP, recomendo fortemente planejar uma reescrita ampla e executá-la junto com a adição de testes (o que é mais fácil em Go) Acho melhor isso do que sofrer mantendo várias linguagens misturadas, como Rust/PHP ou Go/PHP
Tenho curiosidade de saber como o número de linhas caiu tanto ao migrar de PHP para Go Acho Go uma linguagem bastante verbosa; na minha experiência, PHP fica no meio, Haskell é a mais concisa, e Java/Go acabam ficando mais longas por causa de coisas como tratamento de erro
Não consigo acreditar nessa redução de 5x no número de linhas ao migrar de PHP para Go Sempre senti que PHP tem várias sintaxes abreviadas, enquanto Go quase não tem isso
Sempre acho importante distinguir se a melhora de performance e eficiência após um rewrite veio “por causa da linguagem” ou por causa das melhorias de arquitetura incorporadas na nova versão
Quando pensei em reescrever em Go, imaginei que o padrão
if err != nilfaria o código explodir e ficar 10 vezes maior Já vivi um rewrite em Python, e o Python também pode acabar bem verboso, além de padrões como injeção de dependência tornarem os testes mais incômodosEu não recomendaria um rewrite de qualquer jeito (já concluí dois rewrites com sucesso, mas mesmo assim talvez não escolheria isso) Os runtimes modernos de PHP estão realmente muito rápidos, então vale a pena experimentar Especialmente se você aproveitar bem cache de tarefas com algo como swoole, em alguns casos pode ficar tão rápido quanto Go (recomendo consultar benchmarks)
Às vezes acho que realmente precisamos voltar ao básico: pixels, dados, latência/largura de banda No fim das contas, a web também é um problema de otimização para “desenhar os pixels certos em uma velocidade perceptivelmente rápida para o olho humano, dentro dos recursos da rede” Acho que faz sentido abordar assim: “quais são os pixels que o usuário vai ver em seguida?”, “de quais dados eu preciso para desenhá-los?”, “vale a pena fazer prefetch de dados que talvez sejam usados depois?”
Estou fazendo o alumina-ui com egui para WASM, e não preciso de todo o conhecimento complexo de HTML, JavaScript, CSS etc.; basta fornecer um canvas do tamanho adequado para o navegador e renderizar direto com WebGL Posso gerar gráficos acelerados por GL de forma rápida usando uma linguagem de que gosto, o que é muito conveniente Gosto bastante de WASM/WebGL por causa desse tipo de abstração
Focar apenas nos pixels vistos pelo usuário é uma visão simplista demais Em projetos de software, é preciso otimizar não só a UX imediata, mas também o tempo de desenvolvimento O atraso até a primeira tela aparecer e o esforço real de desenvolvimento não são proporcionais de forma alguma
O FrankenPHP parece bem interessante, mas na prática é um pouco excêntrico Ninguém usa PHP sem módulos PHP, e não está claro quais módulos o FrankenPHP suporta, nem se é possível compilar e adicionar módulos extras que eu queira Ele é muito acoplado ao Caddy, e eu não conheço esse servidor web; prefiro nginx Sem um guia, nem sei se dá para usá-lo no nginx como substituto do php-fpm Além disso, as imagens Docker do Caddy ou do FrankenPHP parecem pensar só em certificados do Let's Encrypt, então, se você quiser configurar SSL manualmente ou operar só com HTTP, tudo fica realmente pouco intuitivo
A questão dos módulos PHP normalmente é resolvida compilando tudo diretamente no Dockerfile Por exemplo, para adicionar o módulo pgsql, você instala as dependências com apt, instala o módulo com
docker-php-ext-installe, no fim, remove/limpa as dependências A configuração HTTP também pode ser feita abrindo diretamente a porta 80 no CaddyfileO build estático já inclui, por padrão, dezenas dos principais módulos do PHP A lista detalhada de módulos e o script de build podem ser vistos em frankenphp build-static.sh
Fico curioso sobre em que tipo de workload extensões em linguagens como C/Rust/Go são realmente necessárias Entendo que isso exista, mas gostaria de saber melhor por que essa complexidade precisa ser adicionada à stack e se não há outras formas de resolver
O que mais odeio no PHP é que, a cada requisição HTTP, a aplicação inteira é bootstrapada de novo, com autoload e reavaliação de configuração Claro, existem caches e coisas do tipo, mas ainda assim isso não me parece fazer sentido em comparação com o modelo de engine sempre ativa, como no Go
reactphp.org
php.net ev module
pecl-event
workerman.net
documentação de workers do frankenphp
Para mim, isso é justamente a melhor vantagem do PHP Escalar horizontalmente fica absurdamente fácil
Eu gosto justamente dessa estrutura Ela minimiza o estado de forma inerente (até certo ponto)
Você tem razão, esse modelo é realmente péssimo Fica ainda pior quando PHP é usado como linguagem de template Motores de template customizados ou runtimes persistentes criados para resolver isso continuam sendo apenas “passar batom em porco” Melhor teria sido escolher desde o início uma linguagem que não se chamasse Personal HomePage