如何在PHP中实现事件监听

时间:2011-09-19 10:20:38

标签: php concurrency comet semaphore reverse-ajax

这是我的问题:我有一个脚本(让我们称之为comet.php),这是由AJAX客户端脚本提供的,并等待更改发生如下:

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

我不太喜欢这个,它不是很可扩展,它是(不好的)“糟糕的做法” 我想用信号量(?)或无论如何并发编程来改善这种行为 技术。你能告诉我一些如何处理这个问题的技巧吗? (我知道,这不是一个简短的答案,但起点就足够了。)

修改LibEvent怎么办?

5 个答案:

答案 0 :(得分:12)

您可以使用ZeroMQ解决此问题。

ZeroMQ是一个库,它提供了增压套接字,用于将东西(线程,进程甚至单独的机器)连接在一起。

我假设您正在尝试将数据从服务器推送到客户端。好吧,一个好方法是使用EventSource APIpolyfills available)。

<强> client.js

通过EventSource连接到stream.php。

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是一个非常好的方法。您可以通过ZeroMQ轻松地将socket.io连接到您的PHP代码。

另见

答案 1 :(得分:4)

这实际上取决于您在服务器端脚本中所做的事情。在某些情况下,你别无选择,只能做你上面做的事情。

但是,如果你正在做一些涉及调用函数的东西,这个函数会阻塞直到某些事情发生,你可以使用它来避免竞争而不是usleep()调用(这是恕我直言的部分将被考虑“不良做法”)。

假设您正在等待来自文件或其他类型的阻止流的数据。你可以这样做:

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

实际上,PHP是做错的语言。但有些情况(我知道因为我自己处理过它们),PHP是唯一的选择。

答案 2 :(得分:2)

尽管我喜欢PHP,但我必须说PHP不是这项任务的最佳选择。 Node.js对于这种事情来说要好得多,它的扩展性非常好。如果你有JS知识,它的实现也很简单。

现在,如果您不想浪费CPU周期,则必须创建一个PHP脚本,该脚本将连接到某个端口上的某种服务器。指定的服务器应该侦听所选端口上的连接,并且每隔X个时间检查您要检查的内容(例如,新帖子的db条目),然后将消息调度到每个已连接的客户端,新条目已准备就绪。

现在,在PHP中实现这个事件队列架构并不困难,但是使用Node.js和Socket.IO实现这一点需要5分钟,而不必担心它是否适用于大多数浏览器

答案 3 :(得分:0)

我同意这里的共识,即PHP不是最好的解决方案。您真的需要查看专用的realtime technologies来解决从服务器向客户端提供数据的异步问题。听起来你正在尝试实现HTTP-Long轮询,这对解决跨浏览器来说并不是一件容易的事。 Comet产品的开发人员已经多次解决这个问题,所以我建议你看一下Comet解决方案,或者更好的WebSocket解决方案,并为旧浏览器提供后备支持。

我建议您让PHP执行其擅长的Web应用程序功能,并为您的实时,事件,异步功能选择专用解决方案。

答案 4 :(得分:0)

您需要一个实时库。

一个例子是Ratchet http://socketo.me/

照顾pub sub的部分在http://socketo.me/docs/wamp

进行了讨论

这里的限制是PHP也需要是启动可变数据的那个。

换句话说,当MySQL更新时,这不会让你订阅。但是如果你可以编辑MySQL设置代码,那么你可以在那里添加发布部分。