Simple implementation of long polling mechanism in PHP
Nowadays, the use of Comet-technology is quite popular, “when a constant HTTP connection allows the web server to send (push) data to the browser without an additional request from the browser”, according to Wikipedia.
There are many different implementations of this technology, but now I want to dwell on one of them, which is called Long Polling. In the article I will analyze what it is and what it is eaten with.
Well, for those who know that it may be interesting to look at an implementation that does not use third-party software for its work - only PHP. Why is this necessary if there are special comet-servers that can withstand much higher loads than a PHP script? This can come in handy if you need to make a small project without high workloads, and you are not allowed to install third-party software on the hosting. Well, if I had to implement this functionality in one task, where it was just impossible to use third-party applications, then why not share it.
So what is Long Polling?
It looks like this:
1) The client sends a regular ajax request to the server
2) The server, instead of quickly processing this request and sending a response to the client, starts a cycle, in each iteration of which it monitors the occurrence of events (another client added an entry or deleted).
3) When an event occurs, the server generates a response and sends it to the client, thus completing the request.
4) The client, having received a response from the server, starts the event handler and simultaneously sends another “long” request to the server.
That is, everything is quite simple and clear. However, the implementation raises several issues that need to be addressed.
First of all, the question arises as to how scripts will interact with each other. Indeed, for each client request to the server an independent copy of the PHP script is created. That is, you need some kind of shared memory to store event data.
The traditional option is to run a daemon that operates on events and keeps a connection with clients. Using a daemon naturally solves the memory problem. Clients do not turn to a web server to receive events, but to a daemon. At the same time, the client initiating the event also informs the daemon that the event has occurred. And so everything revolves in the memory of this demon. which can generally be written on anything.
But to us, in view of sewing in one place and the desire to do without third-party applications, the demon will not do anything. And writing a daemon in PHP, for my taste, is not very interesting. The hoster may have problems with changing the maximum running time of the script, and also think over the interfaces for client interaction with this daemon ... No, we will go the other way.
All that remains is to decide how to implement shared memory in PHP itself. The options for storing events in files and databases come to mind, but I want something to be closer - in memory. Memcache, alas, is unavailable to us, but what then to do? And in php there is such a thing as shared memory, more precisely, the concept is not a feature of this language, but they give us the opportunity to use it. The mechanisms of this technology allow us to create a cell in RAM and use it for our own purposes. Here I will use it.
Let's try to introduce a class interface that will implement the functionality we need.
What methods are needed?
1) Naturally, the “wiretapping” method, which will be called when clients send polling requests, and monitor the events that occur. I called this listen method .
2) In addition, we need a method that will create an event. This method is called push .
3) It is likely that we will want to process the event data before sending the result to the client. Therefore, I added a method that registers the event and associates a handler with it. The method is called registerEvent .
As a result, we get the following class interface:
A few notes:
1) In the registerEvent method , only one handler per event is supported because the value returned by the handler is returned to the client as event data. In one handler, you can call several functions and collect the response from the result of their work to the client. However, you can redo the code to support multiple handlers without much difficulty.
2) The listen method uses the lastQueryTime parameter , which allows you to process events that occurred before a listen request was received. This can come in handy when the load on the network is high, and between the completion of one polling request from the client and the start of the other, a noticeable period of time may elapse in which events occur.
3) The above code does not take into account simultaneous access to memory. But in general. for more reliable work, it is desirable to use semaphores.
4) The listen method uses a call to the sleep (1) function . This is necessary in order to reduce the number of idle iterations. A refresh rate of once per second is sufficient to simulate real time.
Well, on the client everything is also extremely simple. We need a method that will send a polling request to the server, wait for a response, start the event handlers and resend the request. Well, the method that registers the event handlers themselves.
Sources:
The described Polling class in PHP .
If someone needs it, I’ll put it in order and lay out the client part.
There are many different implementations of this technology, but now I want to dwell on one of them, which is called Long Polling. In the article I will analyze what it is and what it is eaten with.
Well, for those who know that it may be interesting to look at an implementation that does not use third-party software for its work - only PHP. Why is this necessary if there are special comet-servers that can withstand much higher loads than a PHP script? This can come in handy if you need to make a small project without high workloads, and you are not allowed to install third-party software on the hosting. Well, if I had to implement this functionality in one task, where it was just impossible to use third-party applications, then why not share it.
Theory
So what is Long Polling?
It looks like this:
1) The client sends a regular ajax request to the server
2) The server, instead of quickly processing this request and sending a response to the client, starts a cycle, in each iteration of which it monitors the occurrence of events (another client added an entry or deleted).
3) When an event occurs, the server generates a response and sends it to the client, thus completing the request.
4) The client, having received a response from the server, starts the event handler and simultaneously sends another “long” request to the server.
That is, everything is quite simple and clear. However, the implementation raises several issues that need to be addressed.
Steps to Practice
First of all, the question arises as to how scripts will interact with each other. Indeed, for each client request to the server an independent copy of the PHP script is created. That is, you need some kind of shared memory to store event data.
The traditional option is to run a daemon that operates on events and keeps a connection with clients. Using a daemon naturally solves the memory problem. Clients do not turn to a web server to receive events, but to a daemon. At the same time, the client initiating the event also informs the daemon that the event has occurred. And so everything revolves in the memory of this demon. which can generally be written on anything.
But to us, in view of sewing in one place and the desire to do without third-party applications, the demon will not do anything. And writing a daemon in PHP, for my taste, is not very interesting. The hoster may have problems with changing the maximum running time of the script, and also think over the interfaces for client interaction with this daemon ... No, we will go the other way.
All that remains is to decide how to implement shared memory in PHP itself. The options for storing events in files and databases come to mind, but I want something to be closer - in memory. Memcache, alas, is unavailable to us, but what then to do? And in php there is such a thing as shared memory, more precisely, the concept is not a feature of this language, but they give us the opportunity to use it. The mechanisms of this technology allow us to create a cell in RAM and use it for our own purposes. Here I will use it.
Practice
Let's try to introduce a class interface that will implement the functionality we need.
What methods are needed?
1) Naturally, the “wiretapping” method, which will be called when clients send polling requests, and monitor the events that occur. I called this listen method .
2) In addition, we need a method that will create an event. This method is called push .
3) It is likely that we will want to process the event data before sending the result to the client. Therefore, I added a method that registers the event and associates a handler with it. The method is called registerEvent .
As a result, we get the following class interface:
class Polling
{
/**
* Генерация события
* @param string $event Имя события
* @param array $data Параметры события
*/
public function push($event, $data = null);
/**
* "Слежение"за возникновением событий. Время работы зависит от опции max_execution_time
* @param int $lastQueryTime Время завершения последнего запроса на "прослушивание".
* Используется для того ,чтобы при медленном соединении не терялись события, возникшие
* между запросами. Если не указан, то события отслеживаются с момента начала запроса
* @return array Массив с результатами выполнения обработчиков для возникших событий
*/
public function listen($lastQueryTime = null);
/**
* Регистрация события и назначение обработчика. Поддерживается по одному обработчику на событие.
* @param string $event Имя события
* @param callback $callback Функция-обработчик
*/
public function registerEvent($event, $callback);
}
A few notes:
1) In the registerEvent method , only one handler per event is supported because the value returned by the handler is returned to the client as event data. In one handler, you can call several functions and collect the response from the result of their work to the client. However, you can redo the code to support multiple handlers without much difficulty.
2) The listen method uses the lastQueryTime parameter , which allows you to process events that occurred before a listen request was received. This can come in handy when the load on the network is high, and between the completion of one polling request from the client and the start of the other, a noticeable period of time may elapse in which events occur.
3) The above code does not take into account simultaneous access to memory. But in general. for more reliable work, it is desirable to use semaphores.
4) The listen method uses a call to the sleep (1) function . This is necessary in order to reduce the number of idle iterations. A refresh rate of once per second is sufficient to simulate real time.
Well, on the client everything is also extremely simple. We need a method that will send a polling request to the server, wait for a response, start the event handlers and resend the request. Well, the method that registers the event handlers themselves.
Sources:
The described Polling class in PHP .
If someone needs it, I’ll put it in order and lay out the client part.