检查组是否包含任何客户端
Check if a group contains any client
上下文
我有以下工作流程让服务器从连接的客户端查询数据:
// Server-side code
async Task<string> RequestDataFromClient(string groupName)
{
var responseId = GenerateUniqueId();
// let the clients know that we want something
await hubContext.Clients.Group(groupName).Request(responseId);
try
{
return await WaitForIncomingCall(responseId);
}
catch(TimeoutException)
{
return GetDataFromFallbackMethod();
}
}
服务器首先向给定 SignalR 组中的所有客户端发送 Request
。然后它等待任何客户端使用 WaitForIncomingCall
使用给定的 responseId
调用 Respond
集线器方法。 WaitForIncomingCall
具有以下签名
Task<string> WaitForIncomingCall(responseId)
如果一段时间后没有客户端调用 Respond
hub 方法,WaitForIncomingCall
将抛出 TimeoutException
。如果发生此类异常,服务器将使用回退方法来检索数据(例如,从缓存中)。
问题
如果给定的 SignalR 组中不存在客户端,服务器将等待直到超过超时,直到它启动回退方法。这将对 RequestDataFromClient
的调用者产生显着的延迟。如果 SignalR 组中没有客户端,我宁愿直接调用回退方法。因此,如果没有人可能会回应,甚至不要问。
问题
如何确定 SignalR 组是否为空,或者 Request
调用是否可能已到达连接的客户端?一旦负载平衡发挥作用,手动跟踪连接似乎并不是一个好主意。
据我所知,目前Asp.net核心SignalR没有内置方法来检查组是否为空或组中是否存在客户端。而从official document中,我们还可以发现:
重新连接时不会保留组成员身份。重新建立连接时需要重新加入该组。无法对组的成员进行计数,因为如果应用程序扩展到多个服务器,则此信息不可用。
要检查组是否包含任何连接的客户端,作为解决方法,在 hub 方法中,您可以定义一个全局变量来存储组名称和在线计数,或者您可以存储用户信息(用户名, 群组名称, 在线状态等)在数据库中。
然后,在OnConnectedAsync 方法中,您可以将用户添加到组并计算在线数或更改用户的在线状态,在OnDisconnectedAsync 方法中,从组中删除用户并更改在线用户数。创建一个自定义方法 check/get 在线用户数。
代码如下(本示例代码中,我使用登录用户名创建群组,您可以根据自己的情况更改):
[Authorize]
public class ChatHub : Hub
{
private static Dictionary<string, int> onlineClientCounts = new Dictionary<string, int>();
public override Task OnConnectedAsync()
{
var IdentityName = Context.User.Identity.Name;
Groups.AddToGroupAsync(Context.ConnectionId, IdentityName);
int count = 0;
if (onlineClientCounts.TryGetValue(IdentityName, out count))
onlineClientCounts[IdentityName] = count + 1;//increment client number
else
onlineClientCounts.Add(IdentityName, 1);// add group and set its client number to 1
return base.OnConnectedAsync();
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task SendMessageToGroup(string sender, string groupname, string message)
{
//check if group contains clients or not via the global variable or check database.
await Clients.Group(groupname).SendAsync("ReceiveMessage", sender, message);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var IdentityName = Context.User.Identity.Name;
await Groups.RemoveFromGroupAsync(Context.ConnectionId, IdentityName);
int count = 0;
if (onlineClientCounts.TryGetValue(IdentityName, out count))
{
if (count == 1)//if group contains only 1client
onlineClientCounts.Remove(IdentityName);
else
onlineClientCounts[IdentityName] = count - 1;
}
await base.OnDisconnectedAsync(exception);
}
}
上下文
我有以下工作流程让服务器从连接的客户端查询数据:
// Server-side code
async Task<string> RequestDataFromClient(string groupName)
{
var responseId = GenerateUniqueId();
// let the clients know that we want something
await hubContext.Clients.Group(groupName).Request(responseId);
try
{
return await WaitForIncomingCall(responseId);
}
catch(TimeoutException)
{
return GetDataFromFallbackMethod();
}
}
服务器首先向给定 SignalR 组中的所有客户端发送 Request
。然后它等待任何客户端使用 WaitForIncomingCall
使用给定的 responseId
调用 Respond
集线器方法。 WaitForIncomingCall
具有以下签名
Task<string> WaitForIncomingCall(responseId)
如果一段时间后没有客户端调用 Respond
hub 方法,WaitForIncomingCall
将抛出 TimeoutException
。如果发生此类异常,服务器将使用回退方法来检索数据(例如,从缓存中)。
问题
如果给定的 SignalR 组中不存在客户端,服务器将等待直到超过超时,直到它启动回退方法。这将对 RequestDataFromClient
的调用者产生显着的延迟。如果 SignalR 组中没有客户端,我宁愿直接调用回退方法。因此,如果没有人可能会回应,甚至不要问。
问题
如何确定 SignalR 组是否为空,或者 Request
调用是否可能已到达连接的客户端?一旦负载平衡发挥作用,手动跟踪连接似乎并不是一个好主意。
据我所知,目前Asp.net核心SignalR没有内置方法来检查组是否为空或组中是否存在客户端。而从official document中,我们还可以发现:
重新连接时不会保留组成员身份。重新建立连接时需要重新加入该组。无法对组的成员进行计数,因为如果应用程序扩展到多个服务器,则此信息不可用。
要检查组是否包含任何连接的客户端,作为解决方法,在 hub 方法中,您可以定义一个全局变量来存储组名称和在线计数,或者您可以存储用户信息(用户名, 群组名称, 在线状态等)在数据库中。
然后,在OnConnectedAsync 方法中,您可以将用户添加到组并计算在线数或更改用户的在线状态,在OnDisconnectedAsync 方法中,从组中删除用户并更改在线用户数。创建一个自定义方法 check/get 在线用户数。
代码如下(本示例代码中,我使用登录用户名创建群组,您可以根据自己的情况更改):
[Authorize]
public class ChatHub : Hub
{
private static Dictionary<string, int> onlineClientCounts = new Dictionary<string, int>();
public override Task OnConnectedAsync()
{
var IdentityName = Context.User.Identity.Name;
Groups.AddToGroupAsync(Context.ConnectionId, IdentityName);
int count = 0;
if (onlineClientCounts.TryGetValue(IdentityName, out count))
onlineClientCounts[IdentityName] = count + 1;//increment client number
else
onlineClientCounts.Add(IdentityName, 1);// add group and set its client number to 1
return base.OnConnectedAsync();
}
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
public async Task SendMessageToGroup(string sender, string groupname, string message)
{
//check if group contains clients or not via the global variable or check database.
await Clients.Group(groupname).SendAsync("ReceiveMessage", sender, message);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var IdentityName = Context.User.Identity.Name;
await Groups.RemoveFromGroupAsync(Context.ConnectionId, IdentityName);
int count = 0;
if (onlineClientCounts.TryGetValue(IdentityName, out count))
{
if (count == 1)//if group contains only 1client
onlineClientCounts.Remove(IdentityName);
else
onlineClientCounts[IdentityName] = count - 1;
}
await base.OnDisconnectedAsync(exception);
}
}