Azure SignalR Service Upstream to Multiple Functions / Hubs - c#

I have the following services implemented on Azure:
1x Azure SignalR Service (Serverless) ASRS
2x Azure Functions (Serverless) HubFuncDown & HubFuncUp
On the ASRS I have defined TWO UpStream URLs, one to HubFuncDown Azure Function and the other to HubFuncUp. Using the URL pattern defined in the docs:
HubFuncDown Contains the following method which instructs the device app to disable itself:
[FunctionName(nameof(DisableDevice))]
public async Task DisableDevice([SignalRTrigger] InvocationContext invocationContext, string deviceId, ILogger logger) {
await Clients.User(deviceId).SendAsync(DisableDeviceTarget, new NewMessage(invocationContext, deviceId));
}
And HubFuncUp Contains the following method:
[FunctionName(nameof(DeviceDisabled))]
public async Task DeviceDisabled([SignalRTrigger] InvocationContext invocationContext, string deviceId, ILogger logger) {
// .. Updates DBContext, sends alerts etc
}
I'm trying to tell HubFuncUp that this client is now in a disabled state, the code to do that on the (UWP) client is:
var connection = new HubConnectionBuilder()
.WithUrl("https://hubfuncup.azurewebsites.net/api", options => {
options.AccessTokenProvider = () => Auth();
})
.ConfigureLogging(logging => {
logging.AddProvider(new SerilogLoggerProvider());
logging.SetMinimumLevel(LogLevel.Debug);
})
.WithAutomaticReconnect(new RetryPolicy())
.Build();
And to tell HubFuncUp of the state:
await connection.InvokeAsync("UpdateDeviceState", new DisabledDeviceMessage { DeviceId = 123, State = States.Disabled });
But each time I'm receiving a 404 Error from the call to InvokeAsync("UpdateDeviceState"..). It seems no matter what I do I cannot connect the one ASRS to two Azure Functions using multiple Upstream URLs.
Am I correct in thinking I need to utilise a seperate Azure SignalR Service (which doubles my cost) or can I connect the two Functions to the one SignalR Service via routing on the Upstream URLs?

Related

How to map a SignalR hub to a path with an argument

I'm building a backend with Asp.Net Core & SignalR. The data are not themself located on the Asp.Net Core, but must be requested to some other devices, that will send the data to the Asp.Net Core server, and then the server must give them back to the client through the web socket.
I would like to react to the OnConnectedAsync to request my devices to send periodically some data to Asp.Net Core.
But this means that my hub must be able to know which data I need to be querying.
So I would like to have a Hub responding to the url /api/data/{id} and in my Hub, be able to know which ID has been requested.
app.MapHub<NotificationsHub>("/api/data/{id}");
The id identify a big group of data(that is requested and forwared as bulk), and multiple clients will request the same ID.
I can't find in the doc:
If this is possible
How should I specify the ID parameter?
How do I retrieve this ID in the hub?
If anybody has some pointer, it would be helpful, thank you very much
You can pass parameters as query string parameters. Pass the id from the connection url:
_hubConnection = new HubConnectionBuilder()
.WithUrl("/api/data?id=123")
.Build();
Then read it inside hub:
public override async Task OnConnectedAsync()
{
string id = Context.GetHttpContext().Request.Query["id"];
var connectionId = Context.ConnectionId;
await Groups.AddToGroupAsync(connectionId, id);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
string id = Context.GetHttpContext().Request.Query["id"];
var connectionId = Context.ConnectionId
await Groups.RemoveFromGroupAsync(connectionId, id);
await base.OnDisconnectedAsync(exception);
}
Send data:
_hubContext.Group("123").SomeMethod(dataBatch);
Map hub:
app.MapHub<NotificationsHub>("/api/data")

How to subscribe Web API on Azure Messages

I have 2 Web APIs developed on ASP.NET Core. The idea is: the WebAPI_1 sends a message to the Azure Service Bus and then WebAPI_2 has to catch this moment and read it shortly after the message is sent. I understand how to catch this moment is I have a console app instead of WebAPI_2, but I am not sure how to subscribe WebAPI_2 on such the event happening in Azure Service Bus.
Below is the code where I have WebAPI_1 and the Console App.
WebAPI_1 (Sender):
public class QueueService : IQueueService
{
private readonly IConfiguration _config;
public QueueService(IConfiguration config)
{
_config = config;
}
public async Task SendMessageAsync<T>(T serviceBusMessage, string queueName)
{
var queueClient = new QueueClient(_config.GetConnectionString("AzureServiceBus"), queueName);
string messageBody = JsonSerializer.Serialize(serviceBusMessage);
var message = new Message(Encoding.UTF8.GetBytes(messageBody));
await queueClient.SendAsync(message);
}
}
And this is how I send it:
await queue.SendMessageAsync(obj, "myqueue");
And this is the Console App (Receiver):
Main(){
queueClient = new QueueClient(connectionString, queueName);
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
queueClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
private static async Task ProcessMessagesAsync(Message message, CancellationToken token)
{
var jsonString = Encoding.UTF8.GetString(message.Body);
Model obj = JsonSerializer.Deserialize<Model>(jsonString);
Console.WriteLine($"Person Received: { obj.Field1} { obj.Field2}");
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
But I want WebAPI_2 to be able to receive the messages instead of the Console App.
Please advise.
Receiving messages requires a continuous job. ASP.NET Core Controller, as you've probably found out, is not the right place as it's not running continuously and is intended to respond to the request. For a continuous execution, a background service, or task, is the right option. ASP.NET Core has an option to run a BackgroundService that could be used for exactly what you need.
There are multiple blog posts with the details in case you want to get some inspiration:
Getting Started With Azure Service Bus Queues And ASP.NET Core Background Services
Using An ASP.NET Core IHostedService To Run Azure Service Bus Subscriptions and Consumers

Send Azure SignalR message from Azure Function with CosmosDB Trigger

I’m developing a app that used CosmosDB to store data and then when anyone updates the data i want the clients to be updated.
For this i have decided to use the changefeed and then Azure Functions and Azure SignalR.
I have set up 2 functions.
A negotiate function (This one works and the clients connect correctly to the SignalR server)
And a OnDocumentsChanged function, and my problem is getting the function to actually sending the message, when something is changed.
I have the following function:
[FunctionName("OnDocumentsChanged")]
public static async Task Run(
[CosmosDBTrigger(
databaseName: "NewOrder",
collectionName: "NewOrder",
CreateLeaseCollectionIfNotExists = true,
ConnectionStringSetting = "myserver_DOCUMENTDB",
LeaseCollectionName = "leases")]
IReadOnlyList<Document> updatedNewOrder,
[SignalR(ConnectionStringSetting = "AzureSignalRConnectionString", HubName = "NewOrder")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
if (updatedNewOrder != null && updatedNewOrder.Count > 0)
{
foreach (var Orders in updatedNewOrder)
{
await signalRMessages.AddAsync(new SignalRMessage
{
Target = "NewOrderUpdated",
Arguments = new[] { Orders.Id }
});
}
}
}
I can see that it is correctly triggered when a change is made to the database, but no messages are send.
I guess I’m missing a out part that actually send the SignalRMessages I’m just not sure how to implement.
Thanks.

Azure SignalR Blazor app not receiving messages

I'm looking at incorporating Azure SignalR functionality into my .net core Blazor web application. To this end i've been following this tutorial - Azure Signalr Serverless. This is working fine - i have a project running the Azure functions app and can start up two browsers and have a chat session. What i'm trying to do is add the ability to receive these message notifications from the Azure signalR hub that's been configured into my Blazor app. I've added the following code in Index.razor.cs that mimics the javascript code in the example client:
public class IndexComponent : ComponentBase
{
private HubConnection _connection;
public string Message;
protected override Task OnInitializedAsync()
{
_connection = new HubConnectionBuilder()
.WithUrl("http://localhost:7071/api")
.Build();
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Message = $"Got message {message} from user {user}";
this.StateHasChanged();
});
_connection.StartAsync();
return base.OnInitializedAsync();
}
}
The example javascript code btw is:
const connection = new signalR.HubConnectionBuilder()
.withUrl(`${apiBaseUrl}/api`)
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on('newMessage', newMessage);
connection.onclose(() => console.log('disconnected'));
console.log('connecting...');
connection.start()
.then(() => data.ready = true)
.catch(console.error);
So the problem is that my Blazor app never receives any message notifications sent from the javascript chat clients (so the _connection.On handler is never hit). What am i missing in my Blazor code ?
Ok so this is what i needed to do to get it to work in my Blazor app:
_connection.On<object>("newMessage", update =>
{
Console.WriteLine(update);
//Message = update;
});
I needed to subscribe to the 'newMessage' target (since that's the JS is sending on) and also the type that's being posted isn't a string but a JObject type which i would need to deserialize to the correct type.

Exchanging NServiceBus messages with native implementation of Azure Service Bus

Consuming a message published with NServiceBus, using IQueueClient/Functions-ServiceBusTrigger (Microsoft.Azure.ServiceBus)
I'm working in a WebJob using .NET Core and Microsoft.Azure.ServiceBus to consume a message that has been published by a separate service using NServiceBus. My initial approach with this WebJob was to use a class Functions.cs with a method ProcessMessage that uses the attribute ServiceBusTrigger
Below is an example of how my Function.cs looks like:
public class Functions
{
public Task ProcessAuditLogMessage([ServiceBusTrigger("MyQueue")]
Message message)
{
var messageBody = Encoding.UTF8.GetString(message.Body);
var auditLogMessage = JsonConvert
.DeserializeObject<MyMessage>(messageBody);
_logger.Information("Hello World");
return Task.CompletedTask;
}
}
In Program.cs, I have:
class Program
{
static async Task Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddWebJobs(o => o.AllowPartialHostStartup = true);
var builder = new HostBuilder()
.UseServiceProviderFactory(
new AutofacServiceProviderFactory(serviceCollection))
.ConfigureContainer<ContainerBuilder>(b =>
{
BuildAutofacContainer();
})
.ConfigureWebJobs(b =>
{
b.AddServiceBus(o =>
{
o.ConnectionString = configProvider.AuditLogServiceBus;
});
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
private static IContainer BuildAutofacContainer(IServiceColletion
serviceCollection)
{
...
}
}
I was expecting that this logic would consume the messages published in the Queue but so far the messages get moved to the Dead-letter count with a DeadLetterReason: MaxDeliveryCountExceeded and the error description Message could not be consumed after 10 delivery attempts which gives me the indication that at least there is an attempt to get these messages delivered to my consumer.
Unfortunately that's all I have in terms of error messages/logs (I'm in the process to set up some logs from my Azure Dashboard and see if I can get more detailed logs)
Has anyone come across the scenario o having to consume messages, that have been published with NServiceBus, using Microsoft.Azure.ServiceBus instead of NServiceBus (on the consumer side). Maybe I'm missing something...

Categories

Resources