Fork me on GitHub

Grupo de desenvolvedores de PHP do estado de São Paulo
Próximos encontros e eventos? Visite a página do PHPSP no Meetup

[React PHP] Conceitos básicos – EventLoop

Por em em Artigos

Este artigo é o primeiro de uma sequência que pretendo publicar aqui. Neste entenderemos o que é React PHP e o que nos possibilita.

Outra nota importante é: esta não é uma série introdutória. O foco está em entender como React funciona, deixando de lado introduções didáticas focadas em desenvolver uma aplicação de exemplo.

 

Visão Geral

A palavra chave em React é assíncrono. Esta é a maior ideia por trás da coleção de bibliotecas que vemos consigo.

PHP, por natureza, é dito “bloqueante”. Isto significa que cada procedimento só virá a ser executado após o anterior. Ilustrado na imagem (obrigado @gabrielcouto) e código:

blocking-io

PHP é bloqueante por natureza. Fonte: http://dev.r3c.com.br/palestra-async-html/#/9, acesso em 24/12/2015 16:58

 

Estamos de fato acostumados com este cenário. Porém isto passa a ser problemático quando umArquivoPesado.txt leva alguns segundos para ser carregado à memória.

Esta mesma ideia se aplica às requisições a serviços dentro do código PHP, carregamento de configurações (XML, JSON, YAML…), acesso ao banco de dados e por aí vai. O ponto é: PHP bloqueia a cada comando executado e os comandos que fazem entrada/saída (acesso a disco, acesso a rede…) de dados tendem a ser mais demorados que comandos internos de processamento (um echo, um if, um cálculo…).

React PHP vem com o intuito, justamente, de permitir que executemos pedaços de lógica em paralelo. Nada que não pudesse ser feito com PHP antes, mas React traz uma interface orientada a objetos muito bem organizada e que nos facilita esta utilização.

 

EventLoop

Para tornar isto possível, React centraliza sua execução em um “EventLoop”, que nos permitirá alcançar a ilustração seguinte:

non-blocking-io

React não bloqueante. Fonte: http://dev.r3c.com.br/palestra-async-html/#/21, acesso em 24/12/2015 17:23

 

 

 

 

 

 

 

EventLoop nada mais é que um laço infinito (infinito até que seja interrompido ou que não possua mais processos a executar) de repetição que organiza e elege blocos de código (como são as funções) para execução. Em sua estrutura ele:

  • Gerencia processos a executar (funções, callbacks…)
  • Identifica atualizações sobre processos em paralelo
  • Executa processos referentes aos processos em paralelo

Com este modelo é possível executar procedimentos de entrada/saída (comumente demorados) em paralelo com a execução de tarefas de CPU.

Abaixo exemplifico como o EventLoop gerencia os processos a serem executados:

Acima o EventLoop deverá ter executado, a cada um segundo, os processos $callback01$callback02$controle, sendo que este último identifica que o programa encerrou as leituras necessárias e solicita o fim da execução.

É importante ressaltar que $callback01$callback02$controle não executaram em paralelo, mas o tempo de processamento destes é tão ínfimo que podemos ter esta impressão. Experimente mudar o tempo, em segundos, dos timers (linhas 23 a 25) para visualizar melhor como o EventLoop organiza e elege os processos a serem executados.

 

Timers

No exemplo de código anterior vimos como funciona o método addPeriodicTimer(), que se fizessemos um paralelo com o JavaScript seria equiparado ao setInterval().

Temos também o equivalente ao setTimeout(), que com o React se refere como addTimer() como segue:

Analisando o código acima, deverá ser impresso uma única vez a frase “Timeout veio” após 3 segundos do início da execução do programa. E deverá ser impressa infinitamente a frase “Interval veio” a cada 2 segundos.

Após 15 segundos de execução do programa realizamos uma ordem de cancelamento para o intervalo infinito caso ele esteja ativo perante o EventLoop. O EventLoop, portanto, não possuirá mais itens na fila de execução e encerra o programa na próxima iteração.

 

Streams

Até o momento não vimos nenhuma execução que de fato fosse paralela, apenas trabalhamos com filas de execução muito bem organizadas e temporizadas.

O EventLoop traz consigo uma abstração para lidar com Streams que, em php, podem ser trabalhados utilizando wrappers e/ou funções e nenhum dos dois modelos é muito intuitivo.

Precisamos sempre ter em mente que streams são encaixados em dois grupos: readable (de onde lemos dados) e writable (por onde escrevemos dados). Alguns tipos de streams se encaixam, inclusive, nos dois grupos.

O EventLoop tratará Streams utilizando os métodos addReadStream()addWriteStream(). Abaixo um exemplo de seu funcionamento:

Esta aplicação pode ser testada, por exemplo, utilizando o programa “telnet”.

Screenshot from 2015-12-27 13:36:59

Todo callback passado para addReadStream() será sempre executado quando aquele stream estiver pronto para leitura, e isto varia de acordo com o stream que estiver trabalhando: pode ser uma nova conexão recebida, ou uma mensagem recebida.

De forma análoga, addWriteStream() executará os callbacks assim que o stream estiver preparado para escrita.

 

Ticks

Por fim, talvez o mais importante, precisamos apresentar os Ticks dentro do EventLoop.

Como dito anteriormente, o mecanismo do EventLoop não passa de um loop infinito. Literalmente, veja este trecho retirado de React\EventLoop\StreamSelectLoop:

Cada vez que entramos neste laço (linha 05) o EventLoop dispara filas de execução eleitas para aquela iteração (tick).

Na implementação StreamSelectLoop, um tick inicia a execução (nesta ordem):

  • Da fila nextTickQueue
  • Da fila futureTickQueue
  • Dos timers registrados (addTimer()addPeriodicTimer())
  • Dos callbacks registrados para streams (addReadStream()addWriteStream())

Podemos manipular as filas nextTickQueuefutureTickQueue através dos métodos nextTick()futureTick(), respectivamente. Eles recebem funções (callable) como parâmetro:

Eles realmente parecem fazer a mesma coisa, mas existe uma sutil difereça entre os dois:

  • Next ticks executarão enquanto existirem callbacks em sua fila de execução
  • Future ticks executarão somente os callbacks existentes na fila no momento em que foram inicializados

O exemplo abaixo ilustra melhor esta diferença:

Este future tick envia a mesma função para a fila de future ticks assim que executa, depois solicita o fim do EventLoop. A função futureTick(), porém, executará uma unica vez, pois quando a fila de future ticks iniciou a execução, existia somente uma ocorrência para executar.

Veja a diferença com os next ticks:

Este programa entrará num loop infinito, pois ao fim de nextTick() esta função é enviada novamente para a fila de next ticks e esta fila executa enquanto existirem callbacks, independentemente do momento em que foram adicionados.

 

Implementações de EventLoop

Se você notar, no início deste texto, instanciamos o EventLoop utilizando a React\EventLoop\Factory. Isto porque, atualmente, existem quatro implementações diferentes do EventLoop (todas devem respeitar a interface React\EventLoop\LoopInterface) e a Factory irá instanciar o primeiro disponível. São as implementações:

  • ExtEventLoop (Utiliza a extensão Event)
  • LibEvLoop (Utiliza a extensão Libev)
  • LibEventLoop (Utiliza a extensão LibEvent)
  • StreamSelectLoop (Não utiliza extensões)

Destas, somente StreamSelectLoop funciona somente com PHP pois faz uso de stream_select(). O restante das implementações somente serão instanciadas quando a devida extensão existir.

Em questão de performance, StreamSelect é a implementação menos perfeita. As outras implementações delegam a gerência de entrada/saída às extensões. A medição de performance, porém, dependerá do seu cenário de uso e, algumas vezes, pode ser mais interessante instanciar o EventLoop manualmente em vez de depender da Factory para escolher o melhor para você.

 

Conclusão

Vimos aqui as funcionalidades básicas do EventLoop, como ele se comporta e como está desacoplado do restante das bibliotecas React.

Através destas explicações você já será capaz de criar bibliotecas que interajam com o React, assim como desenvolver novas implementações de EventLoops, modificar existentes ou mesmo contribuir com melhorias neste pacote do projeto.

Tags: , , , , ,

Sobre Níckolas Silva

Desenvolvedor php há aproximadamente 4 anos, Níckolas é tecnólogo em Análise e Desenvolvimento de Sistemas, pós graduando em Engenharia de Software com foco em SOA e já esteve envolvido com desenvolvimento de sistemas de telecom e de gestão. Hoje dedica horas de seus dias ao estudo de tecnologias, práticas e técnicas sobre sistemas distribuídos, procurando integrar todas as áreas de atuação através destes estudos. Além disso está presente em todos eventos PHPSP+Pub, pois acredita que conversas, pessoas e cerveja são a chave para aprender e fortalecer conceitos.

Mais posts de .

  • Alexsandro Souza

    Parabéns Nickolas, muito bom o artigo e sobre algo bem interessante. Espero que você dedique um Capítulo dessa série sobre como podemos gerenciar esses processos, uma vez que o php é single thread e nessa abordagem se tivermos algum erro fatal nossa aplicação para é toda nova “requisição” não será processada. Em JS já temos libs que resolve muito bem esse problema, já temos algo assim em PHP/ React?
    Parabenizo vc novamente pq faz tempo que desejo escrever sobre event loop, asynch e sobre essa opção de não precisar levantar toda a aplicação/ framework a cada requisição, mas não consegui ainda, quem sabe serei um colaborador da sua série ainda. Flw abraço!

  • Nickolas Silva

    Obrigado pelo feedback, Alexsandro. Está anotado e, provavelmente, irei tentar trabalhar em cima disso sim :)

    Quanto à questão de a aplicação ser fail-safe, isto foge um pouco do React. React PHP é montado em cima do padrão Reactor, mas para resolver o seu problema talvez seja mais interessante saber sobre o padrão Reactive.

    Os nomes confudem/misturam, mas têm muito a oferecer.

    Enquanto o Reactor trata simplesmente do modelo de implementação, o padrão Reactive está muito mais voltado à arquitetura de sua aplicação e como esta trabalhará não só com eventos mas também com situações de falha.

    Se quiser sair na frente e saber mais sobre antes de eu tentar escrever algo, recomendo estas duas palestras muito boas acerca do tema:

    https://www.youtube.com/watch?v=r4bJqgqpsIQ

    https://www.youtube.com/watch?v=4L3cYhfSUZs

    Ficarei muito grato se pudermos discutir mais sobre. Talvez daqui até saiam algumas contribuições com o ReactPHP ou Icicle ^^

  • Parabéns! Artigo de peso.. Nickolas, tem um erro de digitação, ta: condigurações..

    Absss

  • não tem mais ^^
    Obrigadão, Sérgio.

  • Maico Borges

    Parabéns! Muito bom artigo.

  • Obrigado, Maico! Espero que tenha sido util (:

  • Pingback: [React PHP] Streams e Sockets | Grupo de desenvolvedores de PHP do estado de São Paulo()

  • Pingback: [React PHP] Streams e Sockets – Tudo sobre PHP()