我有一个旧网站正在运行,它还有一个聊天系统,它总是很好用。但最近我又重新启动了项目并开始改进,用户群也在不断增加。 (在VPS上运行)
现在我的这个shoutbox(在http://businessgame.be/shoutbox运行)最近遇到了问题,当时有超过30人在线,它开始真正放慢整个网站的速度。
这个shoutbox系统是几年前由老我(具有讽刺意味的是年轻的我)编写的,他对旧学校的普通旧JavaScript(POJS?)太过分了,并且拒绝使用像JQuery这样的框架。
我做的是如果有新消息,我每隔3秒用AJAX轮询一次,如果是,则加载所有这些消息(将这些消息作为XML文件传递,然后由JS代码解析为添加的HTML块关于shoutbox内容。
简化脚本是这样的:
AJAX功能
function createRequestObject() {
var xmlhttp;
if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
} else { // code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// Create the object
return xmlhttp;
}
function getXMLObject(XMLUrl, onComplete, onFail) {
var XMLhttp = createRequestObject();
// Check to see if the latest shout time has been initialized
if(typeof getXMLObject.counter == "undefined") {
getXMLObject.counter = 0;
}
getXMLObject.counter++;
XMLhttp.onreadystatechange = function() {
if(XMLhttp.readyState == 4) {
if(XMLhttp.status == 200) {
if(onComplete) {
onComplete(XMLhttp.responseXML);
}
} else {
if(onFail) {
onFail();
}
}
}
};
XMLhttp.open("GET", XMLUrl, true);
XMLhttp.send();
setTimeout(function() {
if(typeof XMLhttp != "undefined" && XMLhttp.readyState != 4) {
XMLhttp.abort();
if(onFail) {
onFail();
}
}
}, 5000);
}
聊天功能
function initShoutBox() {
// Check for new shouts every 2 seconds
shoutBoxInterval = setInterval("shoutBoxUpdate()", 3000);
}
function shoutBoxUpdate() {
// Get the XML document
getXMLObject("/ajax/shoutbox/shoutbox.xml?time=" + shoutBoxAppend.lastShoutTime, shoutBoxAppend);
}
function shoutBoxAppend(xmlData) {
process all the XML and add it to the content, also remember the timestamp of the newest shout
}
真实的脚本更复杂,当页面模糊时加载时间较慢,并且跟踪AJAX调用以避免同时发出双重调用,发布呼叫的能力,加载设置等等。这里所有不太相关
对于那些感兴趣的人,完整的代码在这里: http://businessgame.be/javascripts/xml.js http://businessgame.be/javascripts/shout.js
包含喊叫数据的XML文件示例 http://businessgame.be/ajax/shoutbox/shoutbox.xml?time=0
我每隔30秒获取一次在线用户列表并每2分钟检查一次新的私人消息。
我的主要问题是,既然这个老派JS正在减慢我的网站速度,那么将代码更改为JQuery会提高性能并解决这个问题吗?或者我应该选择像nodeJS,websockets或其他东西一样选择其他技术?或者我可能忽略了这段旧代码中的一个基本错误?
重写整个聊天和私人消息系统(使用相同的后端)需要付出很多努力,所以我想从一开始就这样做,而不是在JQuery中重写整个事情,只是为了弄清楚它没有解决手头的问题。
同时在聊天室中有30个人在线并不是一个例外,所以它应该是一个僵化的系统。
PS:后端是PHP MySQL
答案 0 :(得分:1)
我有偏见,因为我喜欢Ruby,我更喜欢使用Plain JS而不是JQuery和其他框架。
我相信您通过使用AJAX浪费了大量资源,并且应该转移到用于您的用例的websockets。
30个用户并不多...当使用websockets时,我认为单个服务器进程应该能够每秒服务数千个同步更新。
主要原因是websockets是持久的(每次请求都不会发生身份验证),并且广播到多个连接将使用与单个AJAX更新相同数量的数据库查询。
在您的情况下,不是每个人每次都阅读整个XML,POST事件只会广播最新(已发布)的喊(而不是整个XML),并将其存储在XML中以便持久存储(用于新访问者) )。
此外,您不需要所有的身份验证和请求最终都会被回复,而且,没有任何待处理的更新"。
从AJAX迁移到websockets时,最小化数据库请求(XML读取)应该证明是一个巨大的好处。
另一个好处是,足够的同时用户将使AJAX轮询与DoS攻击的行为相同。
现在,30个用户=每秒10个请求。这不是很多,但如果每个请求花费超过100毫秒,则可能会很重 - 这意味着服务器响应的请求少于收到的请求。
Plezi Ruby Websocket Framework的主页上有一个喊话框的简短示例(我是Plezi的作者,我有偏见):
# finish with `exit` if running within `irb`
require 'plezi'
class ChatServer
def index
render :client
end
def on_open
return close unless params[:id] # authentication demo
broadcast :print,
"#{params[:id]} joind the chat."
print "Welcome, #{params[:id]}!"
end
def on_close
broadcast :print,
"#{params[:id]} left the chat."
end
def on_message data
self.class.broadcast :print,
"#{params[:id]}: #{data}"
end
protected
def print data
write ::ERB::Util.html_escape(data)
end
end
path_to_client = File.expand_path( File.dirname(__FILE__) )
host templates: path_to_client
route '/', ChatServer
POJS客户端看起来如此(DOM update
和数据访问($('#text')[0].value
)使用JQuery):
ws = NaN
handle = ''
function onsubmit(e) {
e.preventDefault();
if($('#text')[0].value == '') {return false}
if(ws && ws.readyState == 1) {
ws.send($('#text')[0].value);
$('#text')[0].value = '';
} else {
handle = $('#text')[0].value
var url = (window.location.protocol.match(/https/) ? 'wss' : 'ws') +
'://' + window.document.location.host +
'/' + $('#text')[0].value
ws = new WebSocket(url)
ws.onopen = function(e) {
output("<b>Connected :-)</b>");
$('#text')[0].value = '';
$('#text')[0].placeholder = 'your message';
}
ws.onclose = function(e) {
output("<b>Disonnected :-/</b>")
$('#text')[0].value = '';
$('#text')[0].placeholder = 'nickname';
$('#text')[0].value = handle
}
ws.onmessage = function(e) {
output(e.data);
}
}
return false;
}
function output(data) {
$('#output').append("<li>" + data + "</li>")
$('#output').animate({ scrollTop:
$('#output')[0].scrollHeight }, "slow");
}
如果您想添加更多活动或数据,可以考虑使用Plezi's auto-dispatch feature,它还为您提供易于使用的带有AJAJ(AJAX + JSON)的轻量级 Javascript客户端回退。
...
但是,根据您的服务器架构以及您是否介意使用更重的框架,您可以使用更常见的socket.io(尽管它从AJAX开始并且仅在预热期后移动到websockets)。
修改强>
从XML更改为JSON仍需要解析。问题实际上是XML与JSON解析的速度。
JSON在客户端javascript上会更快,根据以下SO问答:Is parsing JSON faster than parsing XML
JSON在服务器端似乎也受到PHP的青睐(可能是基于意见而不是经过测试):PHP: is JSON or XML parser faster?
但是......我认为你的瓶颈不是JSON或XML。 我认为瓶颈与使用AJAX 时服务器访问(解析?)数据的次数有关。
EDIT2 (由于对PHP与node.js的评论)
你可以使用Ratchet添加一个PHP websocket层...尽管PHP并不是为长时间运行的进程设计的,所以我会考虑添加一个websocket专用堆栈(使用本地代理将websocket连接路由到另一个应用程序) )。
我喜欢Ruby,因为它可以让您快速轻松地编写解决方案代码。 Node.js也常用作专用的websocket堆栈。
我个人会避免使用socket.io,因为它抽象了连接方法(AJAX vs Websockets)并始终以AJAX开始,然后才开始热身#34;升级&#34;升级&#34; (websockets)...另外,socket.io在不使用websockets时使用长轮询,我觉得这很糟糕。我宁愿显示一条消息告诉客户升级他们的浏览器。
Jonny Whatshisface指出,使用node.js解决方案,他达到了约50K并发用户的限制(这可能与本地代理的连接限制有关)。使用C解决方案,他表示没有超过200K并发用户的问题。
这显然还取决于每秒更新的次数以及您是否正在广播数据或将其发送给特定客户......如果您为200K用户每秒发送2次更新,这是400K的更新。但是,每2秒仅更新一次所有用户,即每秒100K更新。因此,试图找出最大负载可能会令人头疼。
就个人而言,我没有在我的应用程序上达到这些数字,所以我从来没有亲自发现Plezi的限制......但是,在测试期间,我没有发送数百个问题每秒数千次更新(但由于本地计算机上的可用端口和打开文件句柄限制,我确实有连接限制。)
这肯定显示了使用websockets可以达到的巨大改进(特别是因为您声明注意到30个并发用户的速度减慢)。