Acelere o carregamento de páginas: técnicas incomuns de aumento de velocidade de sites – Parte 03
Artigo traduzido com permissão do autor, Manuel Lemos: Accelerate scripts running multiple tasks in parallel using asynchronous programming: Unusual Site Speedup Techniques – Part 3
***
Este artigo dá continuidade à série que trata de técnicas não usuais para o aumento de velocidade de sites. Os outros artigos podem ser acessados aqui e aqui.
Introdução
Muitas daslongas tarefas que os scripts no lado do servidor executam são tarefas em que os
scripts estão esperando por outro programa, hardware ou computador responder.
Por exemplo, quando seus scripts acessam um banco dedados, eles têm que estabelecer uma conexão e então executar as consultas no
banco. Na maior parte do tempo em que seu script está estabelecendo
conexões e executando consultas, ele não está fazendo mais nada além de
esperar pelo dado requerido ser enviado para o servidor do banco de dados e
pela resposta do servidor do banco.
E se ao invés de apenas esperar pelo servidor dobanco de dados receber ou responder aos pedidos, o seu script pudesse fazer
algo em paralelo? Essa é a premissa da programação assíncrona.
O
que é programação assíncrona?
Imagine que vocêestá preparando o jantar. Digamos que você terá no jantar sopa de entrada,
carne assada como prato principal e bolo de sobremesa.
Você pode preparar e cozinhar cada parte do seu
jantar uma a uma, mas como cada uma delas precisa que você utilize o forno, enquanto
uma está dentro dele, ao invés de ficar parado esperando, você pode
preparar outra parte e colocá-la no forno ao mesmo tempo.
Você pode chamar essa prática de cozinhar várias partesdo seu jantar em paralelo de cozinha assíncrona. Parece mais lógico e eficiente
fazer isso, pois você pode terminar de preparar o seu jantar muito mais rápido
se conseguir cozinhar várias partes do jantar em paralelo.
A programação assíncrona funciona da mesma maneira.
Imagine que você tenha a página de um site feita de conteúdo recuperado de um
banco de dados que está executando várias consultas. Normalmente você executa
uma consulta, espera pela resposta, recupera os resultados em algumas
variáveis, e assim por diante. Só que no final você reúne todos os dados para
gerar a página HTML final. Agora, imagine que ao invés de esperar pelos
resultados, você pudesse começar imediatamente a nova consulta enquanto o banco
de dados não devolve a resposta para a primeira consulta? Você poderia ganhar
bastante tempo, precisamente pelo mesmo motivo pelo qual você ganharia tempo se
cozinhasse várias partes do seu jantar ao mesmo tempo.
Implementando
a programação assíncrona na prática
Nas linguagens de programação de baixo nível, você precisausar chamados para o sistema operacional subjacente para saber quando o seu
programa está esperando por algum evento para, quando isso acontecer, você
acionar a ação apropriada. Se o seu programa estiver esperando por múltiplos
tipos de eventos, ele pode apurar periodicamente os diferentes recursos do
sistema para checar se algum evento aconteceu.
Seria comochecar se as diferentes partes do seu jantar sendo preparadas no forno estão
prontas antes de seguir para os próximos passos da receita.
Em linguagens de programação de alto nível,geralmente existe uma maneira mais simples de implementar a programação
assíncrona. Geralmente você chama alguma função para registrar uma função callback. Ela é invocada quando algum evento ocorre. Você
pode registrar várias funções callback, que são chamadas quando diferentes
tipos de eventos acontecem.
Implicitamente, existe algum event loop checando paraver se algum dos eventos nos quais seu programa está interessado aconteceram.
Mas você não tem que tomar conta disso diretamente no seu programa. O ambiente
de execução executa o event loop e chama suas funções callback quando
necessário.
Programação assíncrona no JavaScript
Umexemplo de linguagem de programação de alto nível que fornece suporte
intrínseco para a programação assíncrona é o JavaScript. No lado do browser,
geralmente você define qual código é chamado quando diferentes tipos de eventos
acontecem.
Você pode fazer isso, por exemplo, acionando os
atributos ON das tags das páginas (ONCLICK, ONMOUSEOVER, ONCHANGE etc).
<div id="mydiv" onclick="alert('Click!');">Click me!</div>
Alternativamente, você pode chamar funções de certoselementos objetos da página para registrar um evento funções callback, como
addEventListener.
<div id="mydiv">Click me!</div>
<script type="text/javascript"><!--
document.getElementById('mydiv').addEventListener(
'click',
function()
{
alert('Click!');
},
false);
// --></script>
Programação assíncrona do lado do servidor no JavaScript com Node.js
Os exemplosacima descrevem como funciona no lado do browser. Agora você deve estar se
perguntando: uma vez que geralmente você apenas executa um conjunto claro de
instruções em scripts do lado do servidor e então seu script sai, faz sentido
usar programação assíncrona em aplicações do lado do servidor?
Como mencionei acima, faz sentido se você precisarexecutar várias tarefas e puder executá-las em paralelo. No exemplo que
mencionei sobre recuperar os resultados de consultas múltiplas, você poderia
usar a programação assíncrona para terminar o trabalho mais depressa.
Uma abordagem de maior nível usando funções callback para eventos handle no lado do servidor seria ótimo. O JavaScript é
tradicionalmente usado no lado do browser, mas há anos existem soluções
JavaScript para scripts desenvolvidos no lado do servidor, que usam o método
event callback handler para implementar a programação assíncrona.
Uma dessas soluções é o Node.js, que é um framework que roda no topo do mecanismo JavaScriptGoogle V8. V8 é o mesmo mecanismo JavaScript que vem com o Google Chrome. Ele é bastante rápido porque usa técnicas avançadas para compilar
implicitamente o JavaScript no código nativo da máquina correspondente a sua
CPU.
Mas o V8 não precisa do Google Chrome. É ummecanismo JavaScript que pode ser incorporado em outros programas. O Node.js é
um programa que incorpora o V8. O Node.js na verdade estende o V8 para fornecer
vários módulos JavaScript que executam operações que são mais comuns no lado do
servidor de programação, como por exemplo arquivos de acesso, banco de dados e
comunicação através de rede.
Hoje em dia, o Node.js vem com tantos módulos quevocê pode implementar a maioria das tarefas que você implementaria em PHP para
aplicações do lado do servidor. Até certo nível, você pode ver o Node.js como o
PHP para JavaScript. Quer dizer, você poderia desenvolver as mesmas aplicações
do lado do servidor em JavaScript com o Node.js, como você pode fazer com o PHP.
A grande diferença entre o Node.js e o PHP é que como Node.js, por padrão, tudo é assíncrono. Isso significa que toda função do
Node.js para cada programa que você tem que esperar, a função retorna imediatamente, e o Node.js irá invocar uma função callback passada pelo seu
programa quando a tarefa da função em questão terminar.
Por exemplo, se você quiser abrir um arquivo, ler ouescrever um arquivo, listar um diretório, consultar um DNS para resolver alguns
endereços, enviar um pedido para um servidor remoto, abrir uma conexão com o
servidor de um banco de dados, executar uma consulta de banco de dados etc,
você sempre tem que passar uma função callback para lidar com o resultado da
função.
Deixe-me mostrar a vocês um simples exemplo de código
usando o Node.js para listar o diretório atual.
var fs = require('fs');
fs.readdir(".", function(error, files)
{
if(error)
throw error;
for(var f in files)
console.log("Path: " + files[f]);
}
);
Se você quiserdar uma olhada em uma situação mais complexa e ver como ela é comparada com a
programação assíncrona tradicionalmente feita em PHP, você pode checar esse
módulo Node.js para executar a validação de um endereço de e-mail checando o destino do servidor SMTP. Essa é uma parte do e-mail adddres validation class que escrevi em PHP muitos anos atrás.
Programação
assíncrona no PHP
Diferentemente do Node.js, no PHP tudo é síncrono porpadrão. Isso significa que quando você chama qualquer função, o PHP sempre
espera a função terminar seu trabalho e retornar seu resultado no final.
Atualmente o PHP não fornece nenhum tipo de suporteintrínseco para executar programação assíncrona usando callbacks. Existe algum
suporte para implementar a programação assíncrona em PHP usando a função stream_select. Essa função permite que os scripts PHP apurem arquivos abertos
ou conectores para conexões de rede para ver se ler ou escrever dados em
arquivos é possível ou se a rede bloquearia. O PHP consegue dizer à função para
esperar por um certo período até que qualquer dado seja recebido ou tenha
terminado de ser enviado.
Isso é claramente insuficiente para se ter uma programaçãoassíncrona plena no PHP. O Node.js tem suporte para implementar muitos outros
tipos de operações assíncronas, como por exemplo a checagem de arquivo
(listagem de arquivos e checagem de permissões etc…) e acesso a alguns tipos
de bancos de dados assincronamente.
A limitação não é somente da linguagem PHP. São as
extensões PHP que poderiam fornecer suporte assíncrono intrínseco, mas
atualmente elas não funcionam.
Uma solução alternativa para implementar aprogramação assíncrona no PHP é usar o Gearman. Para aqueles que não são
familiares com o Gearman, ele é um programa de middle-ware que age como um servidor
que pode receber chamados para funções implementadas por programas trabalhadores que rodam no
background.
Os programas trabalhadores podem ser escritos em PHPde forma que eles possam implementar quaisquer funcionalidades que você
precise. Programas trabalhadores podem rodar na mesma máquina ou até em
máquinas remotas. Você pode gostar de ler o artigo escrito por César Rodas
sobre como usar o Gearman com PHP para
aprender mais sobre o Gearman.
Um recurso útil do Gearman que pode habilitar aprogramação assíncrona no PHP é o suporte para chamados de trabalhos
assincronamente. Isso significa que quando um trabalho é chamado, o script da
chamada não tem que esperar pelo trabalho terminar. Coletando os resultados dos
trabalhos assíncronos pode ser complicado, então essa solução não é ideal.
Outra solução é usar a extensão pcntl do PHP para deslocar novos processos e executar os scripts PHP emparalelo. Mesmo essa solução funcionando até certo ponto, ela custa caro em
termos de CPU e memória, que são necessários para os novos processos de
bifurcação.
Conclusão
Como você já deve saber, a programaçãoassíncrona, quando possível, não é algo atualmente natural de se fazer no PHP.
Como mencionei acima, a limitação não é da linguagem em si. Algumas extensões
do PHP precisariam evoluir para suportar a programação assíncrona de um modo
mais natural, como no Node.js.
O PHP tem suporte para closures na versão 5.3. Closuressão basicamente funções anônimas que podem ser usadas diretamente para definir
uma função callback no chamado da função que tem um callback handler. Foi um
grande passo na evolução do PHP, pois elas deixam os callbacks baseados em
programação assíncrona mais amigáveis para serem desenvolvidos em PHP.
Antigamente você teria que sempre declarar funções nomeadas para usá-las como
callbacks.
Por outro lado, a programação assíncrona usandocallbacks que você vê no Node.js é complicada. Ela muda completamente a maneira
como você pensava ao definir o fluxo de controle do seu código. Por exemplo, se
você quiser executar um loop no qual o que você faz em cada iteração depende do
resultado de um evento que aciona um chamado para uma função callback, isso
fica difícil de implementar para desenvolvedores que estão acostumados somente
com códigos sincronizados.
Existem algumas técnicas sobre como trabalhar emvolta dessas esquisitices da programação assíncrona, mas esse é um escopo que
não está a alcance deste artigo. Aqui está um texto que te dá uma ideia sobre lidar com os
desafios do fluxo de controle da programação assíncrona.
Vai demorar um pouco para você se acostumar com aprogramação assíncrona usando callbacks e se tornar produtivo desenvolvendo
softwares dessa maneira.
Em resumo, eu gostaria de dizer que irá levar um
tempo para que a programação assíncrona se torne algo mais natural no PHP.
Ainda existem alguns casos em que valeria a pena usá-la, mesmo com o suporte
limitado do PHP. Tudo objetivando a otimização extrema da execução de tarefas
que podem ser feitas em paralelo, da mesma maneira que preparar seu jantar no
menor tempo possível ao colocar no forno vários tipos de comida ao mesmo tempo.
?
Texto original disponível em http://www.phpclasses.org/blog/post/133-Accelerate-scripts-running-multiple-tasks-in-parallel-using-asynchronous-programming-Unusual-Site-Speedup-Techniques-Part-3.html







