Azure SignalR and Azure Web Application with multiple instances - asp.net-core

We are developing ASP.Net core that is hosted as Azure Web Application.
We also use Azure SignalR service
Everything works great as long as we have single instance of the Web App, but once we scale it out we have the following problem:
From the Controller's action we resolve IHubContext and we send message to Hub's client. Everything works great so far
Hub's client accepts response and sends it TheHub endpoint.
The problem here is that response could be sent to another instance of Web App. So we send request from instance #1 but response is sent to instance #2 with 50% chance and instance #1 never receives response
Any ideas of how we could make it work so instance that emitted request actually received response?

SignalR has support for scaleout scenarios out of the box, it's called backplanes. The idea is that with help of one of backplane components, it will spread out SignalR events accross all instances. For Asp.Net framework, use one of these packages
Microsoft.AspNet.SignalR.ServiceBus3
Microsoft.AspNet.SignalR.ServiceBus
Microsoft.AspNet.SignalR.StackExchangeRedis
Microsoft.AspNet.SignalR.SqlServer
For ASP.Net Core only Redis is ported with Microsoft.AspNetCore.SignalR.StackExchangeRedis package, but there are some provided by community, see https://github.com/thomaslevesque/AspNetCore.SignalR.AzureServiceBus

public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ApplicationName = "app1";
}
);
}
You could specify an ApplicationName in the server SDK for different server groups.
It will help your server generate access tokens like ......?hub=app1_<your_hub> during negotiation which can help our ASRS instances differentiate connections coming from different server groups

Unfortunately I didn't find reliable solution that wouldn't require a bit clumsy workarounds
But there are 2 solutions I could offer for this scenario
#LexLi suggested a good approach to solve this problem. So you basically can make your web app a SignalR client as well and make it a member of a group. This way every instance of web app is also a client and then instance that receives response for Hub's client can pass this response to group of web app instances
You could leverage Azure Service Bus topics. So once started instance will start subscribe to listen a topic. And then once any instance receives a response from Hub's client it would place response into Service Bus Topic and then every instance will receive this response from the topic
I was hoping that there could be a better solution for such problem

Related

Does ASP.Net Core SignalR support W3C Trace Contexts or any kind of distributed tracing?

I'm working to set up distributed tracing for my application. One of the connections in the application is a WebSocket connection using SignalR. Both ends of the SignalR connection are asp.net core applications. One is a Windows service (the client) and the other runs under a web server (the server).
Communication will be sourced from both directions so I'll have TraceIds originating with requests on both sides of the connection. Does SignalR have a facility for passing this Trace information back and forth? I have searched all over and there is almost no information about SignalR supporting W3C Trace Contexts or even a custom implemented tracing system like .net core pre 2.0.
I have tried creating a new Activity when making a request on the server side but I don't think any trace information is making it to the client. I may not be looking in the right place, but when I check System.Diagnostics.Activity.CurrentActivity on the client I only get null even though on the server I had the following Activity at the time I made the call.
System.Diagnostics.Activity.Current
{System.Diagnostics.Activity}
ActivityTraceFlags: None
Baggage: {System.Collections.Generic.KeyValuePair<string, string>[0]}
Context: {System.Diagnostics.ActivityContext}
DisplayName: "Microsoft.AspNetCore.Hosting.HttpRequestIn"
Duration: {00:00:00}
Events: {System.Diagnostics.ActivityEvent[0]}
Id: "00-eb7a4125a171a6438a44ddda5a637cd0-f7c16b0bb8512443-00"
IdFormat: W3C
IsAllDataRequested: true
Kind: Internal
Links: {System.Diagnostics.ActivityLink[0]}
OperationName: "Microsoft.AspNetCore.Hosting.HttpRequestIn"
Parent: null
ParentId: null
ParentSpanId: {0000000000000000}
Recorded: false
RootId: "eb7a4125a171a6438a44ddda5a637cd0"
Source: {System.Diagnostics.ActivitySource}
SpanId: {f7c16b0bb8512443}
StartTimeUtc: {7/13/2021 11:26:13 PM}
TagObjects: {System.Collections.Generic.KeyValuePair<string, object>[0]}
Tags: {System.Collections.Generic.KeyValuePair<string, string>[0]}
TraceId: {eb7a4125a171a6438a44ddda5a637cd0}
TraceStateString: null
I hope someone out there can shed some light on this.
Thanks!
-=-=-=-= Edit -=-=-=-=
It looks like at least some level of W3C context tracing is supported by SignalR when hosted in a Blazor app. Not sure what to make of that though. https://github.com/dotnet/aspnetcore/issues/29846
Logging and tracing are really two names for the same technique.
You can use the logging system to collect diagnostic information to help solve the problem.
Logging and diagnostics in ASP.NET Core SignalR
.NET Core logging and tracing
If you want to implement distributed tracing, you can enable Application Insights for ASP.NET Core applications.
Application Insights for ASP.NET Core applications
.NET distributed tracing
After much research, not only does SignalR have little to no extensibility options for the message invocation lifecycle (the only one available appears to be HubFilters) but it also doesn't support W3C Trace Contexts at all. I've taken to implement this myself, but it's a big undertaking so I'm holding off to see if MS starts supporting this in the next 8 - 12 months. Here's hoping.
More on my musings on how this would have to be implemented manually...
HubFilters
These only offer an extensibility touch point when a message is coming from one of the connected clients. Before the method invocation requested by the client takes place you have an opportunity to inspect the message coming from the client and can take actions against all messages in a central location. There seems to be no such extensibility options for messages being sent to a client, being received by a client or being sent from a client. In order to inject code at these points you have to do it manually by wrapping all of your function invocations in another function.
Header and Querystring Support
SignalR does let you add headers and data to the query string, but since Websocket communication doesn't occur over the HTTP protocol there isn't a way to pass data specific to each message using these headers.
Conclusion
If you want to support the same functionality you get from a W3C Trace Contexts implementation in SignalR with websockets, you'll need to pass a common object (I called mine SignalRContext) in every request to and from the client or server. This object can be used to pass along, at a minimum, a TraceId to help tie all of your application tracing together.

ASP.NET API Controllers vs. SignalR Hub Methods

I'm working on a prototype which uses SignalR for broadcasting messages from the server to clients. I also communicate from clients to the server. This is possible via 2 ways: via controllers or via a method call in the SignalR Hub.
Now my question:
Why would I choose either SignalR Hub Methods or the ASP.NET API Controllers as endpoints for client commands to call? (so not for events / broadcasting messages)
And to follow up: Is it recommended to use one or both?
I read that it's not recommended to call the Hub methods from the API Controllers, because they're for the clients to call, not for the server.
Both API Controller and SignalR Hub use HTTP.
I've used:
ASP.NET Core SignalR 1.1.0
Latest .NET Core version
And following link for SignalR information:
https://learn.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-5.0
SignalR uses HTTP to establish connection with the hub (negotiation and handshake), then after connection established, it uses one of the following protocol: websockets, server sent events, long polling and forever frame. You can read more about it here.
You want to use SignalR to stream data to the client when it needs to be streamed. Most simple example would be refresh a status of something. Imagine you need to ask the server if the document was already loaded. You would call the endpoint n# of times until you get the desired answer. With the hub, the server will call the client via SignalR that the load was completed. As you can see you will save resources and drastically lower the amount of requests you make to the server.
So what you use? Hug or Controller? It really depends for the specific operation you want to do, not the general case of broadcast data.

How to properly implement SignalR in a distributed, SOA environment?

I have a good understanding SignalR Hubs in a client/server scenario, where both the client and server are tightly coupled.
Let's say I have a WCF service that receives an update from some external resource. That service could update the database with a new value. However the client would need to be notified that an update has occurred. This could be handled through a service proxy that notifies the client (sounds a bit like polling) or some cache resource.
I could create C#-based clients and connect all the nodes via SignalR hubs, but this creates a closed, non-distributed system.
A SignaR hub that attaches to a WCF service could use the .Net 4.5 could implement a WCF asynchronous service operation, where a hub client would be notified with any service data changes.
I saw something similar in Push Notifications with NServiceBus and SignaR, but not sure if this is an optimal production-level solution.
What other methods could be used in this scenario and how would they be implemented?
If you are not using push notifications directly to the client or some kind of long polling then it is pretty typical to communicate with clients on another channel altogether. Not knowing the business case, it is hard to tell what would be feasible. Usually this manifests itself in the form of SMS, push notifications to mobile, email, etc. This does not answer your question directly, but you may find that there is another way to achieve your goal.

SignalR - Sending a Message from a WCF Project

I've followed the instructions from https://github.com/SignalR/SignalR/wiki/Hubs
entitled "Broadcasting over a Hub from outside of a Hub".
I got this method working from within an MVC Action in the same project. Requesting the Action sends the update to connected clients.
My problem is that I need to be able to send updates from another project, in particular a WCF Web Services project. My app has an API and a web component and when API users make calls that change things, these updates need to be pushed out to the Web clients via SignalR. And calling a web service with the same code as my Test Action doesn't work.
I also tried the same code inside an nunit unit test that didn't work either.
What do I need to do to make this same method described on the Wiki work for a WCF Project?
The easiest solution is probably to provide an API on your Web Application (use MVC or the new WebAPI) that broadcasts to all connected clients. Any other application (an NT Service, an NUnit test, ...) can call that API if it wants to send a message to the clients.
You can't expect SignalR to do anything if you aren't hosting a Hub either in a Web Application running under IIS, or another application hosting it directly.
If you need two-way communication from your separate application to your clients then simply make your application into a SignalR client too and have it communicate via the Web Application hosted SignalR to the clients and have it listen to messages from them too.
For example, here's how I have configured a complex Service + WebSite + Clients solution (ignore the purple for now):
The Live Web Server allows NT Services to connect and create SignalR Groups. NT Services send to those groups. Web browsers connect to a group and receive messages send to that group. In effect the middle box becomes a pubsubhub.
I cannot get exactly what you aim. But if I understood correctly you're trying to send some kind of notifications raised inside WCF services to SignalR clients.
If that's the case; I can suggest you my approach:
I have some WCF services and a SignalR hub in the same application server. IMHO, the best way to communicate WCF with SignalR hub is by using MSMQ.
When a notification occurs inside a WCF service, it puts the notification payload into MSMQ.
On the other end, SignalR hub listens the same queue. When a message put into the queue, it gets the content and broadcasts to the hub clients. Very easy and straightforward. No extra service/hub call at the server side.
SignalR hub can listen for new queue items by using System.Messaging.MessageQueue#ReceiveCompleted method. When this event raised, SignalR hub gets the queue item and broadcasts to its clients.

NServiceBus using static class library

I've got a static class library which I'm using to provide services to an ASP.NET MVC3 application.
I'm trying to get my head around the best way to provide async database calls. I've got an app that sends data to a node, which passes it on to all the nodes that node knows about and so on.
I'm using NServiceBus2 to accept a node message from a web client. Control is then sent back to the web app to allow the controller to finish and hence return the page to the user.
In the background a listener picks up that message and starts the node database trawl. I've created a new class library which is the listener which works fine.
My problem is publishing. Do I have to create the Bus on every call to a method? Where can I store the bus? I suppose I could try the WCF route?
Clarifications
I don't think it's a great idea to raise messages directly from a web application - in the same way you probably wouldn't put DB code in the controller. I'd like to have a separate class library that is the 'business logic'.
There are a couple of ways to get messages on the Bus from a ASP.NET web app. Firstly you can bootstrap the Bus once in a global place(global.asax or otherwise) and provide a reference to it. We prefer to reference the Bus via some abstraction, typically a class such as ServiceAgent<T> where T is a message you will internally Bus.Send(). If you don't want to boostrap the Bus in your web app, the other option is to expose your NServiceBus endpoint as a WCF service. This is done simply by implementing a class with the WcfService<TRequest,TResponse>. From there you can simply call the exposed SOAPy service. If you don't like SOAP, you can configure the endpoint differently. From within your endpoint you can then do a Publish().
you create a bus per service. a service can be a windows service, a win-forms application or in your case a website.
in a website/webservice scenario i normally create the bus in the application_start event of the global.asax.
you can save the bus in a container for example StructureMap or Castle Windsor.
don't publish messages from your website directly instead call a wcf service that publishes a message. or use send from your website. take your time to understand the different usages of messaging with nservicebus. specifically the differences of pub/sub and send/reply scenarios when to use what is essential.
to your other text: i don't understand your scenario. if you have further problems please edit your original post to help us understand.