There are many ways to organize communication between applications, services and devices. In most cases it is a custom ad-hoc solution designed for specific needs of connected parts and is often implemented anew. At Devbridge we’ve actually done quite a number of these implementations.
Currently our primary application deployment platform is Azure Cloud and we've become interested in one of the recently introduced services Azure Service Bus for exactly the reason of standardizing communication between our applications within the Azure Cloud environment and also for Client-service scenarios.
AzureMessaging Component
In short - Azure Service Bus allows us to connect any applications or devices both within and outside of Azure Cloud. Our research for this service capability is concentrated in new component called AzureMessaging which is being implemented as part of our Devbridge Sourcery initiative.
AzureMessaging is created as a wrapper over Azure Service Bus messaging which supports spawning any number of background threads for each message queue. So, if processing a message is an IO intensive operation you can double the throughput by simply assigning two or more worker threads. The component supports automatic Retries on messages generating errors with Failed messages sent to a Dead Letter Queue when its Retry threshold is reached.
Installation
AzureMessaging is provided as a Nuget package. To install the package run the following command in the Visual Studio Package Manager Console:
PM> Install-Package AzureMessaging
Configuration
Set Azure Service Bus connection string in Web.config/App.config file:
<connectionStrings>
<add name="ServiceBusConnectionString" connectionString="SERVICE\_BUS\_CONNECTION\_STRING" />
</connectionStrings>
Messages
The client with the server communicates by sending messages. Examples include:
public class GreetSampleMessage
{
public string Text { get; set; }
}
public class GreetWorldSampleMessage
{
public string Name { get; set; }
public string Description { get; set; }
}
Note: It is important to have unique class names because a queue name is constructed from a type name.
Server Usage
Usage code:
var queueClientFactory = new QueueClientFactory(SERVICE\_BUS\_CONNECTION\_STRING\_NAME);
var server = new AzureMessageService(queueClientFactory);
//Server settings
var settings = new MessageHandlerSettings
{
NoOfThreads = 2,
NoOfRetries = 3,
IntervalBetweenRetries = TimeSpan.FromSeconds(10),
DuplicateIntervalWithEachRetry = true
};
//Register handlers to handle messages from clients
server.RegisterHandler<GreetSampleMessage>(x =>
{
var greetSampleMessage = x.GetBody();
...
return null;
}, settings);
server.RegisterHandler<GreetWorldSampleMessage>(x =>
{
var greetWorldSampleMessage = x.GetBody();
...
return null;
}, settings);
//Start messages handling
server.Start();
...
//Stop messages handling
server.Stop();
//Free resources
server.Dispose();
MessageHandler Settings:
NoOfThreads - number of background threads to handle each message queue
NoOfRetries - number of retries if message handling fails. If the number of retries is exceeded then the message is moved to the Dead Letter Queue. If the number of retries is not set then, in case of handling failure, the message will not be moved to Dead Letter Queue.
IntervalBetweenRetries - interval between retries. Note: actual interval may be different if client and Azure Service Bus times are not synchronized.
DuplicateIntervalWithEachRetry - indicates if interval should be duplicated between retries. For example IntervalBetweenRetries = TimeSpan.FromSeconds(10) and DuplicateIntervalWithEachRetry = true, so first retry will occur after 10 seconds, second - after 20 seconds, etc.
Get messages from Dead Letter Queue:
var deadMessages = AzureMessageService.GetDeadLetteredMessages<GreetSampleMessage>(SERVICE\_BUS\_CONNECTION\_STRING\_NAME, messagesCount: 10, deleteAfterReceiving: true);
Note: Currently handler return result is not processed. In the future we plan to place returned result to queue if result is a class (not null or primitive type).
Client Usage
Usage code:
//Create client
var queueClientFactory = new QueueClientFactory(SERVICE_BUS_CONNECTION_STRING_NAME);
var clientFactory = new AzureMessageClientFactory(queueClientFactory);
var client = clientFactory.CreateMessageQueueClient();
//Send messages
client.Publish(new GreetSampleMessage { Text = "Hello" });
client.Publish(new GreetSampleMessage { Text = "Hello2" });
client.Publish(new GreetWorldSampleMessage { Name = "Greet", Description = "Hello world" });
Additional Notes
When registering handlers, if the database repository is used, you need to create a LifeTime scope so that the new NHibernate session is created and properly disposed for each message.
server.RegisterHandler<QbCreateOrUpdateProduct>(message =>
{
var body = message.GetBody();
var productId = body.ProductId;
using (var scope = BeginLifetimScope())
{
scope.Resolve<IProductService>().SaveProduct(productId);
}
return null;
}, settings);
Easily Testable
There is also an InMemoryQueueClientFactory available, useful for development & testing.
var queueClientFactory = new InMemoryQueueClientFactory();
var server = new AzureMessageService(queueClientFactory);
This will process messages without publishing into Azure Message Queue. It is useful when debugging your own published messages.
Examples
For more examples look in projects:
Devbridge.AzureMessaging.Sample.Server
Devbridge.AzureMessaging.Sample.Client
Devbridge.AzureMessaging.Tests
To run examples and tests you need to set Azure Service Bus connection string in App.config file:
<connectionStrings>
<add name="ServiceBusConnectionString" connectionString="SERVICE\_BUS\_CONNECTION\_STRING" />
</connectionStrings>
License
AzureMessaging is freely distributable under the terms of an Apache V2 license.
Source Code
Source code and component documentation can be found here: https://github.com/devbridge/AzureMessaging
Conclusion
AzureMessaging is still in active development and may evolve a lot in the future. However, in it’s current state it is an essential component for ServiceBridge, allowing communication between the server and client devices. Additionally, we are planning to extend its usage to our other applications moving forward. Stop by our open-source site, Sourcery, and give it a try.