• Share
  • ?
  • Profiles ▼
  • Communities ▼
  • Apps ▼

Blogs

  • My Blogs
  • Public Blogs
  • My Updates

This community can have members from outside your organization. iMasters

  • Log in to participate
fd26864d-cb41-49cf-b719-d89c6b072893 Blog

▼ Tags

▼ Similar Entries

Compile PHP, Informi...

Blog: developerWork...
Gadde_Radhika 50J9GYTSQP
Updated
0 people like thisLikes 0
No CommentsComments 0

All Application Lang...

Blog: DB2 Performan...
DaveBeulke 2700023WUN
Updated
0 people like thisLikes 0
No CommentsComments 0

Bluemix: Deploying J...

Blog: IBM Ecosystem...
Aiman Shaharuddin 270000VT8F
Updated
0 people like thisLikes 0
CommentsComments 3

Hack: Facebook's ver...

Blog: Bernie Michal...
bernie_michalik 1100004GGT
Updated
1 people likes thisLikes 1
No CommentsComments 0

New Additions to our...

Blog: Application P...
KimOConnor 270002PS4T
Updated
0 people like thisLikes 0
No CommentsComments 0

▼ Archive

  • January 2014
  • August 2013
  • July 2013
  • May 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • December 2012
  • November 2012
  • October 2012
  • September 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • April 2010

▼ Blog Authors

iMasters

View All Entries
Clicking the button causes a full page refresh. The user could go to the "Entry list" region to view the new content.) Entry list

Dicas para melhorar o Long Polling com PHP

iMasters 27000343BF | | Tags:  php ‎ | 6,274 Views

Bom, inicialmente quero deixar bem claro que PHP não é a melhor e mais adequada ferramenta para efetuar esse tipo de tarefa. Diferente do NodeJS (por exemplo), a cada requisição feita ao servidor que serve conteúdo com PHP, uma thread é criada e isso não é muito legal porque se temos várias requisições concorrentes ou ativas ao mesmo tempo, vai acabar gerando latência em ambos os lados (servidor e cliente).

No NodeJS, diferente do PHP, é possível manipular bastante conexões concorrentes em uma única thread, justamente porque, por padrão, o Node faz um servidor de thread única e de fato, é mais apropriado para manter várias conexões ativas, ou seja: efetuar o realtime com o cliente.

Mas não quer dizer que “o PHP é péssimo ou inútil” para fazer esse tipo de coisa, justamente porque nem sempre temos um cloud ou servidor dedicado de prontidão pra antender a demanda. Às vezes queremos fazer uma dinâmica melhor na aplicação, como o uso de realtime, mas o cliente não vai arcar com isso e não é estritamente necessário NodeJS para resolver o problema, portanto, o PHP pode ser a solução, não a melhor, mas será.

Entretanto, recentemente no meu desenvolvimento na web tenho utilizado a técnica long polling em algumas aplicações para entregar respostas em tempo real ao cliente, e usei alguns algoritmos para “quebrar o galho” com PHP, fiz bom uso deles e não tive nenhum problema tanto no cliente quanto no servidor, portanto, vamos lá!

1. Latência no servidor

É um problema; e é um dos piores.

A causa é porque gastamos muito processamento em um único processo e em vários clientes, ou seja, o cliente está pronto para receber respostas ao mesmo tempo que abrimos a conexão com o endpoint que faz a persistência, para entregar a resposta em tempo real, e na medida que outros clientes se conectam ao mesmo endpoint, vários processos são criados e esses processos podem estrapolar o tempo limite de execução do PHP que por padrão é de 30 segundos, e acabar gerando essa latência no servidor.

Mas, esses clientes já conectados não são atingidos, literalmente. O problema está nas novas conexões pois como vamos ter muito processamento e o servidor vai estar bastante ocupado com as requisições já abertas, não conseguimos responder a uma nova requisição em tempo hábil, mesmo que não seja diretamente ligada ao endpoint do long polling e isso é ruim pois, se alguém não consegue acessar a aplicação, ela é inútil.

A solução que eu usei pra resolver o problema foi técnicamente a mesma que o Facebook usa (sim, o Facebook também faz long polling usando PHP) que consiste em definir um tempo limite de vida para uma conexão, ou seja, se em X segundos não houver resposta, encerre a tarefa e considere uma resposta vazia e então incie novamente. Isso vai fazer com que um novo processo seja criado (abandonando o antigo), é equivalente a um “refresh”.

Normalmente, o tempo limite que eu defino é de 15 segundos.

Vejam abaixo:

echo str_pad( null , 1 ); // ecoa uma string vazia para testar a conexão
flush(); // força o envio para o cliente da string vazia

if( time() >= ( 15 + $_SERVER[ 'REQUEST_TIME' ] ) || connection_aborted() ) {
die( json_encode( array() ) ); // para o script e envia uma resposta vazia
}

2. Filas de conexão

Não é um problema, é uma otimização. Se você define um tempo limite nas conexões, quer dizer que após atingir um tempo limite e não tiver uma resposta, a conexão será abandonada com uma resposta vazia pro cliente. Aideia é assim que a requisição for feita, adicioná-la em uma fila e definir um limite de conexões nessa fila.

Quando essa fila atingir um certo limite, você faz um timeout de X segundos para limpar essa fila e iniciar uma nova sequência de requisições no servidor. Pra melhorar o algoritmo, você pode adicionar a requisição à essa fila somente se a requisição for fechada sem resposta, assim podemos considerar que provavelmente não há nenhum conteúdo recente para enviar ao cliente.

Isso ajuda o servidor a processar outras requisições com mais tranquilidade, já que alguns clientes serão inativos durante um tempo devido ao timeout que será aplicado. “Enquanto não tenho nada de imediato pra você, vou procurar algo pra outro cara”, e vice-versa.

O tempo que normalmente eu defino para esse timeout é de 30 segundos. O seguinte fragmento de código ilustra programaticamente as operações da fila:

var queue = ( [] || new Array() );
// [ ... ]
if( queue.length < 5 ) {
// [ ... ]
queue.push( this ); // adiciona a requisição na fila
} else {
// define um timeout de 30 segundos para esvaziar a fila
setTimeout( function() {
queue = ( [] || new Array() ); // limpa a fila de requisições
// [ ... ]
} , ( 30 * 1000 ) );
}

3. Aplicação em uso

Também não é um problema, é mais uma otimização.

Bom, vamos afirmar que quando o usuário não está com o foco na janela do website, a aplicação não está sendo usada, portanto podemos parar nossa fila de requisições até que o usuário volte com o foco para a janela do website.

E isso ajuda bastante, porque nem sempre você mantém uma atividade/uso constante na aplicação, então não adianta fornecer resposta ou exibir algum conteúdo que não será visto imediatamente. Quando o usuário voltar com o foco pra janela uma nova requisição será disparada em busca do conteúdo, e se ele estiver disponível, a resposta será imediata, ou seja, vai ser como se estivessemos usando constantemente.

var isInactive = ( false );
$( window ).bind( {
blur : function() {
// [ ... ]
isInactive = ( true );
},
focus : function() {
// [ ... ]
isInactive = ( false );
}
} );

Fiz uma implementação de exemplo seguindo os algoritmos acima e está disponível no GitHub. Para utilização, basta criar o banco de dados e configurar os dados de conexão com ele no arquivo /ajax/endpoint.php.

Para testar, basta abrir a index e fazer insert’s na tabela de notificações:

INSERT INTO `notifications`( `nid`, `notification` ) VALUES( NULL, 'Hello World :) ' );

É isso, abraços!

Qualquer dúvida, critíca, sugestão, por favor, não deixem de fazer!

 ***
Artigo de Andrey Knupp Vital
  • Add a Comment Add a Comment
  • Edit
  • More Actions v
  • Quarantine this Entry
Notify Other People
notification_ex

Send Email Notification

Quarantine this entry

deleteEntry
duplicateEntry

Mark as Duplicate

  • Previous Entry
  • Main
  • Next Entry
Feed for Blog Entries | Feed for Blog Comments | Feed for Comments for this Entry