8

这是我的问题:我有一个 AJAX 客户端脚本要求的脚本(我们称之为 comet.php),并等待发生这样的更改:

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

我不太喜欢这个,它的可扩展性不是很好,而且它(恕我直言)是“不好的做法”,我想用信号量(?)或任何并发编程技术来改善这种行为。你能给我一些关于如何处理这个问题的提示吗?(我知道,这不是一个简短的答案,但一个起点就足够了。)

编辑LibEvent呢?

4

5 回答 5

14

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

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

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

客户端.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]);
});

路由器.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

每个连接到该站点的用户都有自己的 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 代码。

也可以看看

于 2011-12-28T20:30:21.493 回答
4

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

但是,如果您正在做的事情涉及调用将阻塞直到发生某些事情的函数,您可以使用它来避免赛车而不是usleep()调用(恕我直言,这将被认为是“不好的做法”)。

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

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

真的,PHP 是做这种事情的错误语言。但是在某些情况下(我知道,因为我自己处理过它们)PHP 是唯一的选择。

于 2011-09-19T10:28:42.647 回答
2

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

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

现在,在 PHP 中实现这个事件队列架构并没有那么难,但是用 Node.js 和 Socket.IO 来完成这件事实际上需要 5 分钟,而不用担心它是否能在大多数浏览器中工作。

于 2011-09-19T10:49:24.303 回答
0

我同意这里的共识,即 PHP 不是这里的最佳解决方案。您确实需要寻找专门的实时技术来解决将数据从服务器传输到客户端的异步问题。听起来您正在尝试实现 HTTP-Long Polling,这不是一件容易解决跨浏览器的事情。Comet 产品的开发人员已多次解决此问题,因此我建议您查看 Comet 解决方案,或者甚至更好的 WebSocket 解决方案,该解决方案具有对旧浏览器的后备支持。

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

于 2011-09-20T09:54:21.677 回答
0

你需要一个实时库。

一个例子是棘轮http://socketo.me/

处理 pub sub 的部分在http://socketo.me/docs/wamp进行了讨论

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

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

于 2016-04-19T01:14:26.990 回答