1

I'm using SignalR and a web farm in IIS, currently with 3 servers and requests are load balanced via ARR.

There are certain external events that happen which I want to be processed by the server to which the client is connected. So I want to track which of the 3 servers the client is currently connected.

I thought that I could do this using OnConnected and within that method store the MachineName against the ConnectionID in redis. The problem is that OnConnected seems to get called an a different server to the one that the client is connected to.

Upon investigating, it seems that there are three calls, one to /negiotate one to /connect and one to /start. The /connect seems to be the websocket connection that is kept up for the duration, the others are just transient. These three connections can happen on different servers, and it seems that the websocket connection can be to server A (so that's the server that the client's SignalR connection is going to), but the OnConnected gets fired on server B.

I was wondering if I'm overlooking something that will let me see which server the SignalR connection is actually connected to?

Thanks,

Will

2 Answers2

0

If you are going to use a web farm, then you need to implement a backplane to track all of the messaging.

https://learn.microsoft.com/en-us/aspnet/signalr/overview/performance/scaleout-in-signalr

Without a proper backplane implementation its impossible to do what you want to do.

Kelso Sharp
  • 972
  • 8
  • 12
0

I believe that is something you would have to save. Assuming you are using a database for mapping users, you could have an additional field such as "LoggedInOn" and store the server host name or other identifier.

However, other than some aspect of troubleshooting your are looking to do, proper send/receive of messages should cross the backplane to all servers. This way no matter which server they are connected to, messages are received.

If you have external events as you say, once they complete and a message is ready to be sent back to a client, the backplane should push that to all servers.

If that's not happening I would review the docs as Kelso Sharp stated.

Frank M
  • 1,379
  • 9
  • 18
  • My question is more about *how* I go about setting that LoggedInOn field, if the OnConnected gets fired on a different server. The reason that I don't just want all the servers handling the message, is that there may be some database access and other processing happening so it would be inefficient and not scale well if all the servers in the farm were to do this for every client. – William Luke Mar 19 '19 at 13:08
  • What I've done just now is added a new method to the hub, "OnReallyConnected" which I'm calling in the .start() of the client, and from there I can tell the server it's really connected to, but that seems really clunky, there must be a cleaner way? – William Luke Mar 19 '19 at 13:25
  • All the servers handle the message from the backplane by design. Server A may see the client connection and send it to the client but the additional servers in the farm will not see the client (if you are sending to just one client). If you send a message from one client to many clients it has to span all servers to get to everyone. This is by design and I believe you may be concerned about an inefficiency where there is not one. You can always use crank, see the docs, to do density testing. – Frank M Mar 19 '19 at 16:46
  • I'm concerned about inefficiency that's correct. I have an external event (unrelated to SignalR), lets say its "notify user A about event X", that involves a number of database calls to load some records, and then send that information to the user. I don't want those database calls getting repeated on *all* the servers in the farm, and I don't want the user to get a notification from each server in the farm. If I can tell which user is connected to which server, I can then have that server handle the events for the user, and the other servers ignore those events. – William Luke Mar 20 '19 at 14:44
  • If the user initiates the call, the request would go to the server that user is connected to, which then initiates your db query and upon completion use - Clients.Caller.senddatatouser(yourdata);. An external event that triggers the db query, then you need to look up the connectionID for that user and use - Clients.User(userId).senddatatouser(yourdata);. This will send get to the user no matter which server they are connected to. – Frank M Mar 20 '19 at 15:29
  • The external event is triggered on all the servers though (it's on a redis pub/sub), so I need to ensure it's only handled once, and it seemed to make sense to have it handled on the server that the user to which the event relates is connected (I appreciate that with the backplane it could be on any server). I'm guessing there's just no way to tell this in the OnConnected at all, and I just need update my list of users each time a method in the hub is called with which server they're connected to? – William Luke Mar 21 '19 at 18:26