简体   繁体   English

聊天/聊天盒系统的最佳技术是什么? 普通JS,JQuery,Websocket或其他?

[英]What is the best technology for a chat/shoutbox system? Plain JS, JQuery, Websockets, or other?

I have an old site running, which also has a chat system, which always used to work fine. 我有一个旧的网站正在运行,它也有一个聊天系统,该系统通常工作正常。 But recently I picked up the project again and started improving and the user base has been increasing a lot. 但是最近,我再次选择了该项目并开始进行改进,用户群不断增加。 (running on a VPS) (在VPS上运行)

Now this shoutbox I have (running at http://businessgame.be/shoutbox ) has been getting issues lately, when there are over 30 people online at the same time, it starts to really slow down the entire site. 现在,我拥有的这个shoutbox(在http://businessgame.be/shoutbox上运行)最近遇到了问题,当同时有30多个在线用户时,它开始确实使整个站点变慢。

This shoutbox system was written years ago by the old me (which ironically was the young me) who was way too much into old school Plain Old JavaScript (POJS?) and refused to use frameworks like JQuery. 这个shoutbox系统是几年前由老我(讽刺的是年轻的我)编写的,他对老派的Plain Old JavaScript(POJS?)太多了,并且拒绝使用JQuery之类的框架。

What I do is I poll every 3 seconds with AJAX if there are new messages, and if YES, load all those messages (which are handed as an XML file which is then parsed by the JS code into HTML blocks which are added to the shoutbox content. 我要做的是,如果有新消息,我每3秒用AJAX轮询一次,如果是,则加载所有这些消息(将它们作为XML文件处理,然后由JS代码解析为HTML块,添加到shoutbox中内容。

Simplified the script is like this: 简化的脚本是这样的:

The AJAX functions 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);
}

Chat functions 聊天功能

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
}

The real script is far more convoluted, with slower loading times when the page is blurred and keeping track of AJAX calls to avoid double calls at the same time, ability to post a shout, load settings etc. All not very relevant here. 真实的脚本更加复杂,页面模糊时加载速度更慢,并且跟踪AJAX调用可避免同时进行两次调用,发出喊叫声,加载设置等。所有这些在这里都不重要。

For those interested, full codes here: http://businessgame.be/javascripts/xml.js http://businessgame.be/javascripts/shout.js 对于那些感兴趣的人,请在此处查看完整代码: http : //businessgame.be/javascripts/xml.js http://businessgame.be/javascripts/shout.js

Example of the XML file containing the shout data http://businessgame.be/ajax/shoutbox/shoutbox.xml?time=0 包含喊话数据的XML文件示例http://businessgame.be/ajax/shoutbox/shoutbox.xml?time=0

I do the same for getting a list of the online users every 30 seconds and checking for new private messages every 2 minutes. 我这样做是为了每30秒获取一次在线用户列表,并每2分钟检查一次新的私人消息。

My main question is, since this old school JS is slowing down my site, will changing the code to JQuery increase the performance and fix this issue? 我的主要问题是,由于这个老式的JS减慢了我的网站的速度,将代码更改为JQuery会提高性能并解决此问题吗? Or should I choose to go for an other technology alltogether like nodeJS, websockets or something else? 还是我应该选择一起使用其他技术,例如nodeJS,websockets或其他? Or maybe I am overlooking a fundamental bug in this old code? 还是我忽略了这个旧代码中的一个基本错误?

Rewriting an entire chat and private messages system (which use the same backend) requires a lot of effort so I'd like to do this right from the start, not rewriting the whole thing in JQuery, just to figure out it doesn't solve the issue at hand. 重写整个聊天和私人消息系统(使用相同的后端)需要大量的工作,因此我想从一开始就正确地进行此操作,而不是在JQuery中重写整个过程,只是为了弄清楚它不能解决问题手头的问题。

Having 30 people online in the chatbox at the same time is not really an exception anymore so it should be a rigid system. 聊天框中同时有30个人在线并不是一个例外,因此它应该是一个固定的系统。

Could perhaps changing from XML data files to JSON increase performance as well? 从XML数据文件更改为JSON是否还能提高性能?

PS: Backend is PHP MySQL PS:后端是PHP MySQL

I'm biased, as I love Ruby and I prefer using Plain JS over JQuery and other frameworks. 我有偏见,因为我喜欢Ruby,而且我更喜欢使用Plain JS而不是JQuery和其他框架。

I believe you're wasting a lot of resources by using AJAX and should move to websockets for your use-case. 我相信您使用AJAX会浪费大量资源,因此应该转到用Websocket的用例中。

30 users is not much... When using websockets, I would assume a single server process should be able to serve thousands of simultaneous updates per second. 30个用户并不多...使用websockets时,我假设单个服务器进程应该能够每秒提供数千个同时更新。

The main reason for this is that websockets are persistent (no authentication happening with every request) and broadcasting to a multitude of connections will use the same amount of database queries as a single AJAX update. 这样做的主要原因是websocket是持久性的(每个请求都不会进行身份验证),并且广播到多个连接将使用与单个AJAX更新相同的数据库查询量。

In your case, instead of everyone reading the whole XML every time, a POST event will only broadcast the latest (posted) shout (not the whole XML), and store it in the XML for persistent storage (used for new visitors). 在您的情况下,不是每个人都每次都阅读整个XML,而是一个POST事件只会广播最新的(发布的)喊叫声(而不是整个XML),并将其存储在XML中以进行持久存储(供新访客使用)。

Also, you don't need all the authentication and requests that end up being answered with a "No, there aren't any pending updates". 另外,您不需要所有身份验证和最终以“否,没有任何待处理的更新”回答的请求。

Minimizing the database requests (XML reads) should prove to be a huge benefit when moving from AJAX to websockets. 从AJAX迁移到Websocket时,最小化数据库请求(XML读取)应该被证明是巨大的好处。

Another benifit relates to the fact that enough simultaneous users will make AJAX polling behave the same as a DoS attack. 另一个好处是,有足够多的同时用户使AJAX轮询的行为与DoS攻击相同。

Right now, 30 users == 10 requests per second. 现在,每秒30个用户== 10个请求。 This isn't much, but it can be heavy if each request would take more than 100ms - meaning, that the server answers less requests than it receives. 这虽然不多,但是如果每个请求要花费100毫秒以上的时间,则可能会很沉重-这意味着服务器响应的请求少于接收的请求。

The home page for the Plezi Ruby Websocket Framework has this short example for a shout box (I'm Plezi's author, I'm biased): 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 

The POJS client looks like so (the DOM update and from data access ( $('#text')[0].value ) use JQuery): 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");
    }

If you want to add more events or data, you can consider using Plezi's auto-dispatch feature , that also provides you with an easy to use lightweight Javascript client with an AJAJ (AJAX + JSON) fallback. 如果要添加更多事件或数据,可以考虑使用Plezi的自动分派功能 ,该功能还为您提供了易于使用的轻量级 Javascript客户端,具有AJAJ(AJAX + JSON)后备功能。

... ...

But, depending on your server's architecture and whether you mind using heavier frameworks or not, you can use the more common socket.io (although it starts with AJAX and only moves to websockets after a warmup period). 但是,根据服务器的体系结构以及是否介意使用更重的框架,可以使用更常见的socket.io(尽管它以AJAX开头,并且在预热期后才移至websockets)。

EDIT 编辑

Changing from XML to JSON will still require parsing. 从XML更改为JSON仍需要解析。 The question is actually whether XML vs. JSON parsing speeds. 问题实际上是XML与JSON的解析速度。

JSON will be faster on the client javascript , according to the following SO question and answer: Is parsing JSON faster than parsing XML 根据以下SO问答, JSON在客户端javascript上将更快解析JSON比解析XML更快

JSON seems to be also favored on the server-side for PHP (might be opinion based rather than tested): PHP: is JSON or XML parser faster? JSON在PHP的服务器端似乎也受到青睐(可能是基于意见的,而不是经过测试的): PHP:JSON或XML解析器的速度更快吗?

BUT... I really think your bottleneck isn't the JSON or the XML. 但是...我真的认为您的瓶颈不是JSON或XML。 I believe the bottleneck relates to the multitude of times that the data is accessed, (parsed?) and reviewed by the server when using AJAX . 我相信瓶颈与使用AJAX时服务器访问,分析(和解析)数据以及查看数据的次数有关

EDIT2 (due to comment about PHP vs. node.js) EDIT2 (由于对PHP与node.js进行评论)

You can add a PHP websocket layer using Ratchet... Although PHP wasn't designed for long running processes, so I would consider adding a websocket dedicated stack (using a local proxy to route websocket connections to a different application). 您可以使用Ratchet添加PHP websocket层...尽管PHP不是为长时间运行的进程而设计的,所以我会考虑添加websocket专用堆栈(使用本地代理将websocket连接路由到其他应用程序)。

I love Ruby since it allows you to quickly and easily code a solution. 我喜欢Ruby,因为它使您可以快速轻松地编写解决方案。 Node.js is also commonly used as a dedicated websocket stack. Node.js也通常用作专用的websocket堆栈。

I would personally avoid socket.io, because it abstracts the connection method (AJAX vs Websockets) and always starts as AJAX before "warming up" to an "upgrade" (websockets)... Also, socket.io uses long-polling when not using websockets, which I this is terrible. 我个人会避免使用socket.io,因为它抽象了连接方法(AJAX与Websockets),并且始终以AJAX开头,然后才“预热”到“升级”(websockets)...此外,socket.io在以下情况下使用长轮询没有使用websockets,我这很糟糕。 I'd rather show a message telling the client to upgrade their browser. 我宁愿显示一条消息,告诉客户端升级他们的浏览器。

Jonny Whatshisface pointed out that using a node.js solution he reached a limit of ~50K concurrent users (which could be related to the local proxy's connection limit). Jonny Whatshisface指出,使用node.js解决方案,他达到了约5万个并发用户的限制(这可能与本地代理的连接限制有关)。 Using a C solution, he states to have no issues with more than 200K concurrent users. 他说,使用C解决方案时,并发用户数不超过200K不会有问题。

This obviously depends also on the number of updates per second and on whether you're broadcasting the data or sending it to specific clients... If you're sending 2 updates per user per second for 200K users, that's 400K updates. 显然,这还取决于每秒的更新次数,以及是否要广播数据还是将其发送到特定的客户端...如果您要为200K用户每用户每秒发送2个更新,则为40万个更新。 However, updating all the users only once every 2 seconds, that's 100K updates per second. 但是,每2秒仅更新一次所有用户,即每秒10万次更新。 So trying to figure out the maximum load can be a headache. 因此,试图找出最大负载可能会令人头疼。

Personally, I didn't get to reach these numbers on my apps, so I never got to discover Plezi's limits first hand... although, during testing, I had no issues with sending hundred of thousands of updates per second (but I did had a connection limit due to available ports and open file handle limits on my local machine). 就个人而言,我无法在应用程序上达到这些数字,因此我从未亲手发现Plezi的极限……尽管在测试期间,我每秒发送数十万个更新没有问题(但我确实做到了)由于可用端口和我的本地计算机上的打开文件句柄限制而受到连接限制)。

This definitely shows how vast of an improvement you can reach by utilizing websockets (especially since you stated to notice slowdowns with 30 concurrent users). 这绝对表明使用websocket可以带来多大的改进(尤其是因为您声明要注意30个并发用户的速度下降)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM