1

I am hoping that someone out there has come across this or can at least spot what is wrong with my code.

I have created a browser based Chat application using MVC and SignalR, because I need a user to Login to Chat I have implemented a Register against the MVC controller which adds the User to an internal UserList

public static void Register(string userName, string userRole, string userBase, string userLocation)
{
    var userInfo = (from user in UsersList where user.Name == userName select user).FirstOrDefault();
    if (userInfo != null)
    {
        userInfo.Location = userLocation;
        userInfo.Role = userRole;
        userInfo.Base = userBase;
        return;
    }

    var rgx = new Regex("[^a-zA-Z0-9 -]");
    UsersList.Add(
        new UserInfo
        {
            Name = userName,
            NameId = rgx.Replace(userName, "").Replace(" ", ""),
            Base = userBase,
            Location = userLocation,
            Role = userRole,
            Group = "Cleo",
            Container = userLocation,
            ContainerId = rgx.Replace(userLocation, "").Replace(" ", "").Trim()
        });
}

And from the client side I instantiate the chatHub and then call Connect

$(function () {
    var objHub = $.connection.chatHub;
    loadClientMethods(objHub);
    $.connection.hub.start().done(function () {
        loadEvents(objHub);
    });

function loadEvents(objHub) {
    $('#hState').val('open');
    objHub.server.connect(gloalUserNameId);
}

All of this works well when on a single server and all the connections are held in memory, BTW, this has been in production for over a year.

Now, I had a requirement to establish better resiliency for our Chat application, for example in a load balanced environment but since everything is in memory I immediately hit the problems where User A connected to Server X and User B connected to Server Y (neither could see the other, you can see the problem).

So after digging around on the web for many long hours I came across a couple of articles that talk about scaling out SignalR using either Redis or SQL. Since our company uses SQL and has a lot of experience with this I chose this article.

I included the nuget package and modified my Startup class to the following

string connectionString = "Server=.;Trusted_Connection=True;Database=CleoChatHost;";
GlobalHost.DependencyResolver.UseSqlServer(connectionString);
app.MapSignalR();

I configured two websites in IIS on separate ports to simulate a load balanced environment for testing purposes, eg. http://localhost:21215/ and http://localhost:21216/ which I wil call SiteA and SiteB

The following happens when I run each site:

  • Launch SiteA with ?userName=TestUserA, then launch SiteB with ?userName=TestUserB -- The result is that SiteA loads (initially with no contacts), then SiteB loads, SiteA user list is updated with TestUserB. SiteB has not users.

NOTE: Doing the opposite of the above gives the same result.

I am at a loss as to what the issue is, I have checked the SQL database and can see that the backplane has created the SQL tables and data is going into them. I have tried to also implement the Redis solution with the same issue as above.

Has anyone implemented this and come across the same problem? or could anyone shed a little light on what the issue might be please?

EDIT After some further diagnosis/testing it appears that the problem may be related to the internal UserList that is maintained on each server and I believe the sequence of events are as follows:

  • UserA logs onto SiteA
  • SiteA adds UserA to the UserList on SiteA
  • SiteA executes hub.Connect(...) from the client which in turn executes Clients.Client(...).DrawTree(...) and Clients.AllExcept(...).addUser(...)
  • The preceding call updates SiteA user list, and notifies all connected clients of a new user
  • SiteA is connected and has no active users
  • UserB logs onto SiteB
  • SiteB adds UserB to the UserList on SiteB
  • SiteB executes hub.Connect(...) from the client which in turn executes Clients.Client(...).DrawTree(...) and Clients.AllExcept(...).addUser(...)
  • The preceding call updates SiteB user list, and notifies all connected clients of a new user -- This executes addUser on the SiteA client to add UserB to the user list
  • SiteB is connected and has no active users, while SiteA has one active user

I believe the issue is that the SQL backplane is only going to deal with messages when they are sent and have affirmed this from the naming convention of the tables in the SQL database. The internal UserList however is not synchronised between the load balanced servers. The fact that SiteA is updated with UserB is only due to the broadcast of addUser when UserB connected to SiteB (meaning SiteB only ever contains UserB and SiteA only ever contains UserA)

So to update my question above, how would I go about maintaining the UserList between servers? Would I use the SQL backplane to achieve this?

Neil Stevens
  • 3,534
  • 6
  • 42
  • 71
  • did you ever find how to make this work? We are having similar problems see my question here https://stackoverflow.com/questions/47007369/signalr-combined-with-load-balancer-missing-messages – Steven Oct 30 '17 at 02:49

0 Answers0