我可以找到很多关于长轮询如何工作的信息(例如,这个和这个),但没有简单的例子说明如何在代码中实现这一点。

我能找到的只有cometd,它依赖DojoJS框架和一个相当复杂的服务器系统。。

基本上,我将如何使用Apache来服务请求,以及我将如何编写一个简单的脚本(例如,用PHP)来“长轮询”服务器以获取新消息?

该示例不一定是可扩展的、安全的或完整的,它只需要工作即可!


当前回答

最简单的NodeJS

const http = require('http');

const server = http.createServer((req, res) => {
  SomeVeryLongAction(res);
});

server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

server.listen(8000);

// the long running task - simplified to setTimeout here
// but can be async, wait from websocket service - whatever really
function SomeVeryLongAction(response) {
  setTimeout(response.end, 10000);
}

在Express for exmaple的生产方面的场景中,您将在中间件中得到响应。做您需要做的事情,可以将所有长轮询方法的范围扩展到Map或其他流(对其他流可见),并在准备就绪时调用<Response>Response.end()。长轮询连接没有什么特别之处。休息只是您通常构建应用程序的方式。

如果你不知道我所说的范围界定是什么意思,这会让你明白

const http = require('http');
var responsesArray = [];

const server = http.createServer((req, res) => {
  // not dealing with connection
  // put it on stack (array in this case)
  responsesArray.push(res);
  // end this is where normal api flow ends
});

server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

// and eventually when we are ready to resolve
// that if is there just to ensure you actually 
// called endpoint before the timeout kicks in
function SomeVeryLongAction() {
  if ( responsesArray.length ) {
    let localResponse = responsesArray.shift();
    localResponse.end();
  }
}

// simulate some action out of endpoint flow
setTimeout(SomeVeryLongAction, 10000);
server.listen(8000);

正如你所看到的,你真的可以对所有的联系做出反应,一是做任何你想做的事。每个请求都有id,因此您应该能够使用map和访问特定的api外调用。

其他回答

这是PHP非常糟糕的选择之一。如前所述,您可以很快地将所有Apache工作人员绑在一起,这样做。PHP是为启动、执行和停止而构建的。它不是为开始而建造的,等等。。。执行,停止。你会很快让你的服务器陷入困境,并发现你有难以置信的扩展问题。

也就是说,您仍然可以使用PHP执行此操作,并且不会使用nginxHttpPushStreamModule杀死您的服务器:http://wiki.nginx.org/HttpPushStreamModule

您在Apache(或任何其他)前面设置nginx,它将负责保持并发连接的开放。您只需通过将数据发送到一个内部地址来响应有效负载,该地址可以通过后台作业来完成,也可以在收到新请求时将消息发送给正在等待的人。这可以防止PHP进程在长时间轮询期间处于打开状态。

这不是PHP独有的,可以使用nginx和任何后端语言来完成。并发开放连接的负载等于Node.js,所以最大的好处是它可以让你从NEEDING Node中解脱出来。

你会看到很多人提到其他语言库来完成长时间的投票,这是有充分理由的。PHP天生就不适合这种类型的行为。

Tornado是为长轮询而设计的,它在/examples/chatdemo中包含了一个非常小的(数百行Python)聊天应用程序,包括服务器代码和JS客户端代码。它的工作原理如下:

客户机使用JS请求更新,因为(最后一条消息的数量),服务器URLHandler接收这些消息并添加回调以响应客户机到队列。当服务器收到新消息时,onmessage事件将触发,循环通过回调,并发送消息。客户端JS接收消息,将其添加到页面,然后请求更新此新消息ID。

这里是Erik Dubelboer使用Content类型:multipart/x-mixed-replace-header在PHP中的一个简单的长轮询示例:

<?

header('Content-type: multipart/x-mixed-replace; boundary=endofsection');

// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain

After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.


sleep(5);


echo 'Content-type: image/jpg

';

$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);

echo '
--endofsection
';

下面是一个演示:

http://dubbelboer.com/multipart.php

你可以试试icomet(https://github.com/ideawu/icomet),一个用libevent构建的C1000K C++comet服务器。icomet还提供了一个JavaScript库,使用起来非常简单

var comet = new iComet({
    sign_url: 'http://' + app_host + '/sign?obj=' + obj,
    sub_url: 'http://' + icomet_host + '/sub',
    callback: function(msg){
        // on server push
        alert(msg.content);
    }
});

icomet支持多种浏览器和操作系统,包括Safari(iOS、Mac)、IE(Windows)、Firefox、Chrome等。

对于ASP.NET MVC实现,请查看NuGet上提供的SignalR。。请注意,NuGet与Git源代码相比常常是过时的,因为Git源经常提交。

在Scott Hanselman的博客上阅读有关SignalR的更多信息