SignalR OnDisconnected - 一个可靠的方法来处理"用户在线"对于聊天室? [英] SignalR OnDisconnected - a reliable way to handle "User is Online" for chatroom?

查看:3997
本文介绍了SignalR OnDisconnected - 一个可靠的方法来处理"用户在线"对于聊天室?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现一个聊天室。到目前为止,一切都很好 - 用户可以通过客户端JS从他们的浏览器发送的消息,我可以使用C#客户端做同样的事情 - 这些消息得到广播给其他用户。现在,我正在努力实现在线用户

I'm implementing a chat room. So far, so good - users can send messages from their browsers via a JS client, and I can use a C# client to do the same thing - these messages get broadcast to other users. Now, I'm trying to implement "online users".

我的做法是这样的:


  • OnConnected - 更新数据库的用户是IsOnline =真

  • OnDisconnected - 如果用户没有任何其他连接,更新用户在DB是 IsOnline = FALSE

  • 我存储在数据库中的状态,因为我不得不反正查询用户缩略图分贝 - 这似乎是一个简单的替代与集线器的工作字典。

  • OnConnected - update the User in the db to be IsOnline = true
  • OnDisconnected - if the User doesn't have any other connections, update the user in the db to be IsOnline = false
  • I'm storing state in the DB because I have to query the db for user thumbnails anyways - this seemed like a simple alternative to working with the Dictionaries in the hub.

我现在遇到的问题是, OnDisconnected 并不总是得到要求每一个客户ID - 陈旧的连接防止如果用户没有任何其他连接比特从解决为true,因此用户永远是在线。

The problem I'm encountering is that OnDisconnected doesn't always get called for every client Id - the stale connections are preventing the "if the user doesn't have any other connections" bit from resolving to true, so the user is always "online".

一个哈克解决方案,我能想到的是的总是的设置用户在 OnDisconnect - 但是这意味着,如果用户打开两个标签和关闭一个,他们将是离线。然后我可以重新设置用户在线每一个被发送消息,但是这似乎是处理周期的总浪费,仍有时间一大块,用户被视为下线,当他们真的在线。

One hacky solution I can think of is to always set the user to offline in the db upon OnDisconnect - but this means that if the user opens two tabs and closes one, they will be "offline". I could then re-set the user to online for every message that gets sent, but this seems like a total waste of processing cycles and still leaves a chunk of time where the user is seen as offline, when they are really online.

我相信,如果有保证OnDisconnected被调用为每个客户端的方式,这个问题就会消失。它的看起来的一样,如果我离开的客户开了很长一段时间(> 10分钟),然后断开, OnDisconnected 不会被调用。我会尽我所能查明再现步骤,并保持此更新

I believe that if there was a way to guarantee that OnDisconnected gets called for every client, this problem would go away. It seems like if I leave clients open for a long time (> 10 minutes) and then disconnect, OnDisconnected never gets called. I'll try my best to pinpoint the repro steps and keep this updated.

所以 - 这是一个有效的方法来处理在线状态?如果是这样,还有什么可以做,以确保 OnDisconnected 被烧成为每个连接,最终?

So - Is this a valid approach to handling online status? If so, what else can be done to ensure that OnDisconnected is firing for every connection, eventually?

这问题让我担心,因为现有的连接将只是继续随时间增长,如果我没有记错的话,最终因未处理状态的连接四溢。

This problem worries me because existing Connections will just continue to grow over time, if I'm not mistaken, eventually overflowing due to unhandled state connections.

代码:

我使用的在内存的方法来分组

发送消息(C#):

private readonly static ConnectionMapping<string> _chatConnections =
            new ConnectionMapping<string>();
public void SendChatMessage(string key, ChatMessageViewModel message) {
            message.HtmlContent = _compiler.Transform(message.HtmlContent);
            foreach (var connectionId in _chatConnections.GetConnections(key)) {
                Clients.Client(connectionId).addChatMessage(JsonConvert.SerializeObject(message).SanitizeData());
            }
        }



国家管理:

    public override Task OnConnected() {
        HandleConnection();
        return base.OnConnected();
    }

    public override Task OnDisconnected() {
        HandleConnection(true);
        return base.OnDisconnected();
    }

    public override Task OnReconnected() {
        HandleConnection();
        return base.OnReconnected();
    }

    private void HandleConnection(bool shouldDisconnect = false) {
        if (Context.User == null) return;
        var username = Context.User.Identity.Name;
        var _userService = new UserService();
        var key = username;

        if (shouldDisconnect) {
                _chatConnections.Remove(key, Context.ConnectionId);
                var existingConnections = _chatConnections.GetConnections(key);
                // this is the problem - existingConnections occasionally gets to a point where there's always a connection - as if the OnDisconnected() never got called for that client
                if (!existingConnections.Any()) { // THIS is the issue - existingConnections sometimes contains connections despite there being no open tabs/clients
                    // save status serverside
                    var onlineUserDto = _userService.SetChatStatus(username, false);
                    SendOnlineUserUpdate(_baseUrl, onlineUserDto, false);
                }
        } else {
                if (!_chatConnections.GetConnections(key).Contains(Context.ConnectionId)) {
                    _chatConnections.Add(key, Context.ConnectionId);
                }
                var onlineUserDto = _userService.SetChatStatus(Context.User.Identity.Name, true);
                SendOnlineUserUpdate(_baseUrl, onlineUserDto, true);
                // broadcast to clients
        }
    }



ConnectionMapping:

public class ConnectionMapping<T> {
        private readonly Dictionary<T, HashSet<string>> _connections =
            new Dictionary<T, HashSet<string>>();

        public int Count {
            get {
                return _connections.Count;
            }
        }

        public void Add(T key, string connectionId) {
            lock (_connections) {
                HashSet<string> connections;
                if (!_connections.TryGetValue(key, out connections)) {
                    connections = new HashSet<string>();
                    _connections.Add(key, connections);
                }

                lock (connections) {
                    connections.Add(connectionId);
                }
            }
        }

        public IEnumerable<string> GetConnections(T key) {
            HashSet<string> connections;
            if (_connections.TryGetValue(key, out connections)) {
                return connections.ToList();
            }
            return Enumerable.Empty<string>();
        }

        public void Remove(T key, string connectionId) {
            lock (_connections) {
                HashSet<string> connections;
                if (!_connections.TryGetValue(key, out connections)) {
                    return;
                }

                lock (connections) {
                    connections.Remove(connectionId);

                    if (connections.Count == 0) {
                        _connections.Remove(key);
                    }
                }
            }
        }
    }

更新

每dfowler的建议,另一种方法是实施-DB映射,而不是在内存中,这样更多的元数据可用于识别zombified连接。我是从的推荐的方法这已经实现。

Per dfowler's suggestion, an alternative approach would be to implement in-db mapping instead of in-memory, this way more metadata can be used to identify zombified connections. I'm hoping for a solution to the in-memory problem though, instead of re-architect away from a recommended approach that's already implemented.

推荐答案

试试这个样品在这里以下内容:

Try following this sample here:

https://github.com/DamianEdwards/NDCLondon2013/tree/master/UserPresence

这篇关于SignalR OnDisconnected - 一个可靠的方法来处理&QUOT;用户在线&QUOT;对于聊天室?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆