Как реализовать прослушивание событий в PHP - PullRequest
7 голосов
/ 19 сентября 2011

вот моя проблема: у меня есть сценарий (назовем его comet.php), который пересчитан клиентским сценарием AJAX и жду, когда произойдут такие изменения:

while(no_changes){
    usleep(100000);
    //check for changes
}

Мне это не очень нравится, это не очень масштабируемо и (imho) "плохая практика" Я хотел бы улучшить это поведение с помощью семафора (?) Или в любом случае параллельного программирования техника. Можете ли вы дать мне несколько советов, как справиться с этим? (Я знаю, это не короткий ответ, но отправной точки будет достаточно.)

Редактировать : как насчет LibEvent ?

Ответы [ 5 ]

13 голосов
/ 29 декабря 2011

Вы можете решить эту проблему, используя ZeroMQ .

ZeroMQ - это библиотека, которая предоставляет сокеты с наддувом для объединения вещей (потоков, процессов и даже отдельных машин).

Я предполагаю, что вы пытаетесь передать данные с сервера на клиент. Что ж, хороший способ сделать это - использовать EventSource API ( полифилы доступны ).

client.js

Подключается к stream.php через EventSource.

var stream = new EventSource('stream.php');

stream.addEventListener('debug', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

stream.addEventListener('message', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

router.php

Это длительный процесс, который прослушивает входящие сообщения и отправляет их любому слушающему.

<?php

$context = new ZMQContext();

$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind("tcp://*:5555");

$pub = $context->getSocket(ZMQ::SOCKET_PUB);
$pub->bind("tcp://*:5556");

while (true) {
    $msg = $pull->recv();
    echo "publishing received message $msg\n";
    $pub->send($msg);
}

stream.php

Каждый пользователь, подключающийся к сайту, получает свой stream.php. Этот скрипт долго работает и ждет любых сообщений от роутера. Получив новое сообщение, оно выведет это сообщение в формате EventSource.

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_SUB);
$sock->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "");
$sock->connect("tcp://127.0.0.1:5556");

set_time_limit(0);
ini_set('memory_limit', '512M');

header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

while (true) {
    $msg = $sock->recv();
    $event = json_decode($msg, true);
    if (isset($event['type'])) {
        echo "event: {$event['type']}\n";
    }
    $data = json_encode($event['data']);
    echo "data: $data\n\n";
    ob_flush();
    flush();
}

Чтобы отправлять сообщения всем пользователям, просто отправьте их на маршрутизатор. Затем маршрутизатор распространит это сообщение во всех прослушиваемых потоках. Вот пример:

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_PUSH);
$sock->connect("tcp://127.0.0.1:5555");

$msg = json_encode(array('type' => 'debug', 'data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

$msg = json_encode(array('data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

Это должно доказать, что вам не нужен node.js для программирования в реальном времени. PHP может справиться с этим просто отлично.

Кроме того, socket.io - действительно хороший способ сделать это. И вы можете легко подключиться к socket.io к своему PHP-коду через ZeroMQ.

См. Также

4 голосов
/ 19 сентября 2011

Это действительно зависит от того, что вы делаете в скрипте на стороне сервера. В некоторых ситуациях у вас нет выбора, кроме как делать то, что вы делаете выше.

Однако, если вы делаете что-то, что включает в себя вызов функции, которая будет блокировать до тех пор, пока что-то не произойдет, вы можете использовать это, чтобы избежать гонок вместо вызова usleep() (который ИМХО считается частью, которая будет считаться "плохой" практика »).

Скажем, вы ожидали данных из файла или другого потока, который блокирует. Вы могли бы сделать это:

while (($str = fgets($fp)) === FALSE) continue;
// Handle the event here

Действительно, PHP - не тот язык, чтобы делать подобные вещи. Но есть ситуации (я знаю, потому что я имел дело с ними сам), где PHP является единственным вариантом.

2 голосов
/ 19 сентября 2011

Как бы мне ни нравился PHP, я должен сказать, что PHP не лучший выбор для этой задачи.Node.js намного, намного лучше для такого рода вещей, и он действительно хорошо масштабируется.Это также довольно просто реализовать, если у вас есть знания JS.

Теперь, если вы не хотите тратить впустую циклы ЦП, вам нужно создать скрипт PHP, который будет подключаться к какому-либо серверу на определенномпорт.Указанный сервер должен прослушивать соединения на выбранном порту и каждые X проверять количество времени на предмет того, что вы хотите проверить (например, записи в базе данных для новых сообщений), а затем отправляет каждому подключенному клиенту сообщение о том, что новая запись готова.

Теперь не так сложно реализовать эту архитектуру очереди событий в PHP, но вам потребуется буквально 5 минут, чтобы сделать это с Node.js и Socket.IO, не беспокоясь о том, будет ли это работать вбольшинство браузеров.

0 голосов
/ 19 апреля 2016

Вам нужна библиотека в реальном времени.

Одним из примеров является Ratchet http://socketo.me/

Часть, которая заботится о пабе sub, обсуждается в http://socketo.me/docs/wamp

Ограничение здесь заключается в том, что PHP также должен быть тем, кто инициирует изменяемые данные.

Другими словами, это волшебным образом не позволит вам подписаться на обновления MySQL. Но если вы можете редактировать код настройки MySQL, то можете добавить туда часть публикации.

0 голосов
/ 20 сентября 2011

Я согласен с тем, что PHP здесь не лучшее решение. Вам действительно нужно обратить внимание на выделенные технологии реального времени для решения этой асинхронной проблемы доставки данных с вашего сервера вашим клиентам. Похоже, вы пытаетесь реализовать HTTP-длинный опрос, который не так-то просто решить кросс-браузер. Это неоднократно решалось разработчиками продуктов Comet, поэтому я бы посоветовал вам взглянуть на решение Comet или, что еще лучше, на решение WebSocket с резервной поддержкой для старых браузеров.

Я бы посоветовал вам позволить PHP выполнять функциональность веб-приложения, в которой он хорош, и выбрать специальное решение для вашей асинхронной функциональности в реальном времени.

...