One of the great features of MassTransit is the abstraction it provides over the message transport. This allows you to forget about the plumbing and focus on distributed application design and development.
Now would be a great opportunity to take one of our past tutorials and convert it from RabbitMQ to Azure Service Bus. It will become very clear how easily you can switch from one message transport to another. The completed project for this tutorial can be found here under the azure-service-bus branch.
Tutorial
First, clone the master branch from this tutorial. Don’t worry if you don’t have RabbitMQ setup locally, because we will be switching the solution to use Azure Service Bus.
Create an Azure Service Bus
The first thing we need to do is create an AzureSB for this project. You can use a free trial, or if you have an MSDN License that works too. You’ll want to log into https://portal.azure.com. Once at the dashboard screen, follow these steps:
- Click New, and search for “azure service bus” or “service bus”. Then select it, and click “Create”.
- Within the Create window, you will want to enter settings:
Name: <this will be your namespace>
Pricing Tier: <Must be Standard or higher, MassTransit needs Topics and Queues>
Resource group: <I made a new one, but you can choose existing if you have one>
- Wait a bit while it creates/deploys the Service Bus
- Once Created, you will want to click “Shared access policies”, Choose “RootManageSharedAccessKey” and then leave this window open, because we will need to copy the primary key values into our App.config and Web.config.
Update the Project Configs
Before we update our project IoC with the Azure SB transport, we need to add the Azure Key Name and Shared Access Key for the Service Bus we made in the previous section.
- Open your App.config in the StarterKit.Service project. Add the 4 lines with Azure configuration as follows:
<appSettings> <add key="AzureSbNamespace" value="azurereqresp" /> <add key="AzureSbPath" value="TutorialReqResp" /> <add key="AzureSbKeyName" value="RootManageSharedAccessKey" /> <add key="AzureSbSharedAccessKey" value="+yourlongkeyhere=" /> <add key="RabbitMQHost" value="rabbitmq://localhost/tutorial-req-resp" /> <add key="RabbitMQUsername" value="tutorial-req-resp" /> <add key="RabbitMQPassword" value="apple" /> <add key="ServiceQueueName" value="order_status_check" /> </appSettings>
Line 2: This must match the namespace you used when creating the Azure Service Bus
Line 3: This can be a path of your choosing, similar to RabbitMQ’s virtual host. A logical separation for different topics and queues within the same namespace
Line 4: Leave this as is
Line 5: Paste in your shared access key (continue reading to see how you obtain the Shared Access Key) - Open your Service Bus in Azure Portal. Then click Settings -> Shared access policies -> Root manage shared access key. You can copy the Primary key (or secondary, it doesn’t matter) and paste it into your App.config.
- Now open Web.config in StarterKit.Web and add the same 4 lines, plus a new 5th line.
<appSettings> <add key="AzureSbNamespace" value="azurereqresp" /> <add key="AzureSbPath" value="TutorialReqResp" /> <add key="AzureSbKeyName" value="RootManageSharedAccessKey" /> <add key="AzureSbSharedAccessKey" value="+yourlongkeyhere=" /> <add key="AzureQueueFullUri" value="sb://azurereqresp.servicebus.windows.net/tutorialreqresp/order_status_check/" /> <add key="RabbitMQHost" value="rabbitmq://localhost/tutorial-req-resp" /> <add key="RabbitMQUsername" value="tutorial-req-resp" /> <add key="RabbitMQPassword" value="apple" /> <add key="ServiceFullUri" value="rabbitmq://localhost/tutorial-req-resp/order_status_check" /> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="owin:AppStartup" value="StarterKit.Web.Bootstrapper.Startup, StarterKit.Web.Bootstrapper" /> </appSettings>
Line 6: This should look familiar if you followed the original req-resp tutorial here. Because the RequestClient needs to connect to a specific endpoint (queue), we need to provide the full Uri that we can access the queue in Azure. The uri of queues can be determined fairly easily, here’s a quick explanation: sb://<namespace>.servicebus.windows.net/<path>/<queue_name>
Add MassTransit Azure Packages and Register with our Container
Great. Now we have our configuration settings saved and ready to use. Now all we have to do it change the container we register in our IoC of both the StarterKit.Service and StarterKit.Web projects. We don’t need to change a single line of business logic code in our app. Thank you MassTransit!
First, you will need to install the nuget package MassTransit.AzureServiceBus in both the StarterKit.Service and StarterKit.Web.Bootstrapper.
StarterKit.Service Changes
Once complete, create a new file in StarterKit.Service/Modules named AzureServiceBusModule.cs.
Paste the following in the file.
public class AzureServiceBusModule : Module { private readonly System.Reflection.Assembly[] _assembliesToScan; public AzureServiceBusModule(params System.Reflection.Assembly[] assembliesToScan) { _assembliesToScan = assembliesToScan; } protected override void Load(ContainerBuilder builder) { // Creates our bus from the factory and registers it as a singleton against two interfaces builder.Register(c => Bus.Factory.CreateUsingAzureServiceBus(sbc => { var serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", ConfigurationManager.AppSettings["AzureSbNamespace"], ConfigurationManager.AppSettings["AzureSbPath"]); var host = sbc.Host(serviceUri, h => { h.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(ConfigurationManager.AppSettings["AzureSbKeyName"], ConfigurationManager.AppSettings["AzureSbSharedAccessKey"], TimeSpan.FromDays(1), TokenScope.Namespace); }); sbc.ReceiveEndpoint(host, ConfigurationManager.AppSettings["ServiceQueueName"], e => { // Configure your consumer(s) e.Consumer<CheckOrderStatusConsumer>(); e.DefaultMessageTimeToLive = TimeSpan.FromMinutes(1); e.EnableDeadLetteringOnMessageExpiration = false; }); })) .SingleInstance() .As<IBusControl>() .As<IBus>(); } }
Line 15: This helper method from Microsoft.ServiceBus namespace lets us construct our service Uri. You will notice that this uri is the same as our queue in the previous section, sans /<queue_name>.
Line 19: This also uses a helper method to create the Token Provider for us, providing all the proper pieces of information.
Last but not least, head over to IocConfig.cs and change the line:
// Old Registration //builder.RegisterModule(new BusModule(Assembly.GetExecutingAssembly())); // New Registration builder.RegisterModule(new AzureServiceBusModule(Assembly.GetExecutingAssembly()));
Done!
StarterKit.Web.Bootstrapper
Almost there. I guess you can probably imagine we now need to create a new file at StarterKit.Web.Bootstrapper/Modules/AzureServiceBusModule.cs. Paste in the following:
public class AzureServiceBusModule : Module { protected override void Load(ContainerBuilder builder) { // Creates our bus from the factory and registers it as a singleton against two interfaces builder.Register(c => Bus.Factory.CreateUsingAzureServiceBus(sbc => { var serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", ConfigurationManager.AppSettings["AzureSbNamespace"], ConfigurationManager.AppSettings["AzureSbPath"]); var host = sbc.Host(serviceUri, h => { h.TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(ConfigurationManager.AppSettings["AzureSbKeyName"], ConfigurationManager.AppSettings["AzureSbSharedAccessKey"], TimeSpan.FromDays(1), TokenScope.Namespace); }); })) .SingleInstance() .As<IBusControl>() .As<IBus>(); } }
Again, same as previous, but we obviously don’t need to create the endpoint here. Instead our RequestClient will connect directly to the queue to read.
So… open up RequestClientModule.cs, and change this line:
// Old queue uri //Uri address = new Uri(ConfigurationManager.AppSettings["ServiceFullUri"]); // New queue uri Uri address = new Uri(ConfigurationManager.AppSettings["AzureQueueFullUri"]);
And if you haven’t guessed it, last but not least, go to IocConfig.cs, and change:
// Old registration //builder.RegisterModule<BusModule>(); // New registration builder.RegisterModule<AzureServiceBusModule>();
All done. We didn’t touch any of our core code, we just setup the bus, wired it into our IoC and MassTransit handles the rest!