вот моя проблема: у меня есть сценарий (назовем его comet.php), который подкрепляется клиентским скриптом AJAX и ждет, когда произойдут изменения:
while(no_changes){ usleep(100000); //check for changes }
Мне не нравится это слишком много, это не очень масштабируемо, и это («imho») «плохая практика». Я хотел бы улучшить это поведение с помощью семафора (?) Или в любом случае при параллельном программировании. Не могли бы вы дать мне несколько советов о том, как справиться с этим? (Я знаю, это не короткий ответ, но отправной точки было бы достаточно.)
Изменить : как насчет LibEvent ?
Вы можете решить эту проблему с помощью ZeroMQ .
ZeroMQ – это библиотека, которая обеспечивает наддувные сокеты для соединения вещей (потоки, процессы и даже отдельные машины) вместе.
Я предполагаю, что вы пытаетесь передать данные с сервера клиенту. Ну, хороший способ сделать это – использовать API EventSource ( доступные полисы ).
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); }
-<?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.
Смотрите также
Это действительно зависит от того, что вы делаете в своем скрипте на стороне сервера. Есть ситуации, когда у вас нет выбора, кроме как делать то, что вы делаете выше.
Однако, если вы делаете что-то, что связано с вызовом функции, которая будет блокироваться до тех пор, пока что-то не произойдет, вы можете использовать это, чтобы избежать гонок вместо вызова usleep()
(что является ИМХО частью, которая будет считаться «плохой практикой») ,
Предположим, что вы ожидали данных из файла или какого-либо другого потока, который блокирует. Вы можете сделать это:
while (($str = fgets($fp)) === FALSE) continue; // Handle the event here
Действительно, PHP – это неправильный язык для подобных действий. Но есть ситуации (я знаю, потому что я сам их рассматривал), где PHP – единственный вариант.
Насколько мне нравится PHP, я должен сказать, что PHP не лучший выбор для этой задачи. Node.js намного лучше подходит для такого рода вещей, и он очень хорош. Это также довольно просто реализовать, если у вас есть знание JS.
Теперь, если вы не хотите тратить процессорные циклы, вам нужно создать скрипт PHP, который будет подключаться к определенному серверу на определенном порту. Указанный сервер должен прослушивать соединения на выбранном порту и каждый X-период проверки времени для того, что вы хотите проверить (например, записи db для новых сообщений), а затем отправляет сообщение каждому подключенному клиенту, что новая запись готова.
Теперь не так сложно реализовать эту архитектуру очереди событий в PHP, но вам потребуется буквально 5 минут, чтобы сделать это с помощью Node.js и Socket.IO, не беспокоясь, будет ли это работать в большинстве браузеров.
Я согласен с консенсусом здесь в том, что PHP не лучшее решение здесь. Вам действительно нужно искать специализированные технологии реального времени для решения этой асинхронной проблемы доставки данных с вашего сервера вашим клиентам. Похоже, вы пытаетесь внедрить HTTP-Long Polling, что нелегко решить кросс-браузер. Это много раз решалось разработчиками продуктов Comet, поэтому я предлагаю вам взглянуть на решение Comet или даже на лучшее решение для WebSocket с резервной поддержкой старых браузеров.
Я бы предположил, что вы позволили PHP выполнять функциональность веб-приложения, в которой он подходит, и выбрать выделенное решение для вашей азартной функциональности в реальном времени, событий, асинхронных.
Вам нужна библиотека в реальном времени.
Одним из примеров является Ratchet http://socketo.me/
Часть, которая заботится о пабе, обсуждается на http://socketo.me/docs/wamp
Ограничение здесь заключается в том, что PHP также должен быть инициатором изменяемых данных.
Другими словами, это не волшебство позволяет вам подписаться на обновление MySQL. Но если вы можете отредактировать код настройки MySQL, вы можете добавить туда часть публикации.