How to handle SignalR implementation under LoadBalancer Environment - asp.net

I am fairly new to signalr concepts. I have a scenario where load balancing is in place with two servers. The situation is that client request is taken by the load balancer and redirects it to a one of the server based on the load. After redirection the connection from client to the server is lost. Important thing here is that client request is for different purposes i.e they call different methods on the hub. The server continues processing the request further and during this time if it detects any status change, it has to push the notification back to the clients. However at this point, server won't be knowing to which client it has to respond back as the load balancer doesn't store any information about the same once the connection is lost from client to server. How to handle this kind of scenario?. Should I be manually storing session id and other details in a table?
I have gone through the scaleout options suggested for load balancing using backplane by the signalr team(Azure service bus, Redis and SQL Server). However my scenario is little different. Any help will be appreciated.

Related

Can I avoid of using a SignalR backplane behind a load balancer?

I use SignalR in order to expose RabbitMQ messages to browsers. This works fine with one app instance obviously. The question is if it could work with multiple instances too without a backplane. I understand that SignalR client could be disconnected from the pod A and connected back to the pod B but what exactly is the issue here? I am fine to lose some messages during reconnection. Is it the only issue? Is reconnection to the pod B treated as a regular new connection so that the client is just subscribed again as it was subscribed normally without reconnection? Or the system doesn't have input parameters it had during initial subscription and therefore it cannot resubscribe without hints?
As long as all of your SignalR servers are getting the same data from RabbitMQ or getting only the data for the clients connected to them, you don't need a backplane.
You will need a backplane if you have one of the following:
Clients can communicate with one another.
One one SignalR server is connected to RabbitMQ but clients can connect to multiple SignalR servers.
SignalR servers are connected to different queues or getting different data from the same queue.
I have a similar setup with a database instead of RabbitMQ and need a backplane to either have only one of the SignalR servers access the database (and have data be sent to all clients) or to share the database load between servers (and have data be sent to all clients). This way, the server getting the data can have it sent to a client connected to a different server.
I am using SignalR for ASP.NET and the servers do not know who is subscribed to the other servers. All messages are sent over the backplane and each server determines if they apply to their connected clients. This works well with broadcasts for example or if the same user has multiple clients to make sure they all get the same data regardless of the server.

SignalR OnConnected firing on different server to the one which it's actually connected

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
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.
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.

How does GCP Load balancers will manage websocket connections?

Clients are connecting to API gateway server through websocket connection. This server just orchestrates swarm of cloud functions, that are handling all of the data requesting and transforming. Server is statefull - it holds essential session data, which is defining, for example, what cloud functions are allowed to be requested by a given user.
This server doesn't use socket to broadcast data, so socket connections are not interacting between each other, and will not be doing this. So, all it needs to handle is single-client-to-server communication.
What will happen if i'll create bunch of replicas and put load balancer in front of all of them (like regular horizontal scaling)? If a user got connected to certain server instance, then his connection will stick there? or it will be switching between instances by load balancer?
There is a parameter available for load balancer that allows you to do what you are looking for: Session affinity.
"Session affinity if set attempts to send all network request from the same client to the same virtual machine instance."
Actually even if it seems to be related to load balancer you set it while creating target pools and/or backends. You should check if this solution can be applied to your particular configuration.

How to get SignalR Connection to Respect Session Timeout

Using SignalR, it's designed to maintain a long-running connection to the web server. However, I have a scenario where the SignalR connection maintains an open connection to the web server, long after the ASP.Net session has expired. This happens even though neither the client nor the server is sending an data.
How can I set things up so that once the Asp.net session expires, the SignalR connection terminates, freeing up connections on the server?
This is desired because the web servers run on a load balancer, and when taking a server out of the rotation, we need the # of current connections to generally represent the number of active sessions - not SignalR connections that are still kicking just because user left browser open.
SignalR Server does not allow to disconnect a particular client connection (this would be super useful). So the client is responsible to do that.
A workaround would be to add an event listener on the client and disconnect from there, i.e:
hubProxy.On<string>("YouAreUselesLetMeAlone", _ => connection.Dispose());
Obviously, to make this work you need a relation (ConcurrentDictionary?) of asp.net sessions <-> signalr connections, so you can call this method in the correct client when the asp.net session ends.

Are all clients in a group assured to receive signalR calls when SignalR is scaled out behind load balancer?

I've been looking into SignalR implementation incorporated with a load balancer
and have a few basic (if not simple sounding) questions.
I must preface this by saying I've got zero (0) experience with load balancers.
We will have 2 servers sitting behind a load balancer.
The client is an ASP .Net application.
We've been told that the load balancer maintains session affinity.
Consider the following scenario:
Client1 & Client2 -- connect to GroupA--> Server1
Client3 & Client4 -- connect to GroupA--> Server2
1) Server1 makes a client call to GroupA - this assumes that
Clients 1-4 will get the notification, correct?
2) How does the processing occur on this?
3) Is it a function of SignalR itself, or the load balancer?
4) When sending messages at the group level, do messages only get delivered to the client
apps associated with the group on that specific server, or will messages get forwarded
to all clients of that group?
Does anyone have any thoughts on this?
Thanks,
JB
I believe the scenario you're looking at requires a SignalR Backplane to be setup.
Here's a relevant selection from the article but you'll want to read the full thing to answer your specific questions:
Each server instance connects to the backplane through the bus. When a
message is sent, it goes to the backplane, and the backplane sends it
to every server. When a server gets a message from the backplane, it
puts the message in its local cache. The server then delivers messages
to clients from its local cache.

Resources