SignalR javascript客户端计时器锤击服务器进行连接

时间:2012-12-14 10:04:35

标签: javascript signalr

我有一个SignalR的实现,它被用作聊天客户端。问题似乎是我有一个客户端计时器,即ping服务器以通知所有其他客户端用户的状态。

在这种情况下,我有一个心跳和最新的活动。心跳每十五秒左右就会到达服务器,而最新活动会跟踪用户的交互性。将这两个值传递给服务器,以便可以通知所有其他客户端其他人的状态。即:如果用户A没有移动他们的鼠标超过一分钟,当他们的心跳到达服务器时,它将通过SignalR向其他所有用户广播他们现在“离开”..

无论如何,我有一个问题,即SignalR连接似乎呈指数级增长,连接到聊天应用程序的用户数量就越大。

这是客户端js计时器的来源,我怀疑它很可疑,但我不确定为什么:

define(['jquery', 'underscore', 'backbone'],
function ($, _, Backbone) {

    var Timer = Backbone.Model.extend({
        defaults: {
            interval: 1 * 10 * 1000,
            timeout: null
        },

        initialize: function (options) {
            _.bindAll(this, 'start', 'tick', 'stop', 'tickNow');
            if (options.interval) {
                this.set('interval', options.interval);
            }
        },

        start: function () {
            var timer = setTimeout(this.tick, this.get('interval'));
            this.set('timeout', timer);
        },

        tick: function () {
            var self = this;
            self.trigger('timerexpired', this);
            self.start();
        },

        tickNow: function () {
            var self = this;
            self.stop();
            self.trigger('timerexpired', this);
            self.start();
        },

        stop: function () {
            clearTimeout(this.get('timeout'));
        }
    });

    return Timer;
});

3 个答案:

答案 0 :(得分:2)

我注意到您在启动计时器方法中使用了this关键字。可能是您对this.tick调用的调用在实际触发时脱离了上下文。尝试:

start: function () {
    var self = this;
    var timer = setTimeout(this.tick, self.get('interval'));
    this.set('timeout', timer);
},

在浏览器中,将一个断点放入tick方法并检查您确实引用了正确的计时器。

答案 1 :(得分:1)

我没有看到进展如何必然是指数级的,但线性进展肯定会倾向于它。基本上,你可以在这里做两件事:

  • 在客户端使用长轮询/限制,但您已经每15秒发送一次请求,因此不会违反浏览器限制。
  • 在服务器端,如果你想要一个大规模的ajax应用程序,你肯定需要一个负载均衡器。在云端进行亚马逊自动扩展,这可能会对您有所帮助,只需使用负载均衡器。这不会便宜,而且与您体验的活动成正比。

答案 2 :(得分:0)

您不应该只将this.tick作为第一个参数传递给setTimeout,因为tick方法执行时this将绑定到全局window对象而不是Timer实例。

您还应该确保在start实例已经运行时不要调用Timer。我会改变以下方法:

    start: function () {
        var self = this;
        if (this.get('timeout') !== null) {
            throw "start must not be called when the Timer is already running";
        }
        // creating a closure ensures this is bound correctly in tick()
        var timer = setTimeout(function () {
            self.tick(); 
        }, this.get('interval'));
        this.set('timeout', timer);
    },

    tick: function () {
        this.set('timeout', null);
        this.trigger('timerexpired', this);
        this.start();
    },

    stop: function () {
        clearTimeout(this.get('timeout'));
        this.set('timeout', null);
    }

检查Timer是否正常工作的明显方法是将对console.log的调用绑定到timerexpired事件,以确保事件的触发时间不超过预期。

如果您需要帮助确定并发连接数量失控的原因,您应该将代码发布到停止和启动SignalR连接的位置。