I just started to read a lot about service bus architectures and looked more closely into the NServiceBus framework. I am not really getting an idea on how to design multiple private contexts where a set of clients are interested in the copies of the same message (e.g. a chatroom application).
Is the service bus supposed to exist only once or would I create multiple service bus instances, one for each virtual chatroom, and subscribe a message handler instance for each interested client?
Does it have something to do with the concept of topics and subscribes which I already read about in the context of Azure?
Is the service bus supposed to exist only once or would I create multiple service bus instances, one for each virtual chatroom, and subscribe a message handler instance for each interested client?
When using NServiceBus, you will configure a bus for each endpoint that you create. You could think of an endpoint as a service which handles certain messages. Endpoints aren't dynamic, they need to specify a name so that other endpoints can be configured to send messages to or receive messages from them.
It depends on what the requirements of your system are, but I'm assuming that a virtual chatroom is something that you can generate dynamically and/or remove when it's no longer in use.
Does it have something to do with the concept of topics and subscribes which I already read about in the context of Azure?
NServiceBus and Azure Service Bus both provide the infrastructure to do pub/sub.
I am not really getting an idea on how to design multiple private contexts where a set of clients are interested in the copies of the same message
What exactly do you mean by multiple private contexts? I am understanding your question as how to establish a relationship between a client and a chatroom. The part that is missing from your description is persistence, i.e. storing the relationship between a client and a chatroom.
Using NServiceBus as an example, you could have a ClientService that handles messages regarding any actions that clients take and a ChatroomService that handles messages regarding actions that occur within Chatrooms. Then the workflow could go:
Client enters chatroom (client sends command to client service)
ClientService adds information to Client object and updates it (persistence)
ClientService sends command to ChatroomService to update the list of clients associated with the corresponding chat room. (command)
Chatroom service updates list of subscribing clients (persistence)
Another client creates a Post in the chat room. (client sends command to ClientService)
ClientService sends command to ChatroomService with new Post and ID of client who created it, and the ID of the chatroom it was sent to (command)
ChatroomService adds the Post to the list of Posts in the corresponding chatroom (persistence)
ChatroomService generates events for all other clients in the list subscribing to the corresponding chatroom (publish)
ClientService handles the events raised by ChatroomService and distributes the Post to the other subscribed clients. (subscribe)
I haven't actually created a chat room application, so this may not be the best design for your idea but it provided a means of describing how you can use NServiceBus to create your application. You can set up services with endpoints that handle messages corresponding to objects in your system. With this or similar design, you have separation of concern and can scale your services as required based on the traffic that each object type must handle.
Editing to add a more specific example based on question in comments
Note: changed all references of text message to Post so that theres no ambiguity between chat message and IMessage in NServiceBus. I've been using terms command and event on purpose. In NServiceBus, ICommand is a type of message that can only be sent to 1 endpoint, whereas IEvent is a type of message that can be published and subscribed to by many endpoints.
Apologies if the example wasn't clear. What I was trying to convey is that you can store the relationship between Clients and Chatrooms so that when a Chatroom gets a new Post, it can be distributed to only the Clients that are related to it. Imagine that you have a chatroom CR and 3 clients A, B, and C that are all in CR. Also, lets say we have the following classes:
class Chatroom
{
Guid Id { get; set; }
List<Client> Subscribers { get; set; }
List<Posts> Posts { get; set; }
}
class Client
{
Guid Id { get; set; }
List<Chatroom> Chatrooms { get; set; }
ConnectionInfo Info { get; set; }
}
class Post
{
Guid Id { get; set; }
Guid ClientId { get; set; }
Guid ChatroomId { get; set; }
string Text { get; set; }
}
class NewPostCommand : ICommand
{
Post NewPost { get; set; }
}
class NewChatroomPostEvent : IEvent
{
Post NewPost { get; set; }
Chatroom Chatroom { get; set; }
List<Client> Subscribers { get; set; }
}
A creates a Post to CR, and this is implemented using a HTTP call from the client to an API. The API is part of an NServiceBus service that serves as the ClientService. The API call creates a Post that contains the text, A.Id, and CR.Id.
ClientService sends a NewPostCommand to ChatroomService with the Post that was just created.
ChatroomService receives NewPostCommand and passes it to the appropriate handler.
Handler searches for the corresponding Chatroom (CR) in your database using ChatroomId on the Post. The new Post is added to CR.Posts and updates the database.
Handler gets the list of CR.Clients minus Post.ClientId from the CR (resulting in B and C).
Handler publishes a new NewChatroomPostEvent that contains the Post, Chatroom and Subscribers.
ClientService subscribes to NewChatroomPostEvent, receives the message and passes it to the approriate handler.
ClientService has all the functionality for communicating with Clients (using Client.Info) and sends the new Post to each client in the NewChatroomPostEvent.Subscribers list (B and C).
Now that we've walked through the message flow of a new post, you can imagine how you would handle adding and removing subscribers from each chatroom. A client would send a message to the HTTP API when it enters or leaves a chatroom, the ClientService sends a message to the ChatroomService, which would update the appropriate Chatroom.Subscribers list. Hopefully this clarifies how you control which clients are sent updates when new posts arrive.
Related
I'm taking over responsibility for an Azure Service Bus deployment. Documentation is incomplete in that there are undocumented Queues and Topic Subscriptions present.
What information can Azure provide me about the function that is consuming messages sent to a queue/topic subscription? I'd like ultimately to be able to locate the source code which is processing the messages.
e.g. a class library name, class name or even just a namespace of the function that receives the messages. Something. Anything.
Or am I asking the wrong question?
Seems like an oversight on Azure that all information about Service Bus ends at the definition of the "inbox" and offers nothing about the functions that deal with received messages.
Problem is, the whole idea of using a Service Bus is to decouple (parts of) applications, so the sender does not have a tight relationship with the receiver of the message. From your story it seems your consumers are probably Azure Functions but in other situations receiver might be running on-premises or in an other cloud environment like AWS or Google Cloud. The Azure Service Bus does simply not know what kind of hosting environment is consuming the messages. Tools like Application Insights can generate this kind of information but only if enabled on the consuming side.
What information can Azure provide me about the function that is consuming messages sent to a queue/topic subscription? I'd like ultimately to be able to locate the source code which is processing the messages.
e.g. a class library name, class name or even just a namespace of the function that receives the messages. Something. Anything.
If the producing/consuming applications have an Application Insights integration the Application Map might reveal this information.
Enabling Azure Functions to consume Service Bus messages is all based on configuration so you could scan your code repositories based topic names but there are plenty of other locations where this configuration can be stored like Azure Function Application Settings, Azure Key Vault, Azure App Configuration etc.
Even a statement "There are no consumers on this queue/subscription" would be a help.
The closest you can get is see whether there are active messages waiting to be delivered:
If there is no (active) consuming subscription while messages are pushed I would expect an increasing value of the Active Message Count metric. But do mind the Message Time to Live for the specific topic.
Or am I asking the wrong question?
It is a valid question and I definitely feel your pain but there is no satisfying answer to your question. For loosly coupled systems you need documentation, distributed tracing and possibly infrastructure as code to tackle issues like this.
Getting a list of Azure Functions and their bindings
In your comments you mention that it would help to get a list of functions and their bindings. I do know how to do that using the management SDK:
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.AppService;
internal class Program
{
private static async Task Main(string[] args)
{
var client = new ArmClient(new DefaultAzureCredential(new DefaultAzureCredentialOptions()
{
ExcludeVisualStudioCredential = true,
ExcludeVisualStudioCodeCredential = true
}));
var azureFunctions = new List<SiteFunctionResource>();
foreach (var subscription in client.GetSubscriptions())
{
var resourceGroups = subscription.GetResourceGroups();
await foreach (var resourceGroup in resourceGroups.GetAllAsync())
{
var websites = resourceGroup.GetWebSites();
azureFunctions.AddRange(
websites
.Where(ws => ws.Data.Kind.Contains("functionapp", StringComparison.InvariantCultureIgnoreCase))
.SelectMany(ws => ws.GetSiteFunctions().ToList())
);
}
}
foreach (var azureFunction in azureFunctions)
{
var bindings = azureFunction.Data.Config.ToObjectFromJson<Config>().bindings;
Console.WriteLine(azureFunction.Id);
Console.WriteLine("bindings:");
foreach (var binding in bindings)
{
Console.WriteLine($"\t{binding.name}: {binding.type}");
}
Console.WriteLine("-----------------------------------");
}
Console.ReadLine();
}
public class Binding
{
public string type { get; set; }
public string route { get; set; }
public List<string> methods { get; set; }
public string authLevel { get; set; }
public string name { get; set; }
}
public class Config
{
public List<Binding> bindings { get; set; }
}
}
It needs the following NuGet packages:
<PackageReference Include="Azure.Identity" Version="1.7.0-beta.1" />
<PackageReference Include="Azure.ResourceManager" Version="1.3.0" />
<PackageReference Include="Azure.ResourceManager.AppService" Version="1.0.0-beta.3" />
Sample output:
We are in the process of redesigning few our REST API endpoints to transition to a micro service architecture.
Here we are working on the endpoint /invitations/:id/confirm.
This endpoint creates a User, Account using the provided Invitation.
We have 3 aggregates Invitation, User and Account.
The nominal flow we are currently following is:
Check if Invitation exists
Make sure the invitation can be confirmed
Create User
Create Account
Delete Invitation
Return UserId
This operation is done in-process which explained why we can return a UserId right away. We simply load our aggregates from the db, perform the associated business logic and persist the result.
Introducing micro services will require asynchronous processing. In other words, we should send a command to a bus and return status code 202.
In our plan, we want to fire a command named RequestInvitationConfirmation. Basic validation will occur while instantiating this command.
Then this command will be sent through the bus to a consumer in charge of:
- Loading the invitation aggregates (make sure it exists)
- Calling the RequestConfirmation methods (will check that the invitation can be confirmed)
- Raising the InvitationConfirmationRequested event
The InvitationConfirmationRequested event will trigger a SAGA in charge of orchestrating the cross services communication
OnInvitationConfirmationRequested
Send CreateUser command
OnUserCreated
Send CreateAccount command
OnAccountCreated
Send DeleteInvitation command
OnInvitationDeleted
Raise InvitationConfirmed
Since it's asynchronous we need to provide a way to get the current operation state. I saw (https://www.adayinthelifeof.nl/2011/06/02/asynchronous-operations-in-rest/, https://asyncrestapi.docs.apiary.io/#) that a common approach
is to offer a /queue/:id OR /actions/:id endpoints.
This is where we get confused. How can you offer a single endpoint when states may be totally different from a SAGA to another?
Thx
For your saga to process messages within the scope of a single flow, you must correlate all your messages with the proper instance. When a saga is started by the first message, the saga identity is generated according to the rules:
Event(() => ItemAdded, x => x.CorrelateBy(cart => cart.UserName, context => context.Message.UserName)
.SelectId(context => Guid.NewGuid()));
So this id will be used as the identity of your saga that is persisted to the saga repository.
class ShoppingCart :
SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
Here, the CorrelationId is the saga id, therefore is the correlation id of the whole process.
If you have access to your saga repository (and you do), it is quite easy to expose an HTTP API endpoint to retrieve the current saga state by looking at the value of the CurrentState property in your saga state in the database that you use to persist sagas.
I want to build an Azure Function that responds to HTTP POST requests coming from another Azure function i.e. MasterFunction calling NotificationsFunction.
Say, I have the following simple POCO object:
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public int Mileage { get; set; }
}
Both functions will be sharing the same class library containing these POCO objects.
Am I right to assume that in the MasterFunction, I'll have to serialize my Car object to JSON, then make an HTTP call?
Could someone point me to some code samples on a similar scenario?
If both of your functions are in the same azure function app (which I think that's the case), I'd say the best way of calling other functions is using Queues.
In the sense that you put your POCO into a queue and define your second function with a QueueTrigger. So once an object gets into the queue, the other function gets called automatically and the object get dequeued.
You can find samples and more details in here : https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue
If I understand correctly you want service communication between services/endpoints.
You can use Orchestration or choreography for service communication.
orchestration in azure using 1. Durable function 2. Logic app
Choreography in azure using 1. Storage queue 2. Service Bus.
Hope it will help
I'm working with Azure Notification Hub, and I want to send a push notification message to all registered device in .NET backend. But I'm not sure this way will send to all devices because I don't have the way to check the number of devices received push message.
So, how to, I can send a push message to all devices or can make sure this way is correct?
public static async Task<bool> SendBroadcast(string msg)
{
try
{
var notificationHubClient = NotificationHubClient.CreateClientFromConnectionString(ConfigurationManager.AppSettings["ServiceBusPushNotificationConnectionString"], ConfigurationManager.AppSettings["ServiceBusPushNotificationName"]);
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("message", msg);
param.Add("alert", msg);
var template = new TemplateNotification(param);
var result = await notificationHubClient.SendNotificationAsync(template);
Console.WriteLine(JsonConvert.SerializeObject(result));
return true;
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
return false;
}
}
If you don't specify any tag expression, that means it's broadcast. All devices will receive the notification. You can track how many devices are received through using Per Message Telemetry. Please see below links for same.
https://msdn.microsoft.com/en-us/library/azure/mt608135.aspx
https://azure.microsoft.com/en-us/blog/push-notification-hub-telemetry-expiry-update/
You need to use tags as described in Routing and Tag Expressions:
The only way to target specific registrations is to associate them
with a tag, then target that tag. As discussed in Registration
Management, in order to receive push notifications an app has to
register a device handle on a notification hub. Once a registration is
created on a notification hub, the application backend can send push
notifications to it. The application backend can choose the
registrations to target with a specific notification in the following
ways:
Broadcast: all registrations in the notification hub receive the
notification.
Tag: all registrations that contain the specified tag receive the
notification.
Tag expression: all registrations whose set of tags match the
specified expression receive the notification.
Note, there're limitations on broadcast messages that you need to take into account.
Take a look at the Breaking News App Sample on details about how to use broadcast notifications.
I haw an aggregate in my domain that have some Guid secondary keys.I want to get more info about these keys from another domains via RESTfull.
public class ProductAggregate
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public Guid AgencyId { get; set; }
public string AgencyTitle { get; set; }
}
In above code the AgencyTitle not exists in current domain and I want to get it from another live service via Restfull and send aggregated object to the client.
Is it a acceptable way?
It sounds like you are talking about a read model that you present to a user rather than an aggregate.
There are various ways you can handle this:
Local Caching
Keep an in-memory cache locally in your service that maps between AgencyId and AgencyTitle - this can either be through:
listening to an event (i.e. AgencyCreated). This would be preferred if you are have an event-driven system. You could also listen to other events (i.e. AgencyTitleUpdated) if relevant.
by making a web request to the external services. You would query your local cache first, and then decide if to call the external service. You'd need to think about how stale you allow your cache to become.
Denormalising the data
You could duplicate the data by saving the AgencyTitle alongside the AgencyId. This way you have no call to an external service. The tradeoff is you need to consider how often the AgencyTitle is likely to change, and how you handle that change.
Reporting "domain"
You could have a completely separate service that listens for data from other services and maintains view models for UIs. This would keep your other services ignorant of other service's concerns. When using an event-driven system, you'd be listening for events form other services that allow you to build a view model for the UI