Running Dapr Locally for Microservices Communication

Running Dapr Locally for Microservices - Step-by-Step Guide

Microservices architectures have become the backbone of modern applications, allowing for scalable, independent, and modular development. However, microservices communication, state management, and event-driven interactions can be complex. Dapr (Distributed Application Runtime) simplifies these challenges by providing a set of building blocks to enable seamless microservices communication.

This article walks through installing and configuring Dapr CLI, implementing service-to-service communication, and leveraging Dapr for state management and pub/sub messaging.


1. Installing and Configuring Dapr CLI

Dapr CLI is a command-line tool that simplifies running and managing Dapr applications. Follow these steps to install and configure it locally.

Step 1: Install Dapr CLI

On Windows (PowerShell):

wget -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | Invoke-Expression

On macOS and Linux:

wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash

After installation, verify that Dapr CLI is installed:

dapr --version

Step 2: Initialize Dapr Runtime Locally

Run the following command to initialize Dapr with a self-hosted environment:

dapr init

This installs the Dapr runtime, Redis for state management, and Zipkin for distributed tracing.

To verify the installation, use:

dapr list

If Dapr is running correctly, you should see an output listing the Dapr system services.


2. Implementing Service-to-Service Communication

Step 1: Create Two Sample Microservices

We'll create two microservices: Order Service (sender) and Payment Service (receiver). These services will communicate using Dapr's service invocation API.

Order Service (Python Flask Example)

from flask import Flask, request
import requests

app = Flask(__name__)

@app.route("/create-order", methods=["POST"])
def create_order():
    order_data = {"orderId": "1234", "amount": 100}
    service_url = "http://localhost:3500/v1.0/invoke/payment-service/method/process-payment"
    response = requests.post(service_url, json=order_data)
    return response.json()

if __name__ == "__main__":
    app.run(port=5001)

Payment Service (Node.js Example)

const express = require('express');
const app = express();
app.use(express.json());

app.post('/process-payment', (req, res) => {
    console.log('Processing payment for order:', req.body.orderId);
    res.json({ status: 'Payment processed successfully' });
});

app.listen(5002, () => console.log('Payment service running on port 5002'));

Step 2: Run Microservices with Dapr Sidecar

Start the Order Service:

dapr run --app-id order-service --app-port 5001 --dapr-http-port 3500 -- python order_service.py

Start the Payment Service:

dapr run --app-id payment-service --app-port 5002 --dapr-http-port 3600 -- node payment_service.js

Now, you can test the communication by sending a request to Order Service:

curl -X POST http://localhost:5001/create-order

If successful, you should see the payment service log:

Processing payment for order: 1234

3. Using Dapr for State Management

Dapr provides key-value state management out-of-the-box. By default, it uses Redis, but it supports various backends like Azure Cosmos DB, AWS DynamoDB, and PostgreSQL.

Step 1: Implement State Management in Payment Service

Modify the Payment Service to store order payment status using Dapr's state management API.

Update payment_service.js

const fetch = require('node-fetch');
const express = require('express');
const app = express();
app.use(express.json());

const DAPR_STATE_STORE = "statestore";

app.post('/process-payment', async (req, res) => {
    const { orderId, amount } = req.body;
    const paymentStatus = { orderId, amount, status: 'Completed' };

    await fetch(`http://localhost:3600/v1.0/state/${DAPR_STATE_STORE}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify([{ key: orderId, value: paymentStatus }])
    });

    res.json({ message: 'Payment stored successfully' });
});

app.listen(5002, () => console.log('Payment service running on port 5002'));

Step 2: Retrieve Stored Payment Status

To retrieve stored payment details, add the following endpoint:

app.get('/payment-status/:orderId', async (req, res) => {
    const orderId = req.params.orderId;
    const response = await fetch(`http://localhost:3600/v1.0/state/${DAPR_STATE_STORE}/${orderId}`);
    const data = await response.json();
    res.json(data);
});

Start the services and test:

curl http://localhost:5002/payment-status/1234

You should get a JSON response with the payment details.


4. Using Dapr for Publish-Subscribe Messaging

Dapr supports pub/sub messaging using different brokers (Redis, Kafka, RabbitMQ). By default, it uses Redis.

Step 1: Configure Pub/Sub Component

Create a file pubsub.yaml in components/ directory:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: pubsub
  namespace: default
spec:
  type: pubsub.redis
  version: v1
  metadata:
    - name: redisHost
      value: localhost:6379

Step 2: Publish Events from Order Service

Modify order_service.py:

@app.route("/create-order", methods=["POST"])
def create_order():
    order_data = {"orderId": "1234", "amount": 100}
    pubsub_url = "http://localhost:3500/v1.0/publish/pubsub/orders"
    requests.post(pubsub_url, json=order_data)
    return {"message": "Order published"}

Step 3: Subscribe to Events in Payment Service

Modify payment_service.js:

app.post('/orders', (req, res) => {
    console.log('Received Order:', req.body);
    res.sendStatus(200);
});

Start services with:

dapr run --app-id payment-service --app-port 5002 --dapr-http-port 3600 --components-path ./components -- node payment_service.js

Test by sending:

curl -X POST http://localhost:5001/create-order

You should see the payment service log received events.


Conclusion

Dapr simplifies microservices communication, state management, and pub/sub messaging without modifying business logic. Running Dapr locally helps developers prototype and test distributed applications efficiently before deploying to production.

Key takeaways:

  • Service-to-service communication is made easy via Dapr’s sidecar model.
  • State management abstracts away the complexity of choosing a storage backend.
  • Pub/Sub messaging enhances event-driven microservices.

Dapr is a powerful addition to any microservices architecture, improving scalability and resilience while reducing boilerplate code.

Sandip Mhaske

I’m a software developer exploring the depths of .NET, AWS, Angular, React, and digital entrepreneurship. Here, I decode complex problems, share insightful solutions, and navigate the evolving landscape of tech and finance.

Post a Comment

Previous Post Next Post