0

我有登录到 asp.net 网站的组织,当每个成员登录时,我将其 ConnectionId 和 OrganizationId 添加到 SignalR Hub OnConnected 覆盖中名为 OrganizationMembers 的静态 ConcurrentDictionary。我这样做是为了将数据发送到页面回调函数,该函数包含仅与该组织(SignalR 组)相关的数据。OrganizationId 将代表我向其发送数据的 SignalR 组。

同样在 OnConnnected 方法中,我将 OrganizationId 和 OrganizationId.ToString() 添加到另一个表示唯一组织 Id 的 ConcurrentDictionary。稍后我将解释为什么要存储 OrganizationId.ToString()。我存储唯一的组织 ID,以便在调用方法并反复休眠的后台任务中,我可以枚举唯一的组织 ID,并且只发送与其相关的每个组织(SignalR 组)数据。

在 OnDisconnected Hub 覆盖中,删除连接后,我想检查 OrganizationMembers ConcurrentDictionary 中的 OrganizationId 值,以查看该成员是否是最后一个具有该 OrganizationId 的要断开连接的成员,如果是,则将其从 UniqueOrganizations 字典中删除。我知道字典 Values.Contains() 是 O(n) 所以我真的很想避免这种情况。

因此,当任务枚举 UniqueOrganizations 时,不会有我试图不必要地向其发送数据的组织(SignalR 组),例如,来自同一组织的 5 个成员登录但后来都关闭了他们的浏览器,我不会尝试通过 SignalR 回调发送该组数据。

诚然,我不知道 SignalR 集线器的内部工作原理,因此如果我尝试向所有已断开连接的组织成员(SignalR 组)不必要地发送数据,这可能并不重要。

这是对 SignalR Hub 的过度思考吗?我是否应该担心确定最后一个组织(SignalR 组)成员是否已从集线器断开连接并且不从 UniqueOrganizations 中删除 OrganizationId?

如果可以这样做,我该如何避免字典 Values.Contains(),因为它是 O(n)?

// the key is the ConnectionId of an Organization Member
// the value is the OrganizationId of the Member
protected static ConcurrentDictionary<string, int> _organizationMembers;
public static ConcurrentDictionary<string, int> OrganizationMembers {
    get {
        if(_organizationMembers == null) {
            _organizationMembers = new ConcurrentDictionary<string, int>();
        }

        return _organizationMembers;
    }
}

// the key is the OrganizationId to send the specific data to
// the value is the OrganizationId converted to string
protected static ConcurrentDictionary<int, string> _uniqueOrganizations;
public static ConcurrentDictionary<int, string> UniqueOrganizations {
    get {
        if(_uniqueOrganizations == null) {
            _uniqueOrganizations = new ConcurrentDictionary<int, string>();
        }

        return _uniqueOrganizations;
    }
}


// Hub Code

public override Task OnConnected() {
    string connectionId = Context.ConnectionId;
    string organizationId = Context.Request.QueryString["organizationId"];
    int organizationIdValue = int.Parse(organizationId);

    OrganizationMembers.TryAdd(connectionId, organizationIdValue);
    UniqueOrganizations.TryAdd(organizationIdValue, organizationId);

    // the organizationId represents the SignalR group
    Groups.Add(connectionId, organizationId);
    return base.OnConnected();
}


public override Task OnDisconnected() {
    string organizationId = string.Empty;
    int organizationIdValue;
    string connectionId = Context.ConnectionId;

    OrganizationMembers.TryRemove(connectionId, out organizationIdValue);

    // if that happens to be the last Member connection to be removed
    // then remove the OrganizationId from the unique OrganizationIds
    // so it won't be a wasted enumeration and useless callback

    // I want to avoid this O(n) Contains()
    if(!OrganizationMembers.Values.Contains(organizationIdValue)) {
        UniqueOrganizations.TryRemove(organizationIdValue, out organizationId);
    }

    Groups.Remove(connectionId, organizationId);
    return base.OnDisconnected();
}


// Task code
foreach(int organizationIdValue in DataCache.UniqueOrganizations.Keys) {
// this is why I also stored the OrganizationId as a string so I wouldn't have to
// convert it to a string each time the dictionary is enumerated.
// I can just have the organizationId string ready to use as the SignalR Group.
    string organizationId = UniqueOrganizations[organizationIdValue];

    try {
        string organizationData = GetOrganizationData(organizationIdValue);
        _clients.Group(organizationId).sendToOrganizationData(organizationData);
    }
    catch(Exception ex) {
        _clients.Group(organizationId).sendToOrganizationError();
    }
}
4

1 回答 1

1

首先,SignalR 在发送给没有任何订阅的群组时不会浪费资源,因此您应该很好地发送给没有任何成员的群组,只要可以浪费几个周期来做这件事。

如果您没有太多组织,则可以将ConcurrentDictionary<int,int>所有组织ID 作为键,将连接成员的数量作为值。在 OnConnected 和 OnDisconnected 中可以分别使用 Interlocked.Increment 和 Interlocked.Decrement 来跟踪当前连接的成员。然后在您的任务中可以遍历键并跳过任何连接成员为零的组织。

如果您不介意调用 key.ToString(CultureInfo.InvariantCulture) 来获取组名,这个新的 ConcurrentDictionary 可以替换 _uniqueOrganizations。

于 2013-06-13T02:46:38.380 回答