Base solution for your next web application
Open Closed

Microsoft signalR is not working with multiple instances #11117


User avatar
0
shedspotter created

What is your product version? 11.0.0

What is your product type (Angular or MVC)? Angular

What is product framework type (.net framework or .net core)? .NET 6

Hi, We are using microsoft signalR service for real time communication, but the service wont work if we scale out the app service instance from one to two instance, while using single instacne it is working fine. for exp : Ideally the SignalR connection is established with the instance1. But the request API went to the other app service instance2. The instance2 won't be able to send this message because this specific server connection is not associated with it.

Please help us to resolve that issue we are created the ticket for the same in microsoft and ans was "With this, root cause of the issue is identified, and this issue is expected behavior as per your architecture/application design"

Thanks


17 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @shedspotter

    You can solve this in two ways;

    1. You can use Redis for scaling the SignalR, see https://docs.microsoft.com/en-us/aspnet/core/signalr/redis-backplane?view=aspnetcore-6.0
    2. Or, you can use Azure SignalR Service if you are hosting on Azure, see https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-dotnet-core
  • User Avatar
    0
    shedspotter created

    Hi @ismcagdas We already using the Azure SignalR Service service but still we are getting the same issue

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Could you share how did you configure your Startup.cs for Azure SignalR service ?

  • User Avatar
    0
    shedspotter created

    Hi @ismcagdas I have configured like this

    #if DEBUG services.AddSignalR(); #else services.AddSignalR(options => { options.EnableDetailedErrors = true; }) .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[] { new ServiceEndpoint(_appConfiguration["SignalR:PrimaryConnectionString"], EndpointType.Primary, "primary--signalrservice.service.signalr.net"), new ServiceEndpoint(_appConfiguration["SignalR:SecondaryConnectionString"], EndpointType.Secondary, "secondary-signalrservice.service.signalr.net"), });

    #endif

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Thanks, this seems fine. Is it possible that your app is running in DEBUG mode on your production ? Cound you be sure that AddAzureSignalR is executed ?

    Thanks,

  • User Avatar
    0
    shedspotter created

    Hi, our production application is running in release mode and I am 100% sure that AddAzureSignalR is executed

  • User Avatar
    0
    shedspotter created

    Hi, I have created a POC project to verify the singalR issue with multiple instace in azure and the POC is working fine with microsft signalR but in asp net zero application it is not working correctly.If you need I can share the POC project

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @shedspotter

    Is it possible to share teh problematic project via email ?

    Thanks,

  • User Avatar
    0
    shedspotter created

    Hi, sorry I am unable to share the whole project it would be great if we can schedule a call

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @shedspotter

    Sure, please send an email to [email protected] and we can arrange the meeting.

  • User Avatar
    0
    shedspotter created

    Hi @ismcagdas , I have send you the mail in same email thread which is already running

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @shedspotter

    Thanks, we got the email and will reply back soon.

  • User Avatar
    0
    JeffMH created

    Even though you are using Azure SignalR, the app is still responsible for storing who is connected, so when it has to send a message to a particular user, you know how to send it. This is done with IOnlineClientManager. The default version of this uses memory Cache to store who is connected which in a scaled environment doesn't work. You have to change this to be some shared cache . I've seen a thread in here recently where someone used Azure storage for this, we use Redis for this. I copied code from someone else a long while ago that still works today. Would be happy to share the code with either of you. Sorry, I'm in the middle of a big deployment and I noticed this message and new without looking that this was probably the problem. I emailed my code to info@ and they can reply to those that need it.

    It would be a nice addition to the codebase.

  • User Avatar
    0
    shedspotter created

    Hi @ismcagdas Please let me know when we are scheduling the call the as we are not hearing any repsonses from your side on this issue as well as the other performance related issue

    Thanks

  • User Avatar
    0
    shedspotter created

    Hi @ismcagdas, Could you please me to saving the client connection Id in DB

    Thanks

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @shedspotter

    We can continue via email for this topic. When you use Redis, it will store the connection. So it is similar to saving the connection to DB.

  • User Avatar
    0
    JapNolt created

    EDIT: I posted to soon. The code below works for sending messages to clients but does not help with tracking a count of clients or if a user is online.

    I recently encountered the same problem and decided to use the Groups feature in SignalR. This blog article recommended that and per my limited testing, it works well https://consultwithgriff.com/signalr-connection-ids/

    In the OnConnectedAsync, I add the connection to a group for the tenant and also to a group for the user. I would recommend that ANZ adopt this approach because I believe it would work with either a single server or a scaled out system.

    I am willing to share more code if you wish.

    Here's an example of how I extended the AbpCommonHub

    ` public class AbpCommonHubExtended : AbpCommonHub { private readonly IOnlineClientManager _onlineClientManager;

        public AbpCommonHubExtended(
            IOnlineClientManager onlineClientManager,
            IOnlineClientInfoProvider clientInfoProvider) : base(onlineClientManager, clientInfoProvider)
        {
            _onlineClientManager = onlineClientManager;
        }
    
        public override async Task OnConnectedAsync()
        {
            await base.OnConnectedAsync();
    
            var client = _onlineClientManager.GetByConnectionIdOrNull(Context.ConnectionId);
    
            await Groups.AddToGroupAsync(
                client.ConnectionId,
                GetGroupName_AllClientsForTenant(
                    client.TenantId.GetValueOrDefault()));
    
            await Groups.AddToGroupAsync(
                client.ConnectionId,
                GetGroupName_AllClientsForUser(
                    client.TenantId.GetValueOrDefault(),
                    client.UserId.GetValueOrDefault()));
        }
    
        public static string GetGroupName_AllClientsForTenant(int tenantId)
        {
            return $"t_{tenantId}";
        }
        public static string GetGroupName_AllClientsForUser(int tenantId, long userId)
        {
            return $"t_{tenantId}_u_{userId}";
        }
    }
    

    `