Reputation: 6883
I have a website which deployed on the Google Cloud. This website written with asp.net core (v2.2) and signalr.
My application architecture is that I have two machines, running Linux, serving same site. The site served by Kastrel (localhost) and wrapped by nginx (for the outside network). I have cloud Load Balancer which spliting the trafic between those two instances. The LB defined to split the traffic by session-affinity.
I defined SignalR to use Redis in order to work well in the multiple instances enviroment.
My startup.cs
code:
var redisCs = ConfigurationOptions.Parse("REDIS CS");
services.AddSignalR().AddRedis(options =>
{
options.ConnectionFactory = async writer =>
{
var connection = await ConnectionMultiplexer.ConnectAsync(redisCs, writer);
return connection;
};
options.Configuration.ClientName = "Main-website-Signalr";
});
When I testing it, some of the times it's working and on the other time - its not.
When it failed, I see this logs on browser console:
WebSocket connection to 'wss://mysite.com/hubs/myhub?id=R_Zmaew-lRN_r2d_c-xOyg' failed: Error during WebSocket handshake: Unexpected response code: 404
On my browser "Network" tab I see that the browser try to communicate with the address wss://mysite.com/hubs/myhub?id=R_Zmaew-lRN_r2d_c-xOyg
and get 404 response.
I worried that the nginx is blocking the connection somehow, so I decide to connect with the Kestrel without any mediator. I ssh'ed into the machine and run this command:
$ curl 127.0.0.1:5000/hubs/businesses?id=Tta4PmrjMrzHBHa8CT0SPQ -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 5000 (#0)
> GET /hubs/myhub?id=Tta4PmrjMrzHBHa8CT0SPQ HTTP/1.1
> Host: 127.0.0.1:5000
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Wed, 05 Jun 2019 06:26:09 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
<
* Curl_http_done: called premature == 0
* Connection #0 to host 127.0.0.1 left intact
No Connection with that ID
So, I got 404 error - same like I got on from the browser. I guess now that no-one is blocking the connection somehow. It just something with the SignalR configuration is broken.
Why the SignalR can find the connection?
Is that matter for which machine I logged in? the connection isn't passed via Redis somehow?
Upvotes: 4
Views: 7075
Reputation: 1933
When you have multiple server instances behind a single load-balancer you need to have sticky sessions enabled, meaning a unique client will only ever connect to a single machine.
You can see some info about the Redis backplane in https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-2.2#redis-backplane
SignalR is a protocol and part of that protocol means that a connection ID will be created on the server during "negotiate". This means you can't just randomly curl the endpoint with an ID because curl doesn't understand the SignalR protocol.
You can read more about the protocol if you're interested at https://github.com/aspnet/AspNetCore/tree/master/src/SignalR/docs/specs
Upvotes: 5