How do you handle inter-service communication in microservices?
4 minadvancedmicroservicescommunicationmessaging
Quick Answer
Services communicate synchronously (request/response via HTTP/REST or gRPC) or asynchronously (messaging/events via a broker like RabbitMQ, Kafka, or Azure Service Bus). Synchronous is simple but couples availability and adds latency; asynchronous messaging decouples services, improves resilience, and suits event-driven workflows but adds eventual consistency and infrastructure. Choose per interaction, often combining both.
Detailed Answer
Inter-service communication can be synchronous or asynchronous.
Synchronous Communication (Request/Response):
1. HTTP/REST with HttpClient:
// Startup.cs
services.AddHttpClient("OrderService", c =>
{
c.BaseAddress = new Uri("http://order-service:5000");
c.DefaultRequestHeaders.Add("Accept", "application/json");
});
// Service implementation
public class ProductService
{
private readonly IHttpClientFactory _httpClientFactory;
public ProductService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task CheckOrderStatus(int orderId)
{
var client = _httpClientFactory.CreateClient("OrderService");
var response = await client.GetAsync($"/api/orders/{orderId}/status");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync();
}
}
2. gRPC (High-performance RPC):
// order.proto
syntax = "proto3";
service OrderService {
rpc GetOrder (OrderRequest) returns (OrderResponse);
rpc CreateOrder (CreateOrderRequest) returns (OrderResponse);
}
message OrderRequest {
int32 order_id = 1;
}
message OrderResponse {
int32 order_id = 1;
string status = 2;
double total = 3;
}
// gRPC Server
public class OrderServiceImpl : OrderService.OrderServiceBase
{
public override async Task GetOrder(OrderRequest request, ServerCallContext context)
{
var order = await _orderRepository.GetByIdAsync(request.OrderId);
return new OrderResponse
{
OrderId = order.Id,
Status = order.Status,
Total = order.Total
};
}
}
// gRPC Client
public class ProductService
{
private readonly OrderService.OrderServiceClient _orderClient;
public async Task GetOrderDetails(int orderId)
{
var request = new OrderRequest { OrderId = orderId };
return await _orderClient.GetOrderAsync(request);
}
}
Asynchronous Communication (Message-based):
1. RabbitMQ:
// Install: Install-Package RabbitMQ.Client
// Message Publisher
public class OrderCreatedPublisher
{
private readonly IConnection _connection;
public void PublishOrderCreated(OrderCreatedEvent orderEvent)
{
using var channel = _connection.CreateModel();
channel.ExchangeDeclare("orders", ExchangeType.Topic, durable: true);
var message = JsonSerializer.Serialize(orderEvent);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(
exchange: "orders",
routingKey: "order.created",
basicProperties: null,
body: body);
}
}
// Message Consumer
public class InventoryService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
var channel = _connection.CreateModel();
channel.QueueDeclare("inventory-queue", durable: true, exclusive: false);
channel.QueueBind("inventory-queue", "orders", "order.created");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var orderEvent = JsonSerializer.Deserialize(message);
// Process the order
ProcessOrder(orderEvent);
};
channel.BasicConsume("inventory-queue", autoAck: true, consumer);
return Task.CompletedTask;
}
}
2. Azure Service Bus:
// Install: Install-Package Azure.Messaging.ServiceBus
public class ServiceBusPublisher
{
private readonly ServiceBusClient _client;
private readonly ServiceBusSender _sender;
public async Task PublishAsync(T message)
{
var messageBody = JsonSerializer.Serialize(message);
var serviceBusMessage = new ServiceBusMessage(messageBody);
await _sender.SendMessageAsync(serviceBusMessage);
}
}
public class ServiceBusConsumer : BackgroundService
{
private readonly ServiceBusProcessor _processor;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_processor.ProcessMessageAsync += MessageHandler;
_processor.ProcessErrorAsync += ErrorHandler;
await _processor.StartProcessingAsync(stoppingToken);
}
private async Task MessageHandler(ProcessMessageEventArgs args)
{
var body = args.Message.Body.ToString();
var order = JsonSerializer.Deserialize(body);
// Process message
await ProcessOrderAsync(order);
await args.CompleteMessageAsync(args.Message);
}
}