1

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

在这种情况下,我有一个心跳和一个最新的活动。心跳每隔 15 秒左右就会触发一次服务器,最新的活动会跟踪用户的交互性。这两个值被传递给服务器,以便所有其他客户端都可以收到其他人的状态通知。即:如果用户 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;
});
4

3 回答 3

2

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

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

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

于 2012-12-17T03:32:41.070 回答
1

我看不出进展如何必然是指数级的,但线性进展肯定会趋于它。基本上,您可以在这里做两件主要的事情:

  • 在客户端使用长轮询/限制,但您已经每 15 秒发送一次请求,因此这不会违反浏览器限制。
  • 在服务器端,如果你想要一个大规模的 ajax 应用程序,你肯定需要一个负载均衡器。在云中选择 Amazon 自动扩展,这可能会对您有所帮助,并且只需使用负载均衡器即可。这并不便宜,而且与您体验的活动成正比。
于 2012-12-14T10:08:36.370 回答
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 连接的位置发布代码。

于 2012-12-14T19:19:41.847 回答