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.