0

我们正在评估如何通过 SignalR 向连接的客户端发送消息。我们的应用程序在 Azure 中发布,并且有多个实例。我们能够成功地将消息传递给连接到同一实例的客户端,而不是其他实例。

我们最初关注的是 ServiceBus,但我们(可能是错误地)发现 AzureSignalR 基本上应该是一个为我们处理所有后端内容的服务总线。

我们在 Startup.cs 中设置 signalR 如:

public void ConfigureServices(IServiceCollection services)
{
    var signalRConnString = Configuration.GetConnectionString("AxiomSignalRPrimaryEndPoint");
    services.AddSignalR()
    .AddAzureSignalR(signalRConnString)
    .AddJsonProtocol(options =>
   {
       options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
   });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
 app.UseAzureSignalR(routes =>
 {
    routes.MapHub<CallRegistrationHub>("/callRegistrationHub");
    routes.MapHub<CaseHeaderHub>("/caseHeaderHub");
    routes.MapHub<EmployeesHub>("/employeesHub");
 });
}

问题
我们必须存储一些可能应该在服务总线上的对象,而不是存储在单个实例中;但是,我不确定如何告诉集线器对象应该在总线上,而不是在集线器的特定实例内部,如下所示:

public class EmployeesHub : Hub
{
    private static volatile List<Tuple<string, string, string,string, int>> UpdateList = new List<Tuple<string, string, string,string,int>>();
    private static volatile List<Tuple<string, int>> ConnectedClients = new List<Tuple<string, int>>();
}

我们有一些函数需要向所有正在查看当前记录的已连接客户端发送消息,无论它们位于什么实例中:

public async void LockField(string fieldName, string value, string userName, int IdRec)
{
    var clients = ConnectedClients.Where(x => x.Item1 != Context.ConnectionId && x.Item2 == IdRec).Select(x => x.Item1).Distinct().ToList();
    clients.ForEach(async x =>
    {
        await Clients.Client(x).SendAsync("LockField", fieldName, value, userName, true);
    });
    if (!UpdateList.Any(x=> x.Item1 == Context.ConnectionId && x.Item3 == fieldName && x.Item5 == IdRec))
    {               
        UpdateList.Add(new Tuple<string, string, string,string,int>(Context.ConnectionId,userName, fieldName, value, IdRec));
    }
}

这不适用于不同的实例(这是有道理的,因为每个实例都有自己的对象。但是,我们希望通过使用 AzureSignalR 而不是 SignalR(AzureSignalR conn 字符串具有 Azure 服务的端点),它将处理为我们提供服务总线功能。我们不确定要采取哪些步骤才能使其正常运行。

谢谢。

4

2 回答 2

0

只是一个想法,但您是否尝试过使用 SignalR Groups? https://docs.microsoft.com/en-us/aspnet/core/signalr/groups?view=aspnetcore-2.2#groups-in-signalr

您可以尝试为每个组合创建一个组,IdRec然后fieldName将消息广播到该组。这是我认为您的LockField功能可能看起来如何的要点:

public async void LockField(string fieldName, string value, string userName, int IdRec)
{
    string groupName = GetGroupName(IdRec, fieldName);
    await Clients.Group(groupName).SendAsync("LockField", fieldName, value, userName, true);
    await this.Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}

您可以随心所欲地实现该GetGroupName方法,只要它产生唯一的字符串。一个简单的解决方案可能类似于

public string GetGroupName(int IdRec, string fieldName)
{
    return $"{IdRec} - {fieldName}";
}
于 2019-01-31T00:25:13.240 回答
0

这个问题的原因是我抢先尝试限制消息流量。我试图只向正在查看同一记录的客户发送消息。但是,因为我的对象是特定于实例的,它只会从当前实例的对象中获取连接 ID。

进一步的测试(使用 ARR 亲和性)证明,在 Clients.All() 调用中,所有客户端(包括不同实例中的客户端)都会收到消息。

因此,我们的 AzureSignalR 设置似乎是正确的。

当前 POC 解决方案 - 正在测试
- 当客户端注册时,我们将向所有连接的客户端广播“您为此 Id 锁定了哪些字段?”
- 如果客户端使用不同的 ID,它将忽略该消息。
- 如果客户端没有锁定任何字段,它将忽略该消息。
- 如果客户端锁定了某个字段,它将使用所需信息响应消息。
-AzureSignalR 然后将重新广播执行锁定所需的数据。

这会增加消息数量,但不会显着增加。但它将解决持有不同连接 ClientIds 问题的多个实例。

于 2018-12-04T21:56:01.680 回答