对于那些已经在思考这个问题的人来说,这可能是一个愚蠢的问题,也许我只是需要更多的咖啡。
问题:是否使用websockets或ajax,似乎仍然有一些轮询发生。这是正确的吗?
示例 (不是真正的项目):我想关注一个文本文件。除非我错过了一些东西(更多的咖啡?),我是不是仍然必须要么a)询问服务器是否有更新,或b)告诉页面我有更新;通过在一段时间内休眠PHP代码或在客户端使用setTimeout循环。
我确实理解的事情:我肯定已经看到了在服务器和页面之间来回通信的好处。我看到我没有发送http请求。所以我看到了好处。
细节:我一直只是使用xmlhttprequest所以我决定看看这整个websockets我认为我理解,是实时数据发送给客户端,但是,像上面提到的,除非我失踪或一些逻辑的东西,似乎我还告诉php或javascript检查间隔的数据,否则数据被发送在一个无限循环(想象调用mysql)。
也许我的代码逻辑是各种各样的坏。欢迎大家观看。从我发现的所有示例中,每个人似乎都只是在PHP中运行一个无限循环
PHP(减去所有连接术语)
while(true) {
// update once a second
$this->send($client, file_get_contents('/my/file/test.txt'));
sleep(1);
}
Javascript var websocket = new WebSocket( "ws://mysite.com:12345" );
websocket.onmessage = function( str ) {
console.log( str.data );
};
我只是没有掌握这个逻辑,我如何能使它实时没有某种轮询。也许这就是它应该如何工作的。
我明白,如果我从php代码中删除sleep,事情会变得更加实时,太多了,但这似乎会在上面的例子中无限轮询文件,这似乎是不对的。
编辑:澄清一下,我不是专门寻找一个特定的解决方案来观看文本文件。如果你浏览一下这个问题,你可能会想到这个。
Edit:未来的访问者,这个问题的答案是:当用户发送更改时,不是专门监视更改,而是将更改发送给打开的连接。
Websockets允许您完全避免轮询,只要您控制所有事件(或Sub/Pub到外部事件)。
对于你的例子,如果你控制写入文件的动作,那么你可以调用websocket "broadcast"或"publish"这个事件。
通过这种方式,您可以完全避免轮询。
由于我讨厌使用PHP(无意冒犯,我已经受够了),这里有一个使用Plezi实时框架的快速Ruby示例。
在这个例子中,我们使用一个简单的touch
方法来执行一个操作。虽然我没有真正接触到文件,但您可以体验到API的使用允许我控制事件并向其他用户广播- 不涉及轮询。
订阅外部事件也是如此。
要运行此示例,使用[sudo] gem install plezi
安装plezi
gem(取决于您是否需要sudo
和您的系统),并在终端使用irb
命令打开IRB终端。然后粘贴以下代码:
需要"plezi"
class RootController
def index
%{<html><head>
<script>
var websocket = NaN;
function connect() { websocket = new WebSocket( (window.location.protocol.match(/https/) ? 'wws' : 'ws') + '://' + window.location.hostname + (window.location.port == '' ? '' : (':' + window.location.port) ) + "/" ); }
function init()
{
connect()
websocket.onopen = function(evt) { WriteMessage("(Connected and waiting for messages)", "connection") };
websocket.onclose = function(evt) { WriteMessage("(Disconnected. messages will be lost)", "connection");connect(); };
websocket.onmessage = function(evt) {
WriteMessage(evt.data, "");
};
websocket.onerror = function(evt) { WriteMessage(evt.data, 'error'); };
}
function WriteMessage( message, message_type )
{
if (!message_type) message_type = 'received'
var msg = document.createElement("p");
msg.className = message_type;
msg.innerHTML = message;
document.getElementById("output").appendChild(msg);
}
function Send(message)
{
WriteMessage(message, 'sent');
websocket.send(message);
}
window.addEventListener("load", init, false);
</script></head>
<body>
<p>Messages should show up here:</p>
<div id=output></div>
</body>
</html>
}
end
def touch
FileController.touch
"You Touched the file, a message should be sent to the web browser windows."
end
def on_open
subscribe :file_notifications
end
def on_message data
end
def self.push_update_event
publish :file_notifications, "The file was updated."
"Touched - Ok".freeze
end
end
class FileController
def self.touch
puts "INFO: A file should be touched.. you can do whatever you feel like here..."
RootController.push_update_event
end
end
class APIController
def touched
RootController.push_update_event
end
end
Plezi.route '/', RootController
Plezi.route '/api', APIController
exit # the server will start once you exit the irb terminal
现在在两个不同的浏览器窗口中访问:
- http://localhost: 3000/
- http://localhost: 3000/碰
或者,您甚至可以使用外部脚本"编辑文件"(虚拟地),然后访问http://localhost:3000/api/touched,通知所有用户该操作(这里没有显示身份验证,但应该添加)。
我最初会建议类似的事情。
$sFile = "/my/file/test.txt";
$timeMod = filemtime($sFile);
while(true) {
if (filemtime("SomeFileHere.txt")!==$timeMod ) {
$timeMod = filemtime($sFile);
// File has changed, update variable with new timestamp
$this->send($client, file_get_contents($sFile));
} else {
// No change, do nothing here.
}
sleep(1);
}
基本上在你循环之前,你得到最后修改的日期。如果在循环中它发生了变化,我们更新变量并在那个时候发送一个警报。
如果尝试在现实世界中做类似的工作而没有任何加载问题,我会有一个PHP文件在循环中运行(可能是daameon)。它将像上面那样以一个简单的循环每秒监视文件。如果有变化,我会让它通知另一个PHP脚本/线程发送的内容到所有的客户端。
如果同一个文件被发送到所有的客户端,这不会太糟糕。因为你可以使用send to all函数。但如果每个客户端都不一样(比如他们自己的历史/日志)…它将需要每个客户端处理,这将对性能造成一点影响。
为了进一步证明这样的东西,你可以记录下客户端最后一次接收文件内容的时间,并限制它可以再次发送的时间。所以客户端只在10秒后得到一个新的副本,少于10秒,它就忽略了。
到什么范围/规模&这是什么类型的项目?
目前我的python服务器可以接收到来自20,000个客户端的请求,并处理他们的数据并在一秒钟内响应。(GPS处理)。虽然我不会每次发送超过1000字节的内容,但python中的线程和队列功能将使其成为一种更好的方法,并且可以在单个实例中处理这么多客户端。只使用大约130mb的ram,并且随着时间的推移没有内存泄漏
PHP我个人认为不应该永远在循环中运行,它只是感觉不应该如何使用。
这里需要基于事件的编程。使用为您处理事件的库要容易得多。PHP并不是这类编程的最佳工具,但是仍然有一个库。
一个解决方案是使用nodejs/socket。IO服务器,PHP进程可以在发生有趣的事情时向它发送消息。然后nodejs服务器将其传递给客户端。