Leitura Obrigatória Antes do Desenvolvimento
Ao usar o Workerman para desenvolver aplicações, você precisa entender os seguintes conteúdos:
I. Diferenças entre o Desenvolvimento com Workerman e o Desenvolvimento PHP Comum
Exceto pelas variáveis de função relacionadas ao protocolo HTTP que não podem ser usadas diretamente, o desenvolvimento com Workerman não é muito diferente do desenvolvimento PHP comum.
1. Protocolos de Aplicação Diferentes
- O desenvolvimento PHP comum geralmente é baseado no protocolo de aplicação HTTP, onde o WebServer já ajuda o desenvolvedor a realizar a análise do protocolo.
-
Workerman suportam vários protocolos, atualmente incorporando protocolos como HTTP e WebSocket. O Workerman recomenda que os desenvolvedores usem protocolos personalizados mais simples para comunicação.
- Para desenvolvimento com protocolo HTTP, consulte a Seção de Serviços HTTP.
2. Diferença no Ciclo de Requisição
- PHP em aplicações Web libera todas as variáveis e recursos após uma única requisição.
- As aplicações desenvolvidas com Workerman permanecem em memória após a primeira carga e análise, permitindo que definições de classe, objetos globais e membros estáticos de classe não sejam liberados, facilitando a reutilização subsequente.
3. Cuidado para Evitar Definições Repetidas de Classe e Constantes
- Como o Workerman armazena em cache os arquivos PHP compilados, é necessário evitar múltiplas requisições de include/require para os mesmos arquivos de definição de classe ou constantes. Recomenda-se utilizar require_once/include_once para carregar arquivos.
4. Cuidado Com a Liberação de Recursos de Conexão em Padrão Singleton
- Como o Workerman não libera objetos globais e membros estáticos de classe após cada requisição, em padrões singleton como conexões de banco de dados, muitas vezes a instância de banco de dados (que inclui uma conexão de socket de banco de dados) é mantida em um membro estático. Assim, o Workerman reutiliza essa conexão de socket de banco de dados durante o ciclo de vida do processo. É importante observar que, quando o servidor de banco de dados detecta que uma conexão não está ativa por um período de tempo, ele pode fechar proativamente a conexão socket. Quando essa instância de banco de dados for usada novamente, um erro será retornado (uma mensagem de erro como "mysql gone away"). O Workerman fornece uma Classe de Banco de Dados que possui funcionalidade de reconexão. Os desenvolvedores podem usá-la diretamente.
5. Cuidado para Não Usar as Instruções exit ou die
- O Workerman opera no modo de linha de comando do PHP, e ao chamar as instruções exit ou die, o processo atual será encerrado. Embora o subprocesso seja imediatamente recriado para continuar o serviço, isso ainda pode impactar nos negócios.
6. Alterações no Código Exigem Reinício do Serviço para Ter Efeito
Como o Workerman reside em memória, definições de classes e funções são carregadas uma vez e permanecem na memória, não sendo lidas do disco novamente. Portanto, cada modificação no código de negócios requer um reinício para ter efeito.
II. Conceitos Básicos a Conhecer
1. Protocolo de Transporte TCP
TCP é um protocolo de transporte orientado a conexão, confiável e baseado em IP. Uma característica importante do protocolo de transporte TCP é que ele é baseado em fluxo de dados, onde as requisições do cliente são continuamente enviadas ao servidor, e os dados recebidos pelo servidor podem não ser uma requisição completa, podendo ser várias requisições conectadas. Isso exige que identifiquemos os limites de cada requisição no fluxo contínuo de dados. O protocolo de aplicação é principalmente responsável por definir um conjunto de regras para os limites das requisições, evitando confusões nos dados das requisições.
2. Protocolo de Aplicação
O protocolo de aplicação (application layer protocol) define como os processos de aplicações em sistemas diferentes (cliente e servidor) trocam mensagens, por exemplo, HTTP e WebSocket pertencem a protocolos de aplicação. Um exemplo de um protocolo de aplicação simples pode ser {"module":"user","action":"getInfo","uid":456}\n". Este protocolo é encerrado com "\n" (note que aqui "\n" representa uma nova linha) marcando o término da requisição, e o corpo da mensagem é uma string.
3. Conexão Curta
Uma conexão curta refere-se a um cenário em que as partes de comunicação estabelecem uma conexão durante a troca de dados, e após o envio dos dados, a conexão é encerrada, ou seja, cada conexão completa apenas uma operação de envio. Os serviços HTTP de sites WEB geralmente utilizam conexões curtas.
- O desenvolvimento de aplicações de conexão curta pode ser referenciado na seção de fluxo de desenvolvimento básico.
4. Conexão Longa
Uma conexão longa refere-se a um cenário onde é possível enviar múltiplos pacotes de dados em uma única conexão.
Nota: Aplicações de conexão longa devem incluir um heartbeat, caso contrário, a conexão pode ser encerrada pelo firewall do nó de roteamento devido à inatividade por longo período.
Conexões longas são frequentemente utilizadas em situações de comunicação ponto a ponto e operações frequentes. Cada conexão TCP requer um handshake de três etapas, o que consome tempo. Se cada operação precisar conectar-se e depois operar, a velocidade de processamento será significativamente reduzida. Portanto, conexões longas não são encerradas após cada operação, permitindo que os pacotes de dados sejam enviados diretamente na próxima operação sem necessidade de estabelecer uma nova conexão TCP. Por exemplo: a conexão com banco de dados utiliza conexão longa; se for usada conexão curta para comunicação frequente, pode acarretar erros de socket, e a criação frequente de sockets também desperdiça recursos.
Quando é necessário enviar dados proativamente ao cliente, como em aplicações de chat, jogos em tempo real, notificações para dispositivos móveis, conexões longas são necessárias.
O desenvolvimento de aplicações de conexão longa pode ser referenciado no fluxo de desenvolvimento Gateway/Worker.
5. Reinício Suave
O processo de reinício geralmente envolve parar todos os processos e, em seguida, começar a criar novos processos de serviço. Durante esse processo, haverá um breve período sem processos disponíveis para oferecer serviço, ocasionando indisponibilidade temporária, o que pode causar falhas nas requisições em altas taxas de concorrência.
O reinício suave, por outro lado, não para todos os processos de uma só vez, mas sim um por um; a cada processo parado, um novo processo é imediatamente criado para substituí-lo, até que todos os processos antigos sejam substituídos.
Para realizar um reinício suave no Workerman, você pode usar o comando php your_file.php reload, o que permite atualizar a aplicação sem impactar a qualidade do serviço.
Nota: Somente arquivos carregados dentro de callbacks on{...} serão atualizados automaticamente após um reinício suave; arquivos carregados diretamente no script de inicialização ou código codificado não serão atualizados ao executar reload.
III. Diferenciar entre Processo Principal e Subprocesso
É importante observar se o código está sendo executado no processo principal ou no subprocesso. Em geral, o código executado antes da chamada Worker::runAll(); está rodando no processo principal, enquanto o código executado nos callbacks onXXX pertence aos subprocessos. Note que qualquer código após a chamada Worker::runAll(); nunca será executado.
Por exemplo, o código abaixo:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Executado no processo principal
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// Atribuição é executada no processo principal
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
// Esta parte é executada no subprocesso
$connection->send('hello ' . $data);
};
Worker::runAll();
Nota: Não inicialize conexões de banco de dados, memcache, redis, etc. no processo principal, pois as conexões inicializadas no processo principal podem ser herdadas automaticamente pelos subprocessos (especialmente ao usar singleton). Todos os processos podem manter a mesma conexão, e os dados retornados pelo servidor através dessa conexão poderão ser lidos em múltiplos processos, causando confusão nos dados. Da mesma forma, se qualquer processo fechar a conexão (por exemplo, no modo daemon, quando o processo principal é encerrado, levando ao fechamento da conexão), isso causará o fechamento das conexões em todos os subprocessos, resultando em erros imprevisíveis, como erro "mysql gone away".
Recomenda-se inicializar os recursos de conexão dentro do método onWorkerStart.