У меня есть разъем, который вызовет RESP API, используя cURL и PHP.
Мне нужно вызывать один метод каждую секунду, чтобы проверять новые сообщения, а затем обрабатывать их. Я использовал следующие два подхода для обработки сообщений
SetInterval()
: вызывать скрипт php один раз в секунду. Это отлично работает, за исключением того, что я не могу одновременно запустить несколько SetInterval()
из разных вкладок браузера. (Я не хочу, чтобы пользователь открыл 10 вкладков браузера, что приводит к тому, что один пользователь, имеющий 10 SetInterval()
, работает одновременно. EventSource
: сервер отправит обновление браузера после появления новых данных в очереди. Это замедляет время ответа. Каждый вызов, который я делаю для скрипта, занимает около 20 + секунд, чтобы завершить, что является проблемой. Я не знаю, почему это происходит. Вот моя реализация SetInterval()
function startCalls(){ //update the screen using Intervals refreshIntervalId = setInterval(function() { $.getJSON("index.php", {'method': 'getMessages', 'jSON': true} , function(data){ processServerData(data); }); }, 1000); }
как только пользователь входит в систему, я startCalls()
эту функцию startCalls()
внутри файла index.php У меня есть этот код для вызова
if($method == 'getMessages'){ $messaging = new ICWS\Messaging($icws); $messaging->processMessages(); $myQueue = $messaging->getCallsQueue(); echo json_encode($myQueue ); }
Вот моя вторая реализация «Server-Sent Events»
//Server Side Message Polling function startPolling(evtSource){ evtSource.addEventListener("getMessagingQueue", function(e) { var data = JSON.parse(e.data); processServerData(data) }, false); }
как только пользователь входит в систему, я startPolling( new EventSource("poll.php") );
эту функцию startPolling( new EventSource("poll.php") );
Для простоты предположим, что мой метод processServerData
выглядит так
function processServerData(data){ console.log('process the data received from the server'); }
вот мой php-код
<?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(); } ?>
в<?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(); } ?>
Кажется, что сервер блокирует все запросы, которые попадают на сервер, до тех пор, пока бесконечный цикл не остановится с помощью «обновления страницы», как только я обновляю страницу, остальные запросы обрабатываются немедленно
Почему событие Server-Sent вызвало такую проблему?
В этом вопросе можно найти большой ресурс для разных типов полинга
Все выглядит уверенно, поэтому я собираюсь предположить, что вас атакует блокировка сеанса. Сеансы PHP блокируют файл сеанса, так что только один скрипт PHP может использовать сеанс за раз; когда вы думаете об этом, это отличная идея!
Проблема с сеансами и SSE заключается в том, что процесс SSE PHP работает навсегда, и поэтому он блокирует сеанс навсегда. Если какой-либо другой PHP-скрипт пытается работать с одним и тем же сеансом, он будет блокировать (на вызове session_start()
, я считаю).
Это похоже на хорошую статью по этому вопросу; совет – вызвать session_write_close()
только вам станет нужно больше сеанса. Например, если вам просто нужно использовать сеанс, чтобы проверить, что они ранее разрешили себя, то сразу после этого вы вызываете session_write_close()
, а другие процессы не будут заблокированы.