I have a connector that will call a RESP API using cURL and PHP.
I need to call one method every second to check for new messages and then process them. I used the following 2 approaches to handle the messages
- AJAX Polling using
SetInterval()
: call the php script once every second. This works perfectly except I am unable to prevent multipleSetInterval()
from running at the same time from different tabs of the browser. ( I don't want to have user opens 10 browser tabs which leads to one user having 10SetInterval()
are running at the same time. - Server Sent Events using
EventSource
: The server will send update the browser once there are new data in the queue. This slows down the respond time. Every call I make to the script takes about 20+ seconds to complete which is a problem. I am not sure why this is happening.
Here is my SetInterval()
implementation
function startCalls(){
//update the screen using Intervals
refreshIntervalId = setInterval(function() {
$.getJSON("index.php", {'method': 'getMessages', 'jSON': true} , function(data){
processServerData(data);
});
}, 1000);
}
once a user logs in I calls this function startCalls()
inside the index.php file I have this code to be called
if($method == 'getMessages'){
$messaging = new ICWS\Messaging($icws);
$messaging->processMessages();
$myQueue = $messaging->getCallsQueue();
echo json_encode($myQueue );
}
Here is my second implementation "Server-Sent Events"
//Server Side Message Polling
function startPolling(evtSource){
evtSource.addEventListener("getMessagingQueue", function(e) {
var data = JSON.parse(e.data);
processServerData(data)
}, false);
}
once a user logs in I calls this function startPolling( new EventSource("poll.php") );
For the sake of simplicity, assume that my processServerData
method looks like this
function processServerData(data){
console.log('process the data received from the server');
}
here is my php code
<?php
require 'autoloader.php';
//Make sure cURL is enabled on the server
if(!is_callable('curl_init')){
exit('cURL is disabled on this server. Before making API calls cURL extension must be enabled.');
}
if( isset($_COOKIE['icws_username']) ){
$user = trim($_COOKIE['icws_username']);
}
if( isset($_COOKIE['icws_password']) ){
$pass = trim($_COOKIE['icws_password']);
}
if( isset($_COOKIE['icws_station']) ){
$stationName = trim($_COOKIE['icws_station']);
}
if( !isset($user, $pass, $stationName) ){
echo json_encode(array('status' => 'The IC credentials you entered are invalid'));
exit;
}
$scheme = 'http';
$host = 'host';
$port = '8018';
$sleepTime = 1;
$url = sprintf('%s://%s:%s@%s:%s', $scheme, $user, $pass, $host, $port);
try {
header("Content-Type: text/event-stream\n\n");
session_start();
//configure the connection
$conf = new ICWS\Config\Config($url, $stationName);
//create a new instance of icws
$attrebutes = array();
$icws = new ICWS\Connection($conf, $attrebutes, true);
$messaging = new ICWS\Messaging($icws);
ob_end_clean();
while(true){
header("Content-Type: text/event-stream" . PHP_EOL);
header("Cache-Control: no-cache" . PHP_EOL);
$messaging->processMessages();
$result = $messaging->getCallsQueue();
//$current = $icws->getCurrentUserStatusQueue();
echo 'event: getMessagingQueue' . PHP_EOL;
echo 'data: ' . json_encode( $result) . PHP_EOL;
echo PHP_EOL; //required
ob_end_flush();
flush();
sleep(1);
}
} catch(Exception $e){
echo $e->getMessage();
}
?>
The server seems to lock all requests that hit the server until the infinite loop is stopped by "page refresh" as soon as I refresh the page the other requests are processes immediately
Why would the Server-Sent Event cause such an issue?
A great resource for the different types of poling can be found in this Question
Everything looks robust, so I'm going to take a guess that you are being hit by session locking. PHP sessions lock the session file, such that only one PHP script can use the session at a time; when you think about it, this is a great idea!
The problem with sessions and SSE is that the SSE PHP process runs forever, and therefore it locks the session forever. If any other PHP script tries to run with the same session, it will block (at the
session_start()
call, I believe).This looks like a good article on the subject; the advice is to call
session_write_close()
once you know longer need the session. E.g. if you just need to use the session to check they have previously authorized themselves, then straight after that you callsession_write_close()
, and other processes will not get blocked.