SignalR and switching servers and connection lost - signalr

SignalR is running on a certain Server with an open connnection. If we need to bring up another server, then take the original down does SignalR automatically reconnect with the NEW server and start functioning like it was on the first?

We had a similar problem and what we discovered was that SignalR does not automatically reconnect with the new server. Our solution to was to not use the generated proxy. We used an intermediary endpoint whose job it is to provide the current url to use. To connect to SignalR our flow calls the endpoint gets the result using that result we connect to that server for SignalR then when the SignalR connection closes or is lost we repeat the process again as if for the first time.
async function reconnect(connection){
const signalRUrl = await request('<<our intermediary url>>');
connection.hub.url = signalRUrl;
connection.start();
}
var connection = $.hubConnection();
var hubProxy = connection.createHubProxy('yourHub');
hubProxy.on('message',function(){});
await reconnect(connection);
connection.onClose(function () {
reconnect(connection);
});

Related

Why is this SignalR messages being received multiple times?

I am using SignalR in a Blazor server-side app. I added the Microsoft.AspNetCore.SignalR.Client Nuget package (v5.0.11) to the project, and used the following code to create the hub connection...
HubConnection hubConnection = new HubConnectionBuilder()
.WithUrl("url")
.Build();
await hubConnection.StartAsync();
I then send out a message with the following code (Debug.WriteLine added to confirm what's going on)...
Debug.WriteLine($"{DateTime.Now.ToLongTimeString()} SignalR msg sent");
await hubConnection.SendAsync("Send", "Hello");
The component that is to handle such messages creates the hub connection and hooks up to the On handler as follows...
HubConnection hubConnection = new HubConnectionBuilder()
.WithUrl("url")
.Build();
hubConnection.On<string>("Receive", msg =>
Debug.WriteLine($"{DateTime.Now.ToLongTimeString()} SignalR msg received - {msg}"));
await hubConnection.StartAsync();
When the message is sent out, it is definitely only being sent once (which I can confirm from the output panel, where I only see the "sent" output once), but it is received twice.
Searching around, it seems that a common reason for this is if jQuery was loaded twice. However, I have checked this, and it's not the case. It's only being loaded the once. Furthermore, one of the other developers on the team tried it, and he got the message received 15 times! Even if we had accidentally included jQuery twice, we certainly didn't include it 15 times. Also, he's using exactly the same code as me (checked out from source control), so we should get the same results if this were the issue.
Anyone any idea why this could be happening? Thanks
This can occur when you initialise a HubConnection within a component and don’t configure IDispose or IAsyncDispose on your component to dispose the HubConnection.
In a Blazor Server-Side application there is already a SignalR host pushing updates to the client.
If you want to push updates from a page you can use a Singleton Service to handle the communication.
If you set up your own SignalR hub and use the SignalR client in your components, your application is something like the diagram below:
As you can see, the client is actually running on the server. The SignalR hub you've added adds processing and memory overhead and does not add any value.
I created a simple sample app that uses a service that clients listen to for updates:
https://github.com/conficient/BlazorServerWithSignalR

Signalr with redis backplane, sending message to user

I am researching to use signalr to send messages from an api to a specific user (keyword). Locally, I have everything working as designed (even using redis backplane).
When I move up to an actual environment with multiple servers (azure app service), it seems like messages to specific users don't flow through the backplane. If I send a message to Clients.All it works. But, if I send a message to Clients.User(username), the message is never received. This works locally because it's a single server, but doesn't seem to work in multiple servers.
await this.impersonationContext.Clients.User(mainUserName).SendAsync("msguser", new object[] { mainUserName });
await this.impersonationContext.Clients.All.SendAsync("msg", new object[] { "wtf" });
If I call the above code, only the msg event is fired, but the msguser is never recieved.
Could I be missing something on the setup? That's my assumption, I cannot be the only one doing this.
Below is my setup in the startup.cs. This is using .net core 5 with the latest nugets, etc
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
})
.AddStackExchangeRedis(this.Configuration["Redis:Cache"], options => {
options.Configuration.ChannelPrefix = "ImpersonationService";
});
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
Any help would be appreciated.
Update #1
Looks like the user info is being pushed through the backplane (username hidden). So maybe its the connection from javascript client?
Update #2
Getting closer...looks like the subscription isn't setup for the specific user like I see locally. Could this be websockets?
Update #3
Found it....sorta.
I was missing the authorize attribute on the hub, which was allowing the websocket to connect, even though websockets isn't authenticating for some reason (different issue). Once I added the authorize, it will drop down to long polling with the user info and works as designed

SignalR fails to send message to a group when running through LoadBalancer

In order to send SignalR message to a specific user I create a Group for each user upon connection
public override Task OnConnected()
{
log.DebugFormat("Connected. Connection Id = {0} UserId = '{1}'", Context.ConnectionId, UserHelper.UserId);
Groups.Add(Context.ConnectionId, UserHelper.UserId);
return base.OnConnected();
}
Now when a message comes in I send it a group in the following way:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<AlertsHub>();
foreach (var recipient in recipients)
{
hubContext.Clients.Group(recipient).broadcastAlertMessage("Group", msg);
}
That works fine when deployed to a server, but for some reason not working when i access the server through our company load balancer (Citrix Netscaler SDX 11500) but eventually hitting the same sole box.
There is no issue sending messages to all clients
hubContext.Clients.All.broadcastAlertMessage("All", msg);
Also i can keep the connection IDs internally and send messages to a specific client works
hubContext.Clients.Client(AlertsHub.UserToConnectionIdDict["admin"]).broadcastAlertMessage("trageted client", msg);
Why "Group" message doesn't work?
By default, a SignalR server is only aware of and will only send messages to clients connected directly to itself. This is because each SignalR server manages its own messages using its own message bus. Without special configuration, SignalR has no way to know there are other clients connected to a different SignalR server at the same global address.
Fortunately SignalR has scaleout providers that allow you to configure all your SignalR servers in such a way that they can communicate with each other by sharing single message bus.
This "Introduction to Scaleout in SignalR" should provide you with the info you need to get SignalR working properly behind a load balancer: http://www.asp.net/signalr/overview/signalr-20/performance-and-scaling/scaleout-in-signalr

Using SignalR client through a web proxy

I am using the SignalR .NET client library to create a console app/win service to connect to a signal R Hub using HTTPS on the web. Some of these clients may require a web proxy to access the internet. Where/How do I set the web proxy for the SignalR client?
How on earth is this not a real Question guys? I cant get the signalR to connect to the web server hub when the client is behind a firewall/TMG proxy server.
Use Connection.Proxy property for that - https://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.client.connection.proxy(v=vs.111).aspx
Example:
var hubConnection = new HubConnection(url);
var webProxy = new WebProxy(new Uri(“address”));
webProxy.Credentials = new NetworkCredential(“Username”, “Password”);
hubConnection.Proxy = webProxy;
Try this:
var webProxy = new WebProxy(new Uri(proxyUrl));
var connection = new HubConnectionBuilder().WithUrl("signalR hub endpoint", h => h.Proxy = webProxy).Build();
In fact we implemented signalr to have a kind of real time progress about some document generation.
Some of our customers couldn't create documents any more because the connection disconnected.
"Maybe" because of some proxy server who not really blocked but hold the request for a while to scan it, so that signalr client got a timeout

what is the best design to use connections in signalr

I have some doubts regarding my software design with signalr,
usually I start with this code:
var connection = $.hubConnection();
var notifier = connection.createHubProxy('notifier');
connection.start()
.done(function () {
//alert('connection was succesful');
// your event handlers
});
UPDATED QUESTION
If you have a common click event handler that will only use signalR to broadcast a message, and this event handler is on a separate javascript file (out of the context of the current connection):
should I open the connection in the external script? or
should I send a reference of the proxy (already connected) to the external script?
When your page doesn't need a connection you should not open one.

Resources