这是我的问题:我有一个脚本(让我们称之为comet.php),这是由AJAX客户端脚本提供的,并等待更改发生如下:
while(no_changes){
usleep(100000);
//check for changes
}
我不太喜欢这个,它不是很可扩展,它是(不好的)“糟糕的做法” 我想用信号量(?)或无论如何并发编程来改善这种行为 技术。你能告诉我一些如何处理这个问题的技巧吗? (我知道,这不是一个简短的答案,但起点就足够了。)
修改:LibEvent怎么办?
答案 0 :(得分:12)
您可以使用ZeroMQ解决此问题。
ZeroMQ是一个库,它提供了增压套接字,用于将东西(线程,进程甚至单独的机器)连接在一起。
我假设您正在尝试将数据从服务器推送到客户端。好吧,一个好方法是使用EventSource API(polyfills 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设置代码,那么你可以在那里添加发布部分。