Book Image

Hands-On Microservices with C#

By : Matt Cole
Book Image

Hands-On Microservices with C#

By: Matt Cole

Overview of this book

C# is a powerful language when it comes to building applications and software architecture using rich libraries and tools such as .NET. This book will harness the strength of C# in developing microservices architectures and applications. This book shows developers how to develop an enterprise-grade, event-driven, asynchronous, message-based microservice framework using C#, .NET, and various open source tools. We will discuss how to send and receive messages, how to design many types of microservice that are truly usable in a corporate environment. We will also dissect each case and explain the code, best practices, pros and cons, and more. Through our journey, we will use many open source tools, and create file monitors, a machine learning microservice, a quantitative financial microservice that can handle bonds and credit default swaps, a deployment microservice to show you how to better manage your deployments, and memory, health status, and other microservices. By the end of this book, you will have a complete microservice ecosystem you can place into production or customize in no time.
Table of Contents (16 chapters)
11
Trello Microservice – Board Status Updating
12
Microservice Manager – The Nexus

Exchanges

Messages are not published directly to a queue; instead, the producer sends messages to an exchange. An exchange is responsible for the routing of the messages to the different queues. An exchange accepts messages from the producer application and routes them to message queues with the help of bindings and routing keys. A binding is a link between a queue and an exchange:

Message flow in RabbitMQ

  1. The producer publishes a message to an exchange. When you create the exchange, you have to specify the type of it. The different types of exchanges are explained in detail later on.
  2. The exchange receives the message and is now responsible for the routing of the message. The exchange takes different message attributes into account, such as routing key, depending on the exchange type.
  1. Bindings have to be created from the exchange to queues. In this case, we see two bindings to two different queues from the exchange. The exchange routes the message into the queues depending on message attributes.
  2. The messages stay in the queue until they are handled by a consumer.
  3. The consumer handles the message.

Direct exchange

A direct exchange delivers messages to queues based on a message routing key. The routing key can be seen as an address that the exchange is using to decide how to route the message. A message goes to the queues whose binding key exactly matches the routing key of the message.

The direct exchange type is useful when you would like to distinguish messages published to the same exchange using a simple string identifier.

Imagine that Queue A (create_pdf_queue) in the following diagram is bound to a direct exchange (pdf_events) with the binding key pdf_create. When a new message with the routing key pdf_create arrives at the direct exchange, the exchange routes it to the queue where the binding_key = routing_key is, in this case, to Queue A (create_pdf_queue).

SCENARIO 1:

  • Exchange: pdf_events
  • Queue A: create_pdf_queue
  • Binding a key between exchange (pdf_events) and Queue A (create_pdf_queue): pdf_create

SCENARIO 2:

  • Exchange: pdf_events
  • Queue B: pdf_log_queue
  • Binding a key between exchange (pdf_events) and Queue B (pdf_log_queue): pdf_log

EXAMPLE:

A message with the routing key pdf_logis sent to the exchange pdf_events. The message is routed to pdf_log_queue because the routing key (pdf_log) matches the binding key (pdf_log). If the message routing key does not match any binding key, the message will be discarded, as seen in the direct exchange diagram:

A message goes to the queues whose binding key exactly matches the routing key of the message.

Default exchange

The default exchange is a pre-declared direct exchange with no name, usually referred to by the empty string "". When you use the default exchange, your message will be delivered to the queue with a name equal to the routing key of the message. Every queue is automatically bound to the default exchange with a routing key that is the same as the queue name.

Topic exchange

Topic exchanges route messages to queues based on wildcard matches between the routing key and something called the routing pattern specified by the queue binding. Messages are routed to one or many queues based on a matching between a message routing key and this pattern.

The routing key must be a list of words, delimited by a period (.); examples are agreements.us and agreements.eu.stockholm, which, in this case, identifies agreements that are set up for a company with offices in lots of different locations. The routing patterns may contain an asterisk (*) to match a word in a specific position of the routing key (for example, a routing pattern of agreements.*.*.b.* will only match routing keys where the first word is agreements and the fourth word is b). A pound symbol (#) indicates a match on zero or more words (for example, a routing pattern of agreements.eu.berlin.# matches any routing keys beginning with agreements.eu.berlin).

The consumers indicate which topics they are interested in (such as subscribing to a feed for an individual tag). The consumer creates a queue and sets up a binding with a given routing pattern to the exchange. All messages with a routing key that match the routing pattern will be routed to the queue and stay there until the consumer consumes the message.

The following diagram shows three example scenarios:

SCENARIO 1:

Consumer A is interested in all the agreements in Berlin:

  • Exchange: agreements
  • Queue A: berlin_agreements
  • Routing pattern between exchange (agreements) and Queue A (berlin_agreements): agreements.eu.berlin.#
  • Example of message routing key that will match: agreements.eu.berlin and agreements.eu.berlin.headstore

SCENARIO 2:

Consumer B is interested in all the agreements:

  • Exchange: agreements
  • Queue B: all_agreements
  • Routing pattern between exchange (agreements) and Queue B (all_agreements): agreements.#
  • Example of message routing key that will match: agreements.eu.berlin and agreements.us

Topic Exchange: Messages are routed to one or many queues based on a matching between a message routing key and the routing pattern.

SCENARIO 3:

Consumer C is interested in all agreements for European head stores:

  • Exchange: agreements
  • Queue C: headstore_agreements
  • Routing pattern between exchange (agreements) and Queue C (headstore_agreements): agreements.eu.*.headstore
  • Example of message routing keys that will match: agreements.eu.berlin.headstore and agreements.eu.stockholm.headstore

Fanout exchange

The fanout copies and routes a received message to all queues that are bound to it regardless of routing keys or pattern matching, as with direct and topic exchanges. Keys provided will simply be ignored.

Fanout exchanges can be useful when the same message needs to be sent to one or more queues with consumers who may process the same message in different ways.

The fanout copies and routes a received message to all queues that are bound to it regardless of routing keys or pattern matching as with direct and topic exchanges. Keys provided will simply be ignored.

Fanout exchanges can be useful when the same message needs to be sent to one or more queues with consumers who may process the same message in different ways.

The following fanout exchange figure shows an example where a message received by the exchange is copied and routed to all three queues that are bound to the exchange. It could be sport or weather news updates that should be sent out to each connected mobile device when something happens.

The default exchange AMQP brokers must provide for the topic exchange is amq.fanout:

Received messages are routed to all queues that are bound to the exchange.

SCENARIO 1:

  • Exchange: sport news
  • Queue A: Mobile client Queue A
  • Binding: Binging between the exchange (sport news) and Queue A (mobile client Queue A)

EXAMPLE:

A message is sent to the exchange sport news. The message is routed to all queues (Queue A, Queue B, and Queue C) because all queues are bound to the exchange. Provided routing keys are ignored.

Headers exchange

Headers exchange routes are based on arguments containing headers and optional values. Headers exchanges are very similar to topic exchanges, but they route based on header values instead of routing keys. A message is considered matching if the value of the header equals the value specified upon binding.

A special argument named x-match, which can be added in the binding between your exchange and your queue, tells if all headers must match or just one. Either any common header between the message and the binding counts as a match, or all the headers referenced in the binding need to be present in the message for it to match. The x-match property can have two different values: any or all, where all is the default value. A value of all means all header pairs (key, value) must match and a value of any means at least one of the header pairs must match. Headers can be constructed using a wider range of data types—integer or hash, for example, instead of a string. The header's exchange type (used with binding argument any) is useful for directing messages that may contain a subset of known (unordered) criteria:

  • Exchange: Binding to Queue A with arguments (key = value): format = pdf, type = report, x-match = all
  • Exchange: Binding to Queue B with arguments (key = value): format = pdf, type = log, x-match = any
  • Exchange: Binding to Queue C with arguments (key = value): format = zip, type = report, x-match = all

SCENARIO 1:

Message 1 is published to the exchange with the header arguments (key = value): format = pdf, type = report and with the binding argument x-match = all.

Message 1 is delivered to Queue A —all key/value pair match.

SCENARIO 2:

Message 2 is published to the exchange with header arguments of (key = value): format = pdf and with the binding argument x-match = any.

Message 2 is delivered to Queue A and Queue B—the queue is configured to match any of the headers (format or log):

Headers exchange route messages to queues that are bound using arguments (key and value) that contain headers and optional values.

SCENARIO 3:

Message 3 is published to the exchange with the header arguments of (key = value): format = zip, type = log and with the binding argument x-match = all.

Message 3 is not delivered to any queue; the queue is configured to match all of the headers (format or log).