我有一个旧的网站正在运行,它也有一个聊天系统,该系统通常可以正常工作。但是最近我又选择了这个项目并开始改进,用户群不断增加。 (在VPS上运行)
现在,我拥有的这个喊叫框(在http://businessgame.be/shoutbox运行)最近遇到了问题,当同时有30多个在线用户时,它开始确实使整个站点变慢。
这个shoutbox系统是几年前由老我(讽刺的是年轻的我)编写的,他对老派的Plain Old 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个人在线并不是真正的例外,因此它应该是一个固定的系统。
从XML数据文件更改为JSON是否还能提高性能?
PS:后端是PHP MySQL
最佳答案
我有偏见,因为我喜欢Ruby,而且我更喜欢使用Plain JS而不是JQuery和其他框架。
我相信您使用AJAX会浪费大量资源,因此应该将其转移到websockets上。
30个用户并不多...使用websockets时,我假设单个服务器进程应该能够每秒提供数千个同时更新。
这样做的主要原因是websocket是持久性的(每个请求都不会进行身份验证),并且广播到多个连接将使用与单个AJAX更新相同的数据库查询量。
在您的情况下,并不是每个人都每次都阅读整个XML,而是一个POST事件只会广播最新的(发布的)喊叫声(而不是整个XML),并将其存储在XML中以进行持久存储(供新访问者使用)。
另外,您不需要所有身份验证和最终以“否,没有任何待处理的更新”回答的请求。
从AJAX迁移到Websocket时,最小化数据库请求(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,它还为您提供了一个易于使用的轻量级Javascript客户端,具有AJAJ(AJAX + JSON)后备功能。
...
但是,根据服务器的体系结构以及是否愿意使用较重的框架,可以使用更常见的socket.io(尽管它以AJAX开头,并且在预热期后才移至websockets)。
编辑
从XML更改为JSON仍需要解析。问题实际上是XML与JSON的解析速度。
根据以下SO问答,JSON在客户端javascript上将更快。
JSON在PHP的服务器端似乎也很受青睐(可能是基于观点的,而不是经过测试的):Is parsing JSON faster than parsing XML
但是...我真的认为您的瓶颈不是JSON或XML。我相信瓶颈与使用AJAX时服务器访问,分析(和解析)数据以及查看数据的次数有关。
EDIT2(由于对PHP与node.js进行评论)
您可以使用Ratchet添加PHP websocket层...尽管PHP不是为长时间运行的进程而设计的,所以我会考虑添加websocket专用堆栈(使用本地代理将websocket连接路由到其他应用程序)。
我喜欢Ruby,因为它使您可以快速轻松地编写解决方案。 Node.js也通常用作专用的websocket堆栈。
我个人会避免使用socket.io,因为它抽象了连接方法(AJAX与Websockets),并且始终以AJAX开头,然后才“预热”到“升级”(websockets)...此外,socket.io在以下情况下使用长轮询没有使用websockets,我这很糟糕。我宁愿显示一条消息,告诉客户端升级他们的浏览器。
Jonny Whatshisface指出,使用node.js解决方案,他达到了约5万个并发用户的限制(这可能与本地代理的连接限制有关)。他表示,使用C解决方案时,并发用户数不超过200K不会有问题。
显然,这还取决于每秒的更新次数,以及是否要广播数据还是将其发送到特定的客户端...如果您要为200K用户每用户每秒发送2个更新,则为40万个更新。但是,每2秒仅更新一次所有用户,即每秒10万次更新。因此,试图找出最大负载可能会令人头疼。
就我个人而言,我无法在应用程序上达到这些数字,因此我从未亲手发现Plezi的极限……尽管在测试过程中,我每秒发送数十万个更新没有问题(但我确实做到了)由于可用端口和我的本地计算机上的打开文件句柄限制而受到连接限制)。
这绝对表明使用websocket可以带来多大的改进(尤其是因为您声明要注意30个并发用户的速度下降)。
关于javascript - 聊天/聊天盒系统的最佳技术是什么?普通JS,JQuery,Websocket或其他?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34175453/