Как отправлять сообщения Azure SignalR клиентам на всех экземплярах приложения Azure с несколькими экземплярами - PullRequest
0 голосов
/ 04 декабря 2018

Мы оцениваем, как отправлять сообщения подключенным клиентам через SignalR.Наше приложение опубликовано в Azure и имеет несколько экземпляров.Мы можем успешно передавать сообщения клиентам, подключенным к одному и тому же экземпляру, но не к другим экземплярам.

Изначально мы рассматривали ServiceBus, но мы (возможно, по ошибке) выяснили, что AzureSignalR в основном должна быть служебной шиной, котораяобрабатывает весь бэкэнд для нас.

Мы устанавливаем signalR в Startup.cs, например:

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");
 });
}

Issue Мы должны хранить некоторые объекты, которые, вероятно, должны быть на служебной шине, а не храниться в отдельном экземпляре;Однако я не уверен, как сказать концентратору, что объекты должны находиться на шине, а не быть внутренними для этого конкретного экземпляра концентратора, как показано ниже:

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 имеет конечную точку для службы Azure), что она будет обрабатывать функциональность служебной шины для нас. Мы не уверены, какие шаги необходимо предпринять, чтобы обеспечить правильное функционирование.

Спасибо.

Ответы [ 3 ]

0 голосов
/ 05 декабря 2018

Причина этой проблемы в том, что я старательно пытался ограничить трафик сообщений.Я пытался отправлять сообщения только тем клиентам, которые просматривали одну и ту же запись.Однако, поскольку мои объекты были привязаны к конкретному экземпляру, он получал бы только идентификаторы соединения из объекта текущего экземпляра.

Дальнейшее тестирование (с использованием соответствия ARR) подтверждает, что при вызове Clients.All () все клиенты,включая сообщения в разных случаях, получите сообщение.

Итак, наша установка AzureSignalR выглядит правильной.

Текущее решение POC - в настоящее время тестируется -При регистрации клиента мы будем транслировать всем подключенным клиентам "Какое поле вы заблокировали для этого идентификатора?"-Если клиент находится на другом идентификаторе, он будет игнорировать сообщение.-Если у клиента нет заблокированных полей, он будет игнорировать сообщение.-Если у клиента заблокировано поле, он ответит на сообщение необходимой информацией.-AzureSignalR затем ретранслирует данные, необходимые для выполнения блокировки.

Это увеличивает количество сообщений, но незначительно.Но это разрешит несколько экземпляров, содержащих разные проблемы с ClientIds.

0 голосов
/ 31 января 2019

Просто мысль, а вы пробовали использовать 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}";
}
0 голосов
/ 04 декабря 2018

Я не уверен, что это подойдет вам, но мое приложение использует Redis для этого.Если у меня есть 4 клиента, скажем, 1 и 3 связаны с моим первым экземпляром, 2 и 4 - с моим вторым экземпляром.Экземпляр 1 ничего не знает о 2/4, а Экземпляр 2 ничего не знает о 1/3.Если я подключен к экземпляру 1 и мне нужно взаимодействовать с клиентами 2/4, Redis отправит сообщение этим клиентам.

https://stackexchange.github.io/StackExchange.Redis/

...