Neslihko
7 min readApr 25, 2024

A Beginner’s Guide to How Microservices Talk to Each Other

These days, Developers often split large software into smaller parts called microservices. These microservices can work together but act independently. Think of it like building with Lego bricks — lots of small pieces instead of one big block.

But how do these microservices communicate with each other? Microservices communicate with each other primarily through two main methods: synchronous communication via RESTful APIs and asynchronous communication using message queues.

Synchronous Communication with RESTful APIs:

Microservices communicate directly with each other synchronously using RESTful APIs (Representational State Transfer). This means that one microservice can send a request to another microservice, and the receiving microservice responds to that request. It’s like having a conversation where one person asks a question, and the other person responds immediately.

For example, imagine a shopping app where one microservice handles product information and another microservice manages user accounts. When a user wants to view details about a product, the product microservice sends a request to the user account microservice, asking if the user is logged in. The user account microservice then responds with the user’s login status. (I will explain this part in another article)This synchronous communication happens in real-time, allowing microservices to interact seamlessly.

RESTful APIs are commonly used for these types of interactions between microservices because they provide a standard way for services to communicate over HTTP. They define a set of rules for how requests and responses should be structured, making it easier for developers to build and maintain distributed systems.

Asynchronous Communication with Message Queues: In some cases, microservices need to communicate asynchronously, meaning they don’t have to wait for an immediate response.. This is especially useful in scenarios where services may be running at different speeds or experiencing varying levels of load.

When a microservice publishes a message to a message queue, it’s essentially sending a piece of data to a central storage location. This message contains information or instructions that another microservice might need to know about. Think of it like dropping a letter into a mailbox.

On the other end, microservices that are interested in receiving messages from a particular queue can subscribe to it. They periodically check the queue for new messages and process them accordingly. It’s similar to someone checking the mailbox for letters addressed to them.

Talking in a Simple Way with RESTful APIs

Think of RESTful APIs as a way for microservices to talk to each other like people sending messages back and forth.

The Menu (Resources): a RESTful API has resources. These are like items on the menu. Each resource represents something that the API can give you, like information about a user, a product, or an order. Each resource has its own unique name or “URL”, just like each dish on the menu has its own name.

Placing an Order (HTTP Methods): When you’re ready to order, you tell the waiter what you want and how you want it. In the same way, when you use a RESTful API, you “order” by making a request. You use different “methods” like GET, POST, PUT, and DELETE to tell the API what you want to do with a resource. For example, GET is like asking for information, POST is like adding something new, PUT is like updating, and DELETE is like removing.

Getting Served (Responses): After you’ve placed your order, the waiter brings you what you asked for. Similarly, when you make a request to a RESTful API, it sends you back a response. This response contains the information or action you requested. For example, if you asked for information about a user, the API sends back details about that user.

Talking Clearly (Data Format): Just like you and the waiter speak the same language to understand each other, RESTful APIs use a common language for communication. This language is usually JSON or XML, which are formats that both computers and humans can easily understand.

using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace SimpleRestfulApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private static List<User> _users = new List<User>
{
new User { Id = 1, Name = "Alice" },
new User { Id = 2, Name = "Bob" }
};

[HttpGet]
public IActionResult GetUsers()
{
return Ok(_users);
}

[HttpGet("{id}")]
public IActionResult GetUserById(int id)
{
var user = _users.Find(u => u.Id == id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}

[HttpPost]
public IActionResult AddUser(User user)
{
_users.Add(user);
return CreatedAtAction(nameof(GetUserById), new { id = user.Id }, user);
}
}

public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
}

Using RESTful APIs helps microservices understand each other and work together smoothly.

Sending Messages with Message Queues

Sometimes, microservices need to communicate without waiting for an immediate response. This is where message queues come in handy.

  • Leaving Notes: Think of message queues like a bulletin board where microservices can leave messages for each other. It’s like leaving sticky notes with important information or tasks.
  • Talking Later: The neat thing about message queues is that microservices don’t have to talk to each other at the same time. One microservice can leave a message on the queue, and the other microservice can come along later and read it when it’s ready. It’s like leaving a note for your friend to read when they have time.
  • Ensuring Message Safety: Even if something unexpected happens, like a computer crashing, the messages in the queue are safe and won’t disappear. They’ll be waiting there until the microservice is ready to read them.

Different Approaches: There are two major approaches to using message queues:

  • Point-to-Point (Queues):Imagine you have a messaging system where you can send notes to your friends. In a point-to-point setup:
  1. One Sender, One Receiver: It’s like you writing a note and giving it to only one friend. You’re the sender, and your friend is the receiver. You don’t give the same note to multiple friends.
  2. Each Message Goes to One Friend: Every note you send is received by only one friend. Just like when you send a message to one friend’s inbox and not to a group chat.
  3. Each Message is Processed Once: When your friend receives your note, they read it and take action if needed. They don’t read the same note multiple times.
  4. Importance of Message Ordering: Imagine you’re sending numbered notes with steps for a recipe. It’s important that your friend receives the notes in order, so they can follow the recipe correctly.

So, point-to-point messaging is like sending individual notes to specific friends. Each note goes to one friend only, ensuring that it’s handled properly and in the right order if order matters.

This approach is handy when you need to make sure that each message is received by exactly one recipient and that the order of messages is preserved, just like when you need to ensure that each step of a process is followed in sequence.

  • Publish-Subscribe (Topics): Publishing to a Topic: Imagine you have a big announcement to make, like a party invitation. You write the details on a poster and hang it on a bulletin board in a common area.
  1. Multiple Subscribers: Now, imagine your friends checking the bulletin board. Each friend who sees the invitation can decide to attend the party.
  2. Independent Copies for Subscribers: Even if multiple friends see the invitation, each friend can read it and decide independently whether to attend. They don’t rely on each other to know if they’ve seen it.
  3. One-to-Many Communication: You, as the host, only need to write one invitation and post it once. But many friends can see it and make their own decisions.

So, in Publish-Subscribe messaging:

  • You publish a message (like an invitation) to a topic (like a bulletin board).
  • Multiple subscribers (friends) can receive copies of the message independently.
  • Each subscriber (friend) can then act on the message without affecting others.
  • It’s like broadcasting a message to many recipients, where each recipient can respond individually.

This approach is useful when you need to send a message to multiple recipients who might act on it independently, like sending updates to multiple users or broadcasting notifications to different parts of a system.

Integrating with MassTransit and RabbitMQ

MassTransit is a powerful framework for building distributed systems in .NET. It simplifies the development of message-based applications by providing a high-level abstraction over message queuing systems like RabbitMQ.

By integrating MassTransit with RabbitMQ, you can leverage the benefits of both technologies:

Simplified Development: MassTransit abstracts away the complexities of working directly with RabbitMQ, making it easier to develop message-based applications.

Robust Messaging: RabbitMQ provides a reliable messaging infrastructure, ensuring that messages are delivered even in the face of failures or network issues.

Flexibility: MassTransit supports various messaging patterns, including point-to-point and publish-subscribe, allowing you to choose the approach that best fits your application’s requirements.

Scalability: RabbitMQ’s support for clustering and high availability ensures that your messaging infrastructure can scale to handle large volumes of messages.

By combining MassTransit with RabbitMQ, you can build resilient and scalable distributed systems that can easily communicate with each other using message queues.

using System;
using System.Threading.Tasks;
using MassTransit;

class Program
{
static async Task Main(string[] args)
{
var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(new Uri("rabbitmq://localhost/"), h =>
{
h.Username("guest");
h.Password("guest");
});
});

await busControl.StartAsync();

try
{
// Send message
await busControl.Publish(new MyMessage { Text = "Hello, RabbitMQ!" });
Console.WriteLine(" [x] Sent 'Hello, RabbitMQ!'");
}
finally
{
await busControl.StopAsync();
}

Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}

public class MyMessage
{
public string Text { get; set; }
}

In this example:

  • We’re using MassTransit to create a message bus control (busControl) configured to use RabbitMQ as the messaging transport.
  • We start the bus asynchronously (StartAsync) to begin listening for messages.
  • Inside the try block, we publish a message of type MyMessage with the text "Hello, RabbitMQ!" using await busControl.Publish.
  • If any exceptions occur during publishing, we handle them in the finally block and gracefully stop the bus (StopAsync).
  • We print a message to the console to indicate that the message has been sent.
  • Finally, we wait for the user to press Enter before exiting the application.

This sample demonstrates how to use MassTransit to send messages with RabbitMQ. It abstracts away the complexities of working directly with the RabbitMQ client library (RabbitMQ.Client) and provides a more user-friendly interface for interacting with message queues.

Wrapping Up

So, in the world of microservices, communication is key. RESTful APIs help microservices talk to each other directly, like having a conversation, while message queues allow them to send messages back and forth without waiting for an immediate reply.

Understanding how microservices communicate helps developers build better and more reliable software systems.