【问题标题】:How to send Azure SignalR messages to clients on all instances of a multi-instance Azure Application如何在多实例 Azure 应用程序的所有实例上向客户端发送 Azure SignalR 消息
【发布时间】:2018-12-04 19:39:34
【问题描述】:

我们正在评估如何通过 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 服务的端点)它将为我们处理服务总线功能。我们不确定要采取哪些步骤才能使其正常运行。

谢谢。

【问题讨论】:

  • 创建了标签,感谢您指出:)

标签: azure signalr multiple-instances azure-signalr


【解决方案1】:

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

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

所以,我们的 AzureSignalR 设置似乎是正确的。

当前 POC 解决方案 - 正在测试中
-当一个客户端注册时,我们会向所有连接的客户端广播“你为这个Id锁定了什么字段?”
- 如果客户端使用不同的 Id,它将忽略该消息。
- 如果客户端没有锁定任何字段,它将忽略该消息。
-如果客户端有一个字段被锁定,它将用所需的信息回复消息。
-AzureSignalR 然后将重新广播执行锁定所需的数据。

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

【讨论】:

    【解决方案2】:

    只是一个想法,但您是否尝试过使用 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}";
    }
    

    【讨论】:

    • 我们最终选择了 AzureSignalR。它基本上是为它设计的服务总线。效果很好。
    猜你喜欢
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 2016-09-19
    相关资源
    最近更新 更多