У меня есть простой длинный запрос на опрос на моем веб-сайте, чтобы проверить, есть ли какие-либо новые уведомления, доступные для пользователя. Что касается запроса, все работает безупречно; запрос выполняется только после обновления базы данных (и для этого конкретного пользователя создано уведомление), и сразу после этого отправляется новый запрос.
Что еще более странно, так это то, что если я зайду на другой из моих localhost сайтов (мой длинный опрос проводится на сайте виртуального хоста MAMP, www.example.com), это не проблема, и я все еще могу использовать их, как будто ничего не произошло - несмотря на то, что они' re технически на том же сервере.
Это то, что у меня есть на стороне клиента (longpoll.js):
window._Notification = {
listen: function(){
/* this is not jQuery's Ajax, if you're going to comment any suggestions,
* please ensure you comment based on regular XMLHttpRequest's and avoid
* using any suggestions that use jQuery */
xhr({
url: "check_notifs.php",
dataType: "json",
success: function(res){
/* this will log the correct response as soon as the server is
* updated */
console.info(res);
_Notification.listen();
}
});
}, init: function(){
this.listen();
}
}
/* after page load */
_Notification.init();
И это то, что у меня есть на стороне сервера (check_notifs.php):
header("Content-type: application/json;charset=utf-8", false);
if (/* user is logged in */){
$_CHECKED = $user->get("last_checked");
/* update the last time they checked their notifications */
$_TIMESTAMP = time();
$user->update("last_checked", $_TIMESTAMP);
/* give the server a temporary break */
sleep(1);
/* here I am endlessly looping until the conditions are met, sleeping every
* iteration to reduce server stress */
$_PDO = new PDO('...', '...', '...');
while(true){
$query = $_PDO->prepare("SELECT COUNT(*) as total FROM table WHERE timestamp > :unix");
if ($query->execute([":unix" => $_CHECKED])){
if ($query->rowCount()){
/* check if the database has updated and if it has, break out of
* the while loop */
$total = $query->fetchAll(PDO::FETCH_OBJ)[0]->total;
if ($total > 0){
echo json_encode(["total" => $total]);
break;
}
/* if the database hasn't updated, sleep the script for one second,
* then check if it has updated again */
sleep(1);
continue;
}
}
}
}
/* for good measure */
exit;
Я читал о NodeJS и различных других фреймворках, которые предлагаются для длинных опросов, но, к сожалению, в настоящее время они мне недоступны, и я вынужден использовать PHP. Я также осмотрелся, чтобы посмотреть, может ли что-нибудь в конфигурации Apache решить мою проблему, но я наткнулся только на Как увеличить максимальное количество одновременных подключений в Apache?, и то, что упоминалось, не похоже на проблему, учитывая, что я все еще могу использовать свой другой localhost веб-сайт на сервер такой же.
Я действительно смущен тем, как я могу решить эту проблему, поэтому любая помощь приветствуется,
Cheers.
Кроме того, ваш запрос всегда один и тот же в каждом цикле, поэтому цикл здесь кажется бесполезным, потому что вы всегда будете возвращать одни и те же данные. Чтобы изменить это, переместите элементы $_CHECKED, $_TIMESTAMP и $user->... в цикл while.
@GetOffMyLawn смысл цикла в том, чтобы постоянно проверять, обновляется ли база данных :) Но проблема не в этом (сам длинный опрос работает правильно), проблема в том, что он зависает при каждом другом запросе к этому серверу [virtualhost?]
Поднимите пожалуйста @Community
Переместите $_PDO = new PDO('...', '...', '...'); за пределы while-цикла и посмотрите, сохраняется ли проблема.
@huysenruitw хорошо! единственная причина, по которой я это делаю, в первую очередь заключается в том, что я могу закрыть соединение, но не навсегда :) см. $_PDO = null;
Почему вы удаляете соединение PDO и создаете новое на каждой итерации? Также я склонен думать, что это больше проблема конфигурации веб-сервера. У вас есть конкретная конфигурация Apache? Вы используете mod-php или php-fpm? Если FPM, пожалуйста, опубликуйте эту конфигурацию.
Закройте соединение PDO после цикла while.
@ferdynator в настоящее время использую конфигурацию MAMP по умолчанию. Так что я предполагаю, что это будет mod_php :)
IMO PHP-скрипты не должны так зависать... Утечка памяти, вероятно, будет огромной... Почему бы вам просто не запрашивать PHP каждые 10 секунд на стороне клиента, чтобы проверить, существуют ли новые уведомления?
@CarlosAlvesJorge, потому что я пытаюсь избежать бессмысленных HTTP-запросов. По мнению значительной части сообщества программистов (ну, по крайней мере, из того, что я видел), длинный опрос намного эффективнее, чем короткий опрос.
@ГРУВЕР. Это никогда не будет более эффективным, если у вас есть бесконечный цикл, запрашивающий вашу базу данных... По сути, вы сами будете DDNS всего с парой пользователей...
@CarlosAlvesJorge, но это основная идея длительного опроса ... постоянная проверка того, была ли база данных обновлена на стороне сервера
@ГРУВЕР. Длинный пул используется для последовательной проверки изменений событий (которые в идеале не требуют больших ресурсов), чтобы использовать его для бесконечного выполнения «SELECT COUNT (*)» в базе данных — это призыв к катастрофе. Если вы не можете использовать веб-сокеты для уведомлений в реальном времени, просто выполните короткий пул на стороне клиента. Если вы хотите сохранить свой подход, по крайней мере, дайте гораздо больший тайм-аут, чем одна секунда...
@CarlosAlvesJorge, как Facebook, Twitter и Instagram делали это в те дни, когда WebSockets не были доступны?
Кроме того, контролируя клиентскую сторону, вы можете прекратить делать запросы, когда окно не сфокусировано или когда нет движения мыши дольше периода. Я бы хотел, чтобы люди постоянно пользовались моей базой данных, когда они забывают об открытии сеанса...



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


На самом деле происходит то, что php ожидает завершения этого скрипта (заблокированного), чтобы обслуживать следующие запросы к тому же файлу.
Как вы можете прочитать здесь:
- there is some lock somewhere -- which can happen, for instance, if the two requests come from the same client, and you are using file-based sessions in PHP : while a script is being executed, the session is "locked", which means the server/client will have to wait until the first request is finished (and the file unlocked) to be able to use the file to open the session for the second user.
- the requests come from the same client AND the same browser; most browsers will queue the requests in this case, even when there is nothing server-side producing this behaviour.
- there are more than MaxClients currently active processes -- see the quote from Apache's manual just before.
На самом деле где-то есть какой-то замок. Вам нужно проверить, какая блокировка происходит. Возможно, у $_PDO есть блокировка, и вы должны закрыть ее до sleep(1), чтобы она оставалась разблокированной, пока вы не сделаете следующий запрос.
Вы можете попробовать повысить свой MaxClients и/или применить этот ответ
Perform
session_write_close()(or corresponding function in cakephp) to close the session in the begin of the ajax endpoint.
Где я должен разместить функцию session_write_close?
@ГРУВЕР. В начале файла. Но имейте в виду, что это было потому, что в этом случае это был замок. В вашем случае вы должны искать свой замок. Вы используете сеансы?
Я использую сеансы :), но только для определенных пользовательских данных
@ГРУВЕР. у вашего $user может быть блокировка сеанса. Попробуйте разблокировать после последнего $user->update или чего-то еще, что требует открытой сессии.
Поздравляю с репутацией 8K. ;)
Ваша проблема, скорее всего,
if ($total > 0){, вероятно, ничто не возвращает значение больше нуля, поэтому цикл никогда не заканчивается.