tanzu.vmware.com
Open in
urlscan Pro
2a02:26f0:6c00:1b1::2ef
Public Scan
Submitted URL: https://info.pivotal.io/NjI1LUlVSi0wMDkAAAGLUO4PFvX-ci9QelnOh1ltyPAsBWrAIbK8DL9TIz4Yf8Vmul8mUP0aIsyxiGOKjVisdkCPtok=
Effective URL: https://tanzu.vmware.com/developer/blog/building-topic-exchange-rabbitmq-dotnet6/?&utm_campaign=nl_developer_monthly_2023...
Submission: On April 25 via api from CH — Scanned from DE
Effective URL: https://tanzu.vmware.com/developer/blog/building-topic-exchange-rabbitmq-dotnet6/?&utm_campaign=nl_developer_monthly_2023...
Submission: On April 25 via api from CH — Scanned from DE
Form analysis
3 forms found in the DOMGET /developer/search
<form id="searchheaderform" action="/developer/search" accept-charset="UTF-8" method="get"><input name="utf8" type="hidden" value="✓">
<input type="text" name="q" id="searchheaderinput" autocomplete="off" placeholder="Search for guides, videos, code samples, posts, and more...">
<input type="submit" value="" id="searchheadersubmit" data-disable-with="">
</form>
<form id="mktoForm_1609" class="hidden float-lg-right mktoForm mktoHasWidth mktoLayoutLeft" novalidate="novalidate" style="font-family: Helvetica, Arial, sans-serif; font-size: 13px; color: rgb(51, 51, 51); width: 296px;">
<style type="text/css">
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton {
color: #fff;
border: 1px solid #75ae4c;
padding: 0.4em 1em;
font-size: 1em;
background-color: #99c47c;
background-image: -webkit-gradient(linear, left top, left bottom, from(#99c47c), to(#75ae4c));
background-image: -webkit-linear-gradient(top, #99c47c, #75ae4c);
background-image: -moz-linear-gradient(top, #99c47c, #75ae4c);
background-image: linear-gradient(to bottom, #99c47c, #75ae4c);
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:hover {
border: 1px solid #447f19;
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:focus {
outline: none;
border: 1px solid #447f19;
}
.mktoForm .mktoButtonWrap.mktoSimple .mktoButton:active {
background-color: #75ae4c;
background-image: -webkit-gradient(linear, left top, left bottom, from(#75ae4c), to(#99c47c));
background-image: -webkit-linear-gradient(top, #75ae4c, #99c47c);
background-image: -moz-linear-gradient(top, #75ae4c, #99c47c);
background-image: linear-gradient(to bottom, #75ae4c, #99c47c);
}
.mktoForm .mktoButtonWrap.mktoSimple button.mktoButton {
background: #026d63;
}
</style>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap mktoRequiredField"><label for="Email" id="LblEmail" class="mktoLabel mktoHasWidth" style="width: 0px;">
<div class="mktoAsterix">*</div>
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div><input id="Email" name="Email" placeholder="Email Address" maxlength="255" aria-labelledby="LblEmail InstructEmail" type="email"
class="mktoField mktoEmailField mktoHasWidth mktoRequired" aria-required="true" style="width: 130px;"><span id="InstructEmail" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap"><label for="Function__c" id="LblFunction__c" class="mktoLabel mktoHasWidth" style="width: 0px;">
<div class="mktoAsterix">*</div>
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div><select id="Function__c" name="Function__c" aria-labelledby="LblFunction__c InstructFunction__c" class="mktoField mktoHasWidth" style="width: 172px;">
<option value="">Select a Job Function</option>
<option value="Developer">Developer</option>
<option value="C-Level/Executive">Executive</option>
<option value="Director/Manager">Business Manager</option>
<option value="Architect/Team Lead">Architect/Technical Lead</option>
<option value="Analyst/Data Scientist">Analyst</option>
<option value="Operations">Operations</option>
</select><span id="InstructFunction__c" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow">
<div class="mktoFieldDescriptor mktoFormCol" style="margin-bottom: 10px;">
<div class="mktoOffset" style="width: 10px;"></div>
<div class="mktoFieldWrap mktoRequiredField"><label for="Email_Consent__c" id="LblEmail_Consent__c" class="mktoLabel mktoHasWidth" style="width: 0px;">
<div class="mktoAsterix">*</div>
</label>
<div class="mktoGutter mktoHasWidth" style="width: 10px;"></div>
<div class="mktoLogicalField mktoCheckboxList mktoHasWidth mktoRequired" style="width: 295px;"><input name="Email_Consent__c" id="mktoCheckbox_121743_0" type="checkbox" value="true" aria-required="true"
aria-labelledby="LblEmail_Consent__c LblmktoCheckbox_121743_0 InstructEmail_Consent__c" class="mktoField"><label for="mktoCheckbox_121743_0" id="LblmktoCheckbox_121743_0">Yes, I would like to receive communications from VMware and/or its
affiliates regarding products and services, newsletters, and events. </label></div><span id="InstructEmail_Consent__c" tabindex="-1" class="mktoInstruction"></span>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="Original_Lead_Source__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="Website" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="Original_Lead_Source_Description__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="Global_Website_Newletter_Subscription" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="LeadSource" class="mktoField mktoFieldDescriptor mktoFormCol" value="Website" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="Most_Recent_Lead_Source_Description__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="Global_Website_Newsletter_Subscription" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="Opt_In_News__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="TRUE" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="utm_campaign__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="nl_developer_monthly_2023" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="utm_content__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="rabbitmq_dotnet6" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="utm_medium__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="email" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="utm_source__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="newsletter-email" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoFormRow"><input type="hidden" name="utm_term__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="" style="margin-bottom: 10px;">
<div class="mktoClear"></div>
</div>
<div class="mktoButtonRow"><span class="mktoButtonWrap mktoSimple" style="margin-left: 120px;"><button type="submit" class="mktoButton">Subscribe</button></span></div><input type="hidden" name="formid" class="mktoField mktoFieldDescriptor"
value="1609"><input type="hidden" name="munchkinId" class="mktoField mktoFieldDescriptor" value="625-IUJ-009">
</form>
<form class="hidden float-lg-right mktoForm mktoHasWidth mktoLayoutLeft" novalidate="novalidate"
style="font-family: Helvetica, Arial, sans-serif; font-size: 13px; color: rgb(51, 51, 51); visibility: hidden; position: absolute; top: -500px; left: -1000px; width: 1600px;"></form>
Text Content
Skip to main content * Learn * Tanzu.TV * Community * Search * * * By Category Modern Applications Modern App Basics Modernizing Legacy Applications Agile Transformation and Practices Application Development Building Modern Applications Deploying Modern Applications Managing and Operating Applications Kubernetes Platform Building Kubernetes Runtime Managing and Operating Kubernetes Securing Kubernetes By Type Guides Learning Paths Workshops Samples Videos Blog Tanzu Practices Data By Language .NET Java * All Shows The Golden Path to SpringOne Enlightning Spring Office Hours Tanzu Talk Between Chair and Keyboard Code .NET in the Wild .NET Beyond Tanzu Tuesdays Cloud Native Crew * Overview Advocates Team * Learn * By Category * Modern Applications * Modern App Basics * Modernizing Legacy Applications * Agile Transformation and Practices * Application Development * Building Modern Applications * Deploying Modern Applications * Managing and Operating Applications * Kubernetes Platform * Building Kubernetes Runtime * Managing and Operating Kubernetes * Securing Kubernetes By Type * Guides * Learning Paths * Workshops * Samples * Videos * Blog * Tanzu Practices * Data By Language * .NET * Java * Tanzu.TV * All Shows * The Golden Path to SpringOne * Enlightning * Spring Office Hours * Tanzu Talk * Between Chair and Keyboard * Code * .NET in the Wild * .NET Beyond * Tanzu Tuesdays * Cloud Native Crew * Community * Overview * Advocates * Team * Search Live is streaming live. Watch now. 1. Blog 2. Building a Topic Exchange with RabbitMQ and .NET 6 BUILDING A TOPIC EXCHANGE WITH RABBITMQ AND .NET 6 RabbitMQ is a message broker accepts messages from a producer (the message sender) and holds it in a queue so that a consumer (the message receiver) can retrieve it. In this blog, I'll show you how to build a topic exchange on RabbitMQ with .NET 6 LAYLA PORTER AUGUST 3, 2022 WHAT IS RABBITMQ? At a high level, RabbitMQ is an open source message broker. A message broker accepts messages from a producer (the message sender) and holds it in a queue so that a consumer (the message receiver) can retrieve it. This allows for multiple producers and consumers to share the same queue without having to directly pass messages between each other. What RabbitMQ excels at is doing this at scale whilst staying lightweight and easy to deploy. To get started with a basic RabbitMQ implementation, checkout this guide. WHY USE A MESSAGE BROKER? The first question you may have is “why do I want to add in additional complexity?” If you were not using a message broker, you would most likely be using an HTTP or socket-based form of communication. These methods can be difficult to scale and are not always robust. HTTP communication can also tightly couple two systems together - increasing inter-dependency, which is undesirable. The addition of a message broker improves the fault tolerance and resiliency of the systems in which they are employed. They are easy to scale as their publish/subscribe pattern means the addition of many more services can be easily supported - without having to modify existing systems. WHAT IS A TOPIC EXCHANGE? RabbitMQ has four types of exchanges (or message routers) available to route the message in different ways. You will focus specifically on a topic exchange. Topic exchanges route messages to one or many queues based on matching a message routing key and the pattern that was used to bind a queue to an exchange. The topic exchange type is often used to implement various publish/subscribe pattern variations. Topic exchanges are commonly used for the multicast routing of messages. If you would like to learn more about the three other types of exchange available in RabbitMQ, then checkout the documentation. BEFORE YOU BEGIN Before you begin, there are a few tools you will need: * Docker - you’ll run your RabbitMQ server in a container for ease of setup. If you’d prefer to run RabbitMQ natively, checkout the documentation on RabbitMQ’s website. * Text editor or IDE of choice. * .NET 6 or newer * A clone or download of this repo USING RABBITMQ For this example, you’ll need a simple, single-node RabbitMQ server, so you won’t need to change any of the default settings. You can run the container image with the following: docker run -it --rm -p 5672:5672 -p 15672:15672 rabbitmq:3-management The above will run the 3-management tag of the RabbitMQ container image. The 3-management tag of this image ships with the management plug-in, which will provide an HTTP API and a web-based UI for your RabbitMQ server. You’ll tell Docker to run this with an interactive shell with the -it option, as well as tell it to automatically remove the container when it exits with the -rm option. You’ll also forward a couple of ports (5672 for the RabbitMQ server and 15672 for the HTTP API and web UI) from your local machine to the container, which means that you can connect to it on localhost. To access the web UI, navigate to http://localhost:15672. The username and password are both guest. The UI offers features such as add and remove users, as well as being able to view diagnostic information from the server. THE WAFFLE ORDERING SYSTEM To demonstrate the topic exchange you will use this simple flow: 1. An order will come into the OrderService via an API call 2. A message will be created with the routing key order.cookwaffle and passed to RabbitMQ 3. The KitchenService will listen for any messages with a routing key that contains cookwaffle 4. The KitchenService will then create a new Order object and pass it to the SignalR hub to be displayed on the UI The bare bones of this project have been created for you in the following repo on GitHub. This is the start branch. If you would like to see the completed code, checkout the complete branch. This repo consists of three discrete projects - detailed below. Each project already has the RabbitMQ nuget package, RabbitMQ.Client installed ready to use. ORDERSERVICE A webAPI application which, when its API endpoint is called, will create a new order. You will need to add a message to the topic exchange with the routing key order.cookwaffle. This project is the “Producer”. KITCHENSERVICE A Blazor Server application with SignalR. SignalR will be used to update the kitchen UI browser window as orders come in. You will need to add a listener to this application to listen for messages with a specific routing key pattern matching #.cookwaffle, to publish an order to the kitchen UI. This is a wildcard routing key as it contains the #. This service will look for any messages relating to cookwaffle regardless of suffixes. This project is the “Consumer”. MESSAGING.COMMON A class library where you will store shared code to do with RabbitMQ setup. This project has a model called RabbitMQSettings consisting of three fields: HostName, ExchangeName and ExchangeType which will be used to define these properties in RabbitMQ. It also has a class called IServiceCollectionExtensions which you will use in your other two projects to set up RabbitMQ client connections. CONNECTING TO RABBITMQ Open up the solution in your IDE of choice. You will start with the Messaging.Common project as this will be used by the other two projects. As previously mentioned, this project contains a file called IServiceCollectionExtensions which you will use to set a connection to RabbitMQ. First you will need to get the connection settings for the RabbitMQ broker from appsettings.json and bind them to a C# object. You can then add these settings as a singleton in the IoC container IServiceCollection. public static IServiceCollection SetUpRabbitMq(this IServiceCollection services, IConfiguration config) { var configSection = config.GetSection("RabbitMQSettings"); var settings = new RabbitMQSettings(); configSection.Bind(settings); // add the settings for later use by other classes via injection services.AddSingleton<RabbitMQSettings>(settings); } Next, you will need to create an instance of ConnectionFactory and add it to the IoC container, using the HostName from the RabbitMQSettings object. As you’ll be using an async consumer later on in this demo, you will also need to tell the ConnectionFactory this by setting DispatchConsumersAsync to `true. services.AddSingleton<IConnectionFactory>(sp => new ConnectionFactory { HostName = settings.HostName, DispatchConsumersAsync = true; }); To ensure your connections are disposed of correctly by the IoC container, you will need to create a ModelFactory that implements the IDisposable interface. The outline for this class is already created for you in the IServiceCollectionExtensions file. For more information on correct disposal of dependencies added to the IServiceCollection please refer to this excellent blog series by Steve Collins. First, create a constructor that accepts both the RabbitMQSettings and the IConnectionFactory you just created and added to the IoC container. You will also call CreateConnection on the ConnectionFactory. Assign both of the above to private fields. public class ModelFactory : IDisposable { private readonly IConnection _connection; private readonly RabbitMQSettings _settings; public ModelFactory(IConnectionFactory connectionFactory, RabbitMQSettings settings) { _settings = settings; _connection = connectionFactory.CreateConnection(); } public void Dispose() { throw new NotImplementedException(); } } Now, create a method called CreateChannel that will return an instance of IModel. This is where you will declare both the type and name of your RabbitMQ exchange. public IModel CreateChannel() { var channel = _connection.CreateModel(); channel.ExchangeDeclare(exchange: _settings.ExchangeName, type: _settings.ExchangeType); return channel; } Lastly, in the Dispose method, you will need to call Dispose on the connection. public void Dispose() { _connection.Dispose(); } You will now have a completed ModelFactory class which you can add to the IoC container back in the SetUpRabbitMq method above and calling the CreateChannel method. public static IServiceCollection SetUpRabbitMq(this IServiceCollection services, IConfiguration config) { var configSection = config.GetSection("RabbitMQSettings"); var settings = new RabbitMQSettings(); configSection.Bind(settings); // add the settings for later use by other classes via injection services.AddSingleton<RabbitMQSettings>(settings); // As the connection factory is disposable, need to ensure container disposes of it when finished services.AddSingleton<IConnectionFactory>(sp => new ConnectionFactory { HostName = settings.HostName }); services.AddSingleton<ModelFactory>(); services.AddSingleton(sp => sp.GetRequiredService<ModelFactory>().CreateChannel()); return services; } That is everything you need to do in the “Messaging.Common” project. RABBITMQ CONFIGURATION For both the KitchenService and the OrderService projects you will store the configuration for RabbitMQ in the appsettings.json file in each project. In production, you will store this configuration wherever you add environment variables. Add the following JSON to the top-level of appsettings.json file in the KitchenService and OrderService projects: { // other configuration removed for brevity "RabbitMQSettings": { "HostName": "localhost", "ExchangeName": "waffle_messaging", "ExchangeType": "topic" } } Here you are specifying: * the HostName as localhost as you are running locally * the ExchangeName as waffle_messaging - this can be changed to suit your needs * finally you define which type of Exchange you would like as topic PRODUCING MESSAGES IN THE ORDER SERVICE The first thing you will need to do is to call the code you just wrote in the “Messaging.Common” project. The reference to the “Messaging.Common” project is already in place, therefore in the “Program.cs” file, before builder.Build() is called, add the following: builder.Services.SetUpRabbitMq(builder.Configuration); This will pass in the application configuration including the RabbitMQSettings – you can see these in the appsettings.json file – and set up the topic exchange, if it doesn’t already exist, and connect to it. Next, you will need to write the code that will convert a C# Order object into a message. Open the “RabbitSender.cs” file and add the following constructor and fields: public class RabbitSender { private readonly IModel _channel; private readonly RabbitMQSettings _rabbitSettings; public RabbitSender(RabbitMQSettings rabbitSettings, IModel channel) { _channel = channel; _rabbitSettings = rabbitSettings; } } Both the RabbitMQSettings and the IModel dependencies were set up in the Messaging.Common project. You can now add a generic method called PublishMessage to the RabbitSender class with the following code: public void PublishMessage<T>(T entity, string key) where T : class { var message = JsonSerializer.Serialize(entity); var body = Encoding.UTF8.GetBytes(message); _channel.BasicPublish(exchange: _rabbitSettings.ExchangeName, routingKey: key, basicProperties: null, body: body); Console.WriteLine(" [x] Sent '{0}':'{1}'", key, message); } The above code serializes the C# object to JSON, encodes it to UTF8 and then publishes it to the RabbitMQ message broker, along with the specified routing key. The key and serialized message will both be printed to the console for monitoring. This could be changed at a later date to print to logs. Now you can add the RabbitSender to the IoC container. Back in “Program.cs”. add the following immediately after you call SetUpRabbitMq: builder.Services.SetUpRabbitMq(builder.Configuration); builder.Services.AddSingleton<RabbitSender>(); The OrderService already has an API endpoint in the “Program.cs” file that will automatically create a seed waffle order, for demonstration purposes only, when the endpoint is called unless an actual order is passed through. You can now update the API to use RabbitSender and send a message to the message broker like so: app.MapPost("/waffleOrder", (RabbitSender rabbitSender, [FromBody] Order order) => { if (order.Id is 0) { order = new Order().Seed(orderIdSeed); orderIdSeed++; } rabbitSender.PublishMessage<Order>(order, "order.cookwaffle"); }); You have made the RabbitSender class available via dependency injection and then called the PublishMessage method passing both the order and the routing key order.cookwaffle. That is everything you need to do in the “OrderService” project. CONSUMING MESSAGES IN THE KITCHEN SERVICE Just as you did in the OrderService, add the RabbitMQ setup to the IServiceCollection in the “Program.cs” file before builder.Build() is called. builder.Services.SetUpRabbitMq(builder.Configuration); Next, you will need to write the code that will listen for incoming messages from the message broker. The RabbitReceiver class has already been created for you and it implements IHostedService. This means that the service, once added to the IoC container, will be a long-running, background service. Open the “RabbitReceiver.cs” file and add the following constructor and fields: public class RabbitReceiver : IHostedService { private readonly RabbitMQSettings _rabbitSettings; private readonly IModel _channel; private readonly IHubContext<OrderHub> _orderHub; public RabbitReceiver(RabbitMQSettings rabbitSettings, IModel channel, IHubContext<OrderHub> hub) { _rabbitSettings = rabbitSettings; _channel = channel; _orderHub = hub; } // code omitted for brevity } Next, you’ll need to create a method in the RabbitReceiver class that actually does the listening. Call this DoStuff and add the following code: private void DoStuff() { _channel.ExchangeDeclare(exchange: _rabbitSettings.ExchangeName, type: _rabbitSettings.ExchangeType); var queueName = _channel.QueueDeclare().QueueName; _channel.QueueBind(queue: queueName, exchange: _rabbitSettings.ExchangeName, routingKey: "#.cookwaffle"); } In the above code, you are declaring the Exchange, checking if it exists and creating if it doesn’t. You will then tell the IModel channel to declare a queue and return the generated name. Next, the queue is bound to the IModel and set to listen to any message with a routing key matching cookwaffle . Finally, you will declare the consumer and add an expression to the Received event on that consumer. The BasicConsume at the end of this code block will actually start the consumption process. Add the following code to the DoStuff method: var consumerAsync = new AsyncEventingBasicConsumer(_channel); consumerAsync.Received += async (_, ea) => { var body = ea.Body.ToArray(); var message = Encoding.UTF8.GetString(body); var order = JsonSerializer.Deserialize<Order>(message); await _orderHub.Clients.All.SendAsync("new-order", order); _channel.BasicAck(ea.DeliveryTag, false); }; _channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumerAsync); When the consumer receives a message with the required routing key pattern #.cookwaffle it will: 1. Get the message body from the EventArgs ea - note that the first argument (happens to be the IModel) in the expression is not used currently and is therefore discarded. 2. Decode the body into a string called message 3. Deserialize the message from JSON to a strongly typed Order model called order 4. Call the “SignalR” hub _orderhub to send a message to all clients (i.e. the kitchen UI) 5. Finally, the BasicAck is called to let RabbitMQ know that the message has been received and processed The last two steps of the RabbitReceiver class is to implement the IHostedService interface correctly. First, the StartAsync code should be updated to this: public Task StartAsync(CancellationToken cancellationToken) { DoStuff(); return Task.CompletedTask; } The above code will begin on application start and call the DoStuff method. Lastly, you need to dispose of the channel correctly on shutdown by updating the StopAsync method to the following: public Task StopAsync(CancellationToken cancellationToken) { _channel.Dispose(); return Task.CompletedTask; } The final step to complete the code in the “KitchenService” is to ensure all the code you have written is instantiated on startup. In the “Program.cs” file, add the following code after you call the SetUpRabbitMq on builder.Services: builder.Services.AddHostedService<RabbitReceiver>(); GETTING IT UP AND RUNNING Now the code is complete, you can start both the “KitchenService” and “OrderService” projects either from using dotnet run in the CLI root of each project or by configuring your IDE to run both projects on start. The Messaging.Common project will not run as it is just a class library. You’ll, of course, need to have RabbitMQ running in Docker as outlined at the start of this post. Once the “OrderService” has started you can navigate to https://localhost:7196/swagger/index.html to see the API explorer provided by Swagger. The “KitchenService” UI is available on https://localhost:7193/ and should be a simple web view. On swagger, click on the endpoint /waffleorder and then on the “Try it out” button. You can either modify the message body or leave it as is - it will seed automatically if not set - then click on “Execute”. Almost instantaneously, you should see an order pop up on the “KitchenService” UI. Your message has been successfully delivered by RabbitMQ - Congrats! WHAT’S NEXT? You have covered a very happy path above that software seldom follows! RabbitMQ is a powerful, enterprise-level message broker with a plethora of features including those that deal with the unhappy path. I strongly urge you to check out the documentation and learn more about what to do when things go wrong. I hope you enjoyed this tutorial. If you have any thoughts or ideas please say hello on any of the channels below: Email: laylap@vmware.com Twitter: @LaylaCodesIt GitHub: layla-p Report an issue VMware Tanzu © 2023 VMware, Inc Terms of Use Privacy Policy Your California Privacy Rights Cookies Settings * * * * * Sign up for the developer newsletter Subscribe * * Select a Job FunctionDeveloperExecutiveBusiness ManagerArchitect/Technical LeadAnalystOperations * Yes, I would like to receive communications from VMware and/or its affiliates regarding products and services, newsletters, and events. Subscribe Done! WE CARE ABOUT YOUR PRIVACY We use cookies to provide you with the best experience on our website, to improve usability and performance and thereby improve what we offer to you. Our website may also use third-party cookies to display advertising that is more relevant to you. By clicking on the “Accept All” button you agree to the storing of cookies on your device. If you close the cookie banner, only strictly necessary cookies will be stored on your device. If you want to know more about how we use cookies, please see our Cookie Policy. Cookies Settings Accept All Cookies COOKIE PREFERENCE CENTER * GENERAL INFORMATION ON COOKIES * STRICTLY NECESSARY * PERFORMANCE * FUNCTIONAL * ADVERTISING * SOCIAL MEDIA GENERAL INFORMATION ON COOKIES When you visit our website, we use cookies to ensure that we give you the best experience. This information does not usually directly identify you, but it can give you a more personalized web experience. Because we respect your right to privacy, you can choose not to allow some types of cookies by clicking on the different category headings to find out more and change your settings. However, blocking some types of cookies may impact your experience on the site and the services we are able to offer. Further information can be found in our Cookie Policy. STRICTLY NECESSARY Always Active Strictly necessary cookies are always enabled since they are essential for our website to function. They enable core functionality such as security, network management, and website accessibility. You can set your browser to block or alert you about these cookies, but this may affect how the website functions. For more information please visit www.aboutcookies.org or www.allaboutcookies.org. Cookies Details PERFORMANCE Performance Performance cookies are used to analyze the user experience to improve our website by collecting and reporting information on how you use it. They allow us to know which pages are the most and least popular, see how visitors move around the site, optimize our website and make it easier to navigate. Cookies Details FUNCTIONAL Functional Functional cookies help us keep track of your past browsing choices so we can improve usability and customize your experience. These cookies enable the website to remember your preferred settings, language preferences, location and other customizable elements such as font or text size. If you do not allow these cookies, then some or all of these services may not function properly. Cookies Details ADVERTISING Advertising Advertising cookies are used to send you relevant advertising and promotional information. They may be set through our site by third parties to build a profile of your interests and show you relevant advertisements on other sites. These cookies do not directly store personal information, but their function is based on uniquely identifying your browser and internet device. Cookies Details SOCIAL MEDIA Social Media Social media cookies are intended to facilitate the sharing of content and to improve the user experience. These cookies can sometimes track your activities. We do not control social media cookies and they do not allow us to gain access to your social media accounts. Please refer to the relevant social media platform’s privacy policies for more information. Cookies Details Back Button COOKIE LIST Filter Button Consent Leg.Interest checkbox label label checkbox label label checkbox label label * View Third Party Cookies * Name cookie name Clear checkbox label label Apply Cancel Confirm My Choices Allow All