Programação IO simultânea em PHP

O problema de E / S simultâneas sempre foi um desafio técnico na programação de back-end, desde o processo Fork de bloqueio síncrono mais antigo, até multiprocessos / multithreads, até a IO assíncrona atual, corotina. Como os programadores PHP possuem uma poderosa estrutura LAMP, eles sabem muito pouco sobre o conhecimento subjacente.O objetivo deste artigo é apresentar em detalhes as várias tentativas do PHP para programação simultânea de IO e, finalmente, introduzir o uso do Swoole para explicar o abrangente problema de IO.

Bloqueio síncrono de vários processos / vários segmentos

Os primeiros programas do lado do servidor usavam vários processos e threads para resolver o problema de E / S simultâneas. O modelo de processo apareceu pela primeira vez e o conceito de processo existe desde o nascimento do sistema Unix. Os primeiros programas do lado do servidor geralmente aceitavam o Accept para criar um processo após uma conexão do cliente e, em seguida, o processo filho entrava no loop para bloquear e interagir de forma síncrona com a conexão do cliente, enviar e receber dados.

O modo multithread aparece mais tarde, os threads são mais leves que os processos e os threads compartilham pilhas de memória, portanto é muito fácil conseguir a interação entre diferentes threads. Para programas como salas de bate-papo, as conexões do cliente podem interagir e os jogadores na sala de bate-papo podem enviar mensagens para qualquer outra pessoa. É muito simples de implementar no modo multithread, você pode ler e escrever diretamente uma conexão do cliente no thread. O modelo de processo múltiplo requer o uso de canais, filas de mensagens e memória compartilhada para obter a interação dos dados e pode ser realizado por uma tecnologia complexa chamada comunicação entre processos (IPC).

Exemplo de código:

O processo do modelo multiprocesso / encadeamento é

  1. Crie um soquete, ligue a porta do servidor (bind), ouça a porta (listen), use o PHP stream_socket_server uma função para concluir as três etapas acima; é claro, você também pode usar a extensão php sockets para obter separadamente.
  2. Digite o loop while, bloqueie a operação de aceitação e aguarde a conexão do cliente entrar. Nesse momento, o programa entrará no estado de suspensão até que um novo cliente inicie uma conexão com o servidor e o sistema operacional inicie o processo. A função accept retorna o soquete ao qual o cliente se conecta
  3. O processo principal usa a bifurcação (php: pcntl_fork) para criar processos filhos no modelo de múltiplos processos e pthread_create (php: new Thread) para criar threads filhos no modelo de vários segmentos. Se não houver nenhuma declaração especial abaixo, process será usado para significar processo / thread.
  4. Depois que o processo filho é criado com êxito, ele entra em um loop while, bloqueando a chamada recv (php: fread), aguardando o cliente enviar dados ao servidor. Depois de receber os dados, o programa do servidor os processa e, em seguida, usa send (php: fwrite) para enviar uma resposta ao cliente. Os serviços de conexão longa continuarão a interagir com os clientes, enquanto os serviços de conexão curta geralmente fecham quando recebem uma resposta.
  5. Quando a conexão do cliente é fechada, o processo filho sai e destrói todos os recursos. O processo principal reciclará esse processo filho.

 

O maior problema desse modelo é que o custo da criação e destruição do processo / encadeamento é alto. Portanto, o padrão acima não pode ser aplicado a programas de servidor muito ocupados. A versão aprimorada correspondente resolve esse problema, que é o modelo clássico de Líder-Seguidor .

Exemplo de código:

Sua característica é que N processos serão criados após o início do programa. Cada processo filho entra no Accept, aguardando a entrada de uma nova conexão. Quando o cliente se conectar ao servidor, um dos processos filhos será ativado para iniciar o processamento de solicitações do cliente e não aceitará mais novas conexões TCP. Quando essa conexão é fechada, o processo filho será liberado, retornará Aceitar e participará do processamento da nova conexão.

A vantagem desse modelo é que ele pode reutilizar completamente os processos sem consumo adicional e o desempenho é muito bom. Muitos programas de servidor comuns são baseados nesse modelo, como Apache, PHP-FPM.

O modelo de multiprocessos também apresenta algumas desvantagens.

  1. Esse modelo depende muito do número de processos para resolver o problema de simultaneidade.Uma conexão de cliente precisa ocupar um processo, e o número de processos de trabalho possui o maior número possível de recursos de processamento simultâneo. O número de processos que o sistema operacional pode criar é limitado.
  2. Iniciar um grande número de processos causará custos adicionais de agendamento de processos. Quando centenas de processos podem consumir menos de 1% da CPU no agendamento da alternância de contexto do processo, ele pode ser ignorado.Se milhares ou até dezenas de milhares de processos forem iniciados, o consumo aumentará linearmente. O consumo de agendamento pode ser responsável por dezenas ou até 100% da CPU.

Além disso, existem alguns cenários em que o modelo de múltiplos processos não pode ser resolvido. Por exemplo, um programa de bate-papo instantâneo (IM), em que um servidor precisa manter dezenas de milhares ou até centenas de milhares de milhões de conexões ao mesmo tempo (o problema clássico do C10K), o modelo de múltiplos processos não consegue lidar com isso.

Outro cenário é a fraqueza do modelo de multiprocessos. Geralmente, o servidor da Web inicia 100 processos. Se uma solicitação consumir 100ms, 100 processos podem fornecer 1000qps. Esse recurso de processamento não é ruim. No entanto, se você quiser chamar a interface Http externa na solicitação, como o login QQ e Weibo, levará um longo tempo e uma solicitação levará 10s. Esse processo pode processar apenas 0,1 solicitações por segundo e 100 processos podem atingir apenas 10qps, o que é muito ruim.

Existe uma tecnologia que possa lidar com todas as E / S simultâneas em um processo? A resposta é sim, esta é a tecnologia de multiplexação IO.

Multiplexação de E / S / loop de eventos / sem bloqueio assíncrono

De fato, o histórico de reutilização de E / S é tão longo quanto o processo múltiplo.O Linux forneceu uma chamada de sistema muito cedo, o que pode manter 1024 conexões em um processo. Mais tarde, a chamada do sistema de sondagem foi adicionada e a sondagem fez algumas melhorias para resolver o problema do limite de 1024, que pode manter qualquer número de conexões. Mas outro problema com o select / poll é que ele precisa fazer um loop para detectar se há um evento na conexão. É aí que surge o problema: se o servidor tiver 1 milhão de conexões e apenas uma conexão enviar dados ao servidor por vez, a seleção / pesquisa precisará repetir 1 milhão de vezes, das quais apenas 1 é atingida e as 990.000 restantes. 9999 vezes são inválidas, desperdiçando recursos da CPU em vão.

Até que o kernel do Linux 2.6 forneça uma nova chamada de sistema epoll, que possa manter um número ilimitado de conexões, e sem pesquisas, isso realmente resolverá o problema do C10K. Agora, todos os tipos de programas de servidor de E / S assíncronos simultâneos são baseados em epoll, como Nginx, Node.js, Erlang, Golang. Os programas de thread único de processo único, como o Node.js, podem manter mais de 1 milhão de conexões TCP, tudo graças à tecnologia epoll.

Os programas não bloqueadores assíncronos multiplexados de IO usam o modelo clássico de reator , que, como o nome indica, significa o reator, que não lida com o envio e o recebimento de dados. Só é possível monitorar as alterações de evento de um identificador de soquete.

O reator possui 4 operações principais:

  1. add add soquete para ouvir o reator, que pode ser soquete de escuta ou soquete do cliente, também pode ser pipe, eventfd, sinal etc.
  2. definir o monitoramento de eventos de modificação, você pode definir o tipo de monitoramento, como legível e gravável. É legível e fácil de entender.Para soquete de escuta, uma nova conexão de cliente precisa ser aceita. Para que a conexão do cliente receba dados, é necessário recv. Eventos graváveis ​​são mais difíceis de entender. Um SOCKET possui uma área de cache. Se você deseja enviar 2 milhões de dados para a conexão do cliente, eles não podem ser enviados de uma só vez. A área de cache TCP padrão do sistema operacional é de apenas 256K. Você só pode enviar 256 K. por vez. Depois que o buffer estiver cheio, o send retornará um erro EAGAIN. No momento, é necessário monitorar o evento gravável.Na programação puramente assíncrona, você deve monitorar o gravável para garantir que a operação de envio seja completamente sem bloqueio.
  3. del é removido do reator e não escuta mais eventos
  4. callback é a lógica de processamento correspondente após o evento, geralmente formulada em add / set. A linguagem C é implementada com ponteiros de função, JS pode usar funções anônimas, PHP pode usar funções anônimas, matrizes de métodos de objetos e nomes de funções de cadeia.

O reator é apenas um gerador de eventos.As operações reais nos identificadores de soquete, como conectar / aceitar, enviar / recuperar e fechar são realizadas no retorno de chamada. A codificação específica pode se referir ao seguinte pseudo-código:

O modelo do Reator também pode ser usado em conjunto com multiprocessos e multithread, tanto para obter IO assíncrona e sem bloqueio, mas também para usar multi-core. Os atuais programas populares de servidor assíncrono são todos desta maneira: como

  • Nginx: Reator de vários processos
  • Nginx + Lua: reator multi-processo + corotina
  • Golang: Reator de rosca única + corotina de rosca múltipla
  • Swoole: Reator multithreaded + trabalhador multiprocessos

O que é corotina

A corotina é na verdade um modelo de reator de E / S assíncrono da perspectiva da tecnologia subjacente.A camada de aplicação implementa o agendamento de tarefas por si só e usa o Reactor para alternar cada segmento do modo de usuário em execução no momento, mas o código do usuário não tem conhecimento da existência do Reactor.

Prática de programação IO simultânea em PHP

Extensões relacionadas ao PHP

  • Stream: pacote de soquete fornecido pelo kernel do PHP
  • Soquetes: encapsulamento da API do soquete subjacente
  • Libevent: Encapsulamento da biblioteca libevent
  • Evento: Com base no encapsulamento mais avançado do Libevent, fornece suporte para interfaces, temporizadores e processamento de sinal orientados a objetos
  • Pcntl / Posix: suporte a vários processos, sinais e gerenciamento de processos
  • Pthread: multithreading, gerenciamento de threads, suporte a bloqueio
  • O PHP também possui extensões relacionadas para memória compartilhada, semáforos e filas de mensagens
  • PECL: a biblioteca estendida do PHP, incluindo a parte inferior do sistema, análise de dados, algoritmos, drivers, computação científica, gráficos, etc. Se não for encontrado na biblioteca padrão do PHP, você poderá encontrar a função desejada no PECL.

Vantagens e desvantagens da linguagem PHP

Vantagens do PHP:

  1. O primeiro é simples, o PHP é mais simples do que qualquer outra linguagem, e pode realmente ser iniciado em uma semana. O C ++ possui um livro chamado "Estudo aprofundado de C ++ em 21 dias". Na verdade, é impossível aprender em 21 dias. Pode-se até dizer que o C ++ não pode ser dominado em profundidade em 3-5 anos. Mas o PHP definitivamente pode começar em 7 dias. Portanto, o número de programadores PHP é muito grande e o recrutamento é mais fácil do que outras linguagens.
  2. A função do PHP é muito poderosa, porque a biblioteca padrão oficial e a biblioteca de extensão do PHP fornecem 99% das coisas que podem ser usadas para a programação do servidor. Biblioteca de extensões PHP PECL qualquer função que você desejar.

Além disso, o PHP tem uma história de mais de 20 anos, o ecossistema é muito grande e muitos códigos podem ser encontrados no Github.

Desvantagens do PHP:

  1. O desempenho é relativamente baixo, porque, afinal de contas, é um script dinâmico e não é adequado para operações intensivas.Se também é escrito em PHP e depois em c ++, a versão do PHP é cem vezes pior.
  2. A convenção de nomenclatura das funções é ruim.Todo mundo sabe disso.O PHP é mais prático e não possui especificações. A nomeação de algumas funções é muito confusa; portanto, toda vez que você precisar ler o manual do PHP.
  3. A granularidade da interface das estruturas e funções de dados fornecidas é relativamente grossa. O PHP possui apenas uma estrutura de dados Array, e a camada inferior é baseada no HashTable. Matriz do PHP é uma coleção de funções como Mapa, Conjunto, Vetor, Fila, Pilha, Pilha e outras estruturas de dados. Além disso, o PHP possui uma SPL que fornece o encapsulamento de classe de outras estruturas de dados.

Então PHP

  1. PHP é mais adequado para programas práticos em nível de aplicativo, uma ferramenta para desenvolvimento de negócios e implementação rápida
  2. PHP não é adequado para o desenvolvimento de software de baixo nível
  3. Use C / C ++, JAVA, Golang e outras linguagens estáticas compiladas como um complemento ao PHP, combinando dinâmica e estática
  4. Usando ferramentas IDE para alcançar a conclusão automática, a gramática solicita  

Extensão PHP Swoole

Com base nas extensões acima, o uso de PHP puro pode implementar totalmente o servidor da Web assíncrono e os programas clientes. Mas se você deseja implementar um encadeamento de várias IO, ainda há muitas tarefas de programação tediosas a serem executadas, incluindo como gerenciar a conexão, como garantir o princípio de transmissão e recepção de dados e o processamento de protocolos de rede. Além disso, o desempenho do código PHP na parte de processamento do protocolo é relativamente ruim, então iniciei um novo projeto de código aberto Swoole, usando a linguagem C e PHP para concluir este trabalho. O módulo de negócios flexível usa PHP para desenvolver com alta eficiência, e a parte inferior subjacente e a parte de processamento do protocolo são implementadas na linguagem C, o que garante alto desempenho. Ele é carregado no PHP de maneira estendida, fornecendo uma estrutura completa de comunicação de rede e, em seguida, o código PHP para escrever alguns negócios. Seu modelo é baseado no Reactor multiencadeado + Trabalhador de múltiplos processos, que suporta totalmente assíncrono e semi-assíncrono e semi-síncrono.

Alguns recursos do Swoole:

  • Aceitar encadeamento para resolver Aceitar gargalos de desempenho e problemas de grupo de choque
  • Vários threads de E / S podem fazer melhor uso de vários núcleos
  • Oferece dois modos: totalmente assíncrono e semi-síncrono e semi-assíncrono
  • O modo assíncrono é usado para peças com alto IO simultâneo
  • Modo síncrono para lógica de negócios complexa
  • A camada inferior suporta atravessar todas as conexões, enviar dados entre si, mesclar e dividir automaticamente pacotes de dados e atomicidade da transmissão de dados.

Modelo de processo / thread de Swoole:

Fluxo de execução do programa Swoole:

Usando a extensão PHP + Swoole para realizar a programação de comunicação assíncrona

O código de exemplo pode ser visualizado na https://github.com/swoole/swoole-src.

Servidor e cliente TCP

Servidor TCP assíncrono :

 

Aqui o novo objeto swoole_server, os parâmetros são passados ​​para o HOST e PORT de escuta e, em seguida, configuram três funções de retorno de chamada, respectivamente, onConnect tem uma nova conexão para inserir, onReceive recebeu os dados de um cliente, ao fechar um cliente fechou a conexão . Por fim, chame start para iniciar o programa do servidor. A camada inferior do swoole iniciará um número correspondente de threads do Reator e processos de trabalho de acordo com quantos núcleos de CPU a máquina atual possui.

Cliente assíncrono:

O método de uso do cliente é semelhante ao servidor, exceto pela existência de quatro eventos de retorno de chamada.O OnConnect se conecta com êxito ao servidor e, em seguida, você pode enviar dados ao servidor. O onError falhou ao conectar-se ao servidor. O servidor onReceive envia dados para a conexão do cliente. Conexão onClose fechada.

Após definir o retorno de chamada do evento, iniciar uma conexão com o servidor, os parâmetros são IP, PORT e timeout do servidor.

 

Cliente de sincronização:

O cliente de sincronização não precisa definir nenhum retorno de chamada de evento, não possui o monitoramento do reator e está bloqueando o serial. Aguarde a conclusão do IO antes de prosseguir para a próxima etapa.

Tarefas assíncronas:

A função de tarefa assíncrona é usada para executar uma função demorada ou de bloqueio em um programa do servidor puramente assíncrono. A implementação subjacente usa um pool de processos, o onFinish será acionado após a conclusão da tarefa e os resultados do processamento da tarefa podem ser obtidos no programa. Por exemplo, uma mensagem instantânea precisa ser transmitida Se transmitida diretamente em código assíncrono, pode afetar o processamento de outros eventos. Além disso, a leitura e gravação de arquivos também podem ser obtidas usando tarefas assíncronas, porque os identificadores de arquivos não podem usar o monitoramento do Reator como soquetes. Como o identificador do arquivo é sempre legível, a leitura direta do arquivo pode bloquear o programa do servidor e o uso de tarefas assíncronas é uma ótima opção.

Temporizador assíncrono de milissegundos

Essas duas interfaces implementam as funções setInterval e setTimeout semelhantes a JS e podem ser configuradas para implementar uma função em intervalos de n milissegundos ou executar uma função após n milissegundos.

Cliente MySQL assíncrono

A swoole também fornece a um cliente assíncrono do MySQL um pool de conexões embutido. Você pode definir o número máximo de conexões do MySQL. Solicitações simultâneas de SQL podem reutilizar essas conexões em vez de criá-las repetidamente, o que protege o MySQL de ser esgotado pelos recursos de conexão.

Cliente Redis assíncrono

Programa da Web assíncrono

A lógica do programa é ler os dados do Redis e exibir a página HTML. O desempenho da medição de pressão usando ab é o seguinte:

Os resultados do teste de desempenho da mesma lógica em php-fpm são os seguintes:

Programa WebSocket

O Swoole possui um servidor de soquete da web embutido, que pode implementar a função de enviar páginas da Web ativamente, como o WebIM. Existe um projeto de código aberto que pode ser usado como referência. https://github.com/matyhtf/php-webim

Rotina PHP + Swoole

A programação assíncrona geralmente usa o método de retorno de chamada.Se você encontrar uma lógica muito complicada, poderá aninhar funções de retorno de chamada camada por camada. As corotinas podem resolver esse problema.Você pode escrever o código sequencialmente, mas o tempo de execução é assíncrono e sem bloqueio. Engenheiros da Tencent baseados na extensão Swoole e na sintaxe Yield / Generator do PHP5.5 para obter corotinas do tipo Golang, o nome do projeto é TSF (Tencent Server Framework), endereço do projeto de código aberto: https://github.com/tencent-php/tsf . Atualmente, existem aplicativos de larga escala no QQ corporativo da Tencent, no projeto de conta pública QQ e no projeto ilegal de negligenciar rodas.

O uso do TSF também é muito simples: as chamadas a seguir são 3 operações de E / S, que são completamente escritas em série. Mas, na verdade, é executado de forma assíncrona e sem bloqueio. O agendador inferior do TSF assume a execução do programa e continuará sendo executado após a conclusão do IO correspondente.

Use PHP + Swoole no Raspberry Pi

O PHP e o Swoole podem ser compilados e executados na plataforma ARM, portanto, o PHP + Swoole também pode ser usado para desenvolver programas de comunicação de rede no sistema Raspberry Pi.

Publicado 23 artigos originais · ganhou elogios 2 · Vistas 5240

Acho que você gosta

Origin blog.csdn.net/bianlitongcn/article/details/104990539
Recomendado
Clasificación