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

Form analysis 3 forms found in the DOM

GET /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