Menu

Backend & APIs Topic

Microservices with Kafka And RabbitMQ

Microservices Architecture with Message Brokers

The Extended Stack Overview

In this architecture:

  • FastAPI serves as the HTTP gateway and individual microservice framework

  • PostgreSQL provides persistent data storage for each service's domain data

  • Redis handles caching, sessions, and real-time features

  • Kafka/RabbitMQ enables asynchronous communication between services


Message Brokers: Kafka vs RabbitMQ

RabbitMQ: The General-Purpose Message Broker

When to Choose RabbitMQ:

  • Complex routing needs

  • Background tasks/job queues

  • Request/reply patterns

  • When you need message acknowledgments and delivery guarantees

Key Features:

  • Exchanges & Queues: Direct, topic, fanout, headers exchanges

  • Protocols: AMQP 0-9-1 primarily

  • Delivery Guarantees: At-least-once, exactly-once(with publisher confirms)

  • Message Priorities: Yes

  • Dead Letter Exchanges: For handling failed messages

Common Microservices Patterns with RabbitMQ:

python

# Example: Order Service publishing an event
async def create_order(order_data):
    # Create order in local PostgreSQL
    order = await OrderService.create(order_data)
    
    # Publish event to RabbitMQ
    await rabbitmq_publish(
        exchange="order_events",
        routing_key="order.created",
        message={
            "event_id": str(uuid.uuid4()),
            "event_type": "order_created",
            "timestamp": datetime.utcnow().isoformat(),
            "data": {
                "order_id": order.id,
                "user_id": order.user_id,
                "total_amount": order.total_amount
            }
        }
    )
    
    return order

# Inventory Service consuming the event
async def consume_order_created():
    async def callback(message):
        event = json.loads(message.body)
        if event["event_type"] == "order_created":
            await InventoryService.reserve_stock(
                event["data"]["order_id"],
                event["data"]["items"]
            )
    
    await rabbitmq_consume(
        queue="inventory_order_created",
        exchange="order_events",
        routing_key="order.created",
        callback=callback
    )

Apache Kafka: The Distributed Event Streaming Platform

When to Choose Kafka:

  • High throughput event streaming

  • Event sourcing architectures

  • Real-time data processing pipelines

  • When you need replayability and long-term retention

  • Multiple consumers needing the same messages

Key Features:

  • Topics & Partitions: Scalable, ordered message streams

  • Consumer Groups: Parallel message processing

  • Retention Period: Configurable (hours to forever)

  • High Throughput: Millions of messages per second

  • Durability: Replicated, persistent log

Common Microservices Patterns with Kafka:

python

# Example: User Service publishing to Kafka
async def update_user_profile(user_id, profile_data):
    # Update in local PostgreSQL
    user = await UserService.update(user_id, profile_data)
    
    # Produce event to Kafka
    await kafka_producer.send(
        topic="user-events",
        value={
            "event_id": str(uuid.uuid4()),
            "event_type": "user_profile_updated",
            "timestamp": datetime.utcnow().isoformat(),
            "entity_type": "user",
            "entity_id": user_id,
            "data": {
                "email": user.email,
                "name": user.name,
                "preferences": user.preferences
            }
        }
    )

# Multiple services consuming the same event
# Notification Service
async def send_profile_update_notification():
    async for message in kafka_consumer:
        event = message.value
        if event["event_type"] == "user_profile_updated":
            await NotificationService.send(
                event["entity_id"],
                "Profile updated successfully"
            )

# Analytics Service
async def track_profile_updates():
    async for message in kafka_consumer:
        event = message.value
        if event["event_type"] == "user_profile_updated":
            await AnalyticsService.track(
                "profile_update",
                event["entity_id"],
                event["data"]
            )

Complete Microservices Architecture Example

Service Structure

text

ecommerce-platform/
├── api-gateway/                 # FastAPI
├── services/
│   ├── user-service/           # FastAPI + PostgreSQL
│   ├── order-service/          # FastAPI + PostgreSQL  
│   ├── inventory-service/      # FastAPI + PostgreSQL
│   ├── notification-service/   # FastAPI + Redis
│   └── analytics-service/      # FastAPI + ClickHouse
├── shared/
│   ├── events/                 # Shared event schemas
│   └── clients/               # Service clients
└── infrastructure/
    ├── kafka/                 # Kafka cluster
    ├── redis/                 # Redis cluster
    └── postgres/             # PostgreSQL instances

Event-Driven Order Processing Flow

python

# Order Service
async def create_order(order_data):
    # 1. Validate business rules
    if not await InventoryService.check_availability(order_data.items):
        raise HTTPException(400, "Items not available")
    
    # 2. Create order in local database
    async with database.transaction():
        order = await Order.create(**order_data)
        order_items = await OrderItem.bulk_create(order.id, order_data.items)
    
    # 3. Publish OrderCreated event
    await kafka_producer.send(
        topic="order-events",
        value={
            "event_id": str(uuid.uuid4()),
            "event_type": "OrderCreated",
            "timestamp": datetime.utcnow().isoformat(),
            "data": {
                "order_id": order.id,
                "user_id": order.user_id,
                "items": order_data.items,
                "total_amount": order.total_amount
            }
        }
    )
    
    # 4. Cache order summary in Redis
    await redis.setex(
        f"order:summary:{order.id}",
        3600,  # 1 hour
        json.dumps(order.summary())
    )
    
    return order

# Inventory Service (consumes OrderCreated)
async def handle_order_created():
    consumer = aiokafka.AIOKafkaConsumer(
        "order-events",
        bootstrap_servers=KAFKA_BROKERS,
        group_id="inventory-service"
    )
    await consumer.start()
    
    try:
        async for msg in consumer:
            event = json.loads(msg.value)
            if event["event_type"] == "OrderCreated":
                await reserve_stock(event["data"])
                
                # Publish StockReserved event
                await kafka_producer.send(
                    topic="inventory-events",
                    value={
                        "event_type": "StockReserved",
                        "order_id": event["data"]["order_id"]
                    }
                )
    finally:
        await consumer.stop()

# Payment Service (consumes StockReserved from RabbitMQ)
async def process_payment():
    async with rabbitmq_channel() as channel:
        await channel.queue_declare("payment-queue")
        
        async for message in channel:
            event = json.loads(message.body)
            if event["event_type"] == "StockReserved":
                payment_result = await PaymentService.charge(
                    event["order_id"],
                    event["amount"]
                )
                
                if payment_result.success:
                    # Publish PaymentCompleted event
                    await kafka_producer.send(
                        topic="payment-events",
                        value={
                            "event_type": "PaymentCompleted",
                            "order_id": event["order_id"]
                        }
                    )
                else:
                    # Compensating transaction - release stock
                    await kafka_producer.send(
                        topic="inventory-events", 
                        value={
                            "event_type": "StockRelease",
                            "order_id": event["order_id"]
                        }
                    )

Technology Stack & Libraries

FastAPI Microservices Setup

python

# requirements.txt for a typical service
fastapi==0.104.1
uvicorn==0.24.0
# Database
asyncpg==0.29.0
sqlalchemy==2.0.23
alembic==1.12.1
# Message Brokers
aiokafka==0.8.0
aio-pika==9.4.1
# Redis
redis==5.0.1
# HTTP clients
httpx==0.25.2

Configuration Management

python

# app/core/config.py
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # Service config
    service_name: str = "user-service"
    environment: str = "development"
    
    # Database
    database_url: str
    
    # Redis
    redis_url: str
    
    # Kafka
    kafka_brokers: list[str] = ["localhost:9092"]
    
    # RabbitMQ
    rabbitmq_url: str = "amqp://guest:guest@localhost/"
    
    class Config:
        env_file = ".env"

Service Discovery & Communication

python

# HTTP-based service communication
async def call_service(service_name: str, endpoint: str, method: str = "GET", data: dict = None):
    # Service discovery via Consul or Kubernetes DNS
    service_url = await service_discovery.resolve(service_name)
    
    async with httpx.AsyncClient() as client:
        response = await client.request(
            method,
            f"{service_url}/{endpoint}",
            json=data,
            headers={"X-Service-Origin": settings.service_name}
        )
        return response.json()

# Event-based communication
async def publish_event(topic: str, event: dict):
    await kafka_producer.send(topic, value=event)

Deployment & Orchestration

Docker Compose for Development

yaml

# docker-compose.yml
version: '3.8'
services:
  # Message Brokers
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    # ... config
  
  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on: [zookeeper]
    # ... config
  
  rabbitmq:
    image: rabbitmq:3-management
    # ... config
  
  # Databases
  postgres-user:
    image: postgres:15
    # ... config
  
  postgres-order:
    image: postgres:15
    # ... config
  
  redis:
    image: redis:7-alpine
  
  # Services
  api-gateway:
    build: ./api-gateway
    ports: ["8000:8000"]
    depends_on: [kafka, rabbitmq]
  
  user-service:
    build: ./services/user-service
    depends_on: [postgres-user, redis, kafka]
  
  order-service:
    build: ./services/order-service  
    depends_on: [postgres-order, redis, kafka, rabbitmq]

Kubernetes for Production

yaml

# k8s/service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    spec:
      containers:
      - name: order-service
        image: order-service:latest
        env:
        - name: KAFKA_BROKERS
          value: "kafka-cluster:9092"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: order-db-secret
              key: url
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 8000

Key Benefits of This Architecture

1. Loose Coupling

  • Services communicate via events, not direct calls

  • Each service can evolve independently

  • Technology heterogeneity per service

2. Scalability

  • Scale services independently based on load

  • Kafka enables high-throughput event processing

  • Redis provides fast data access

3. Resilience

  • Message brokers provide retry mechanisms

  • Failed messages can be re-processed

  • Circuit breakers prevent cascading failures

4. Data Consistency

  • Saga pattern for distributed transactions

  • Event sourcing for auditability

  • Compensating transactions for rollbacks

5. Observability

  • Distributed tracing across services

  • Centralized logging

  • Metrics collection and monitoring


When to Use Which Broker?

Use RabbitMQ When:

  • You need complex routing and message patterns

  • Task queues and background job processing

  • Request-reply communication patterns

  • Smaller to medium scale systems

Use Kafka When:

  • High-throughput event streaming

  • Multiple consumers need the same messages

  • Event sourcing and replayability

  • Real-time analytics and data pipelines

  • Large-scale, data-intensive applications

Use Both When:

  • Kafka for event streaming and data pipeline

  • RabbitMQ for task queues and inter-service RPC

  • Leverage strengths of both systems

This extended stack provides a robust foundation for building modern, scalable, and maintainable microservices architectures that can handle complex business domains and high traffic loads.

Comments (0)

No comments yet. Be the first to comment!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?