Dapr 分布式运行时实战:用 Sidecar 模式构建云原生微服务
作者:Crown_22 | 云原生 & 分布式系统开发者 | 技术分享
前言
微服务架构下,每个服务都要处理服务发现、消息队列、状态管理、分布式锁、可观测性等横切关注点。这些逻辑跟业务无关,却占了大量代码量。
Dapr(Distributed Application Runtime)的核心理念:把分布式系统的通用能力抽离为 Sidecar 进程,通过 HTTP/gRPC API 调用,让业务代码零侵入地获得这些能力。
本文将从零构建一个基于 Dapr 的微服务系统,涵盖状态管理、发布订阅、服务间调用、Actor 模式和可观测性。

一、Dapr 架构原理
1.1 Sidecar 模式
┌─────────────────────┐ ┌─────────────────────┐
│ 应用进程 │ │ Dapr Sidecar │
│ │ │ │
│ ┌───────────────┐ │ HTTP │ ┌────────────────┐ │
│ │ 业务代码 │◄─┼──────┼─►│ State Store │ │
│ │ (无SDK依赖) │ │ :3500│ │ Pub/Sub │ │
│ └───────────────┘ │ │ │ Service Invoke │ │
│ │ │ │ Bindings │ │
└─────────────────────┘ │ └───────┬────────┘ │
│ │ │
└──────────┼───────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ Redis │ │ RabbitMQ │ │ CosmosDB │
└─────────┘ └──────────┘ └──────────┘
1.2 核心构建块
| 构建块 | 功能 | API |
|---|---|---|
| Service-to-Service | 服务间调用 + 服务发现 | GET/POST /v1.0/invoke/{service}/{method} |
| State Management | 状态存储(KV) | GET/PUT/DELETE /v1.0/state/{store} |
| Pub/Sub | 发布订阅消息 | POST /v1.0/publish/{topic} |
| Bindings | 外部系统绑定 | POST /v1.0/bindings/{name} |
| Actors | 虚拟 Actor 模式 | PUT/POST /v1.0/actors/{type}/{id} |
| Observability | 可观测性 | 自动注入 trace/metrics |
| Secrets | 密钥管理 | GET /v1.0/secrets/{store}/{key} |
二、环境搭建
2.1 安装 Dapr CLI
# 安装 Dapr CLI
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
# 初始化 Dapr(开发模式,自动配置 Redis)
dapr init
# 验证安装
dapr --version
# Output: CLI version: 1.14.0 Runtime version: 1.14.0
# 检查组件
dapr components
2.2 组件配置
# ~/.dapr/components/statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
# ~/.dapr/components/pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
三、服务间调用实战
3.1 订单服务
# order_service.py
import json
import uuid
from datetime import datetime
from flask import Flask, request, jsonify
app = Flask(__name__)
# Dapr 配置
DAPR_HTTP_PORT = 3500
STATE_STORE = "statestore"
CATALOG_APP_ID = "catalog-service"
def dapr_invoke(app_id: str, method: str, data: dict = None) -> dict:
"""通过 Dapr 调用其他服务"""
import requests
url = f"http://localhost:{
DAPR_HTTP_PORT}/v1.0/invoke/{
app_id}/method/{
method}"
if data:
resp = requests.post(url, json=data)
else:
resp = requests.get(url)
return resp.json() if resp.status_code == 200 else None
def dapr_save_state(key: str, value: dict):
"""保存状态到 Dapr State Store"""
import requests
url = f"http://localhost:{
DAPR_HTTP_PORT}/v1.0/state/{
STATE_STORE}"
data = [{
"key": key, "value": value}]
requests.post(url, json=data)
def dapr_get_state(key: str) -> dict:
"""从 Dapr State Store 获取状态"""
import requests
url = f"http://localhost:{
DAPR_HTTP_PORT}/v1.0/state/{
STATE_STORE}/{
key}"
resp = requests.get(url)
return resp.json() if resp.status_code == 200 else None
@app.route("/orders", methods=["POST"])
def create_order():
"""创建订单"""
data = request.json
order_id = str(uuid.uuid4())
# 1. 通过 Dapr 调用商品服务获取商品信息
items_with_info = []
for item in data["items"]:
product = dapr_invoke(CATALOG_APP_ID, f"products/{
item['product_id']}")
if not product:
return jsonify({
"error": f"商品 {
item['product_id']} 不存在"}), 400
# 检查库存
if product["stock"] < item["quantity"]:
return jsonify({
"error": f"商品 {
product['name']} 库存不足"}), 400
items_with_info.append({
"product_id": item["product_id"],
"product_name": product["name"],
"unit_price": product["price"],
"quantity": item["quantity"]
})
# 2. 计算总金额
total = sum(i["unit_price"] * i["quantity"] for i in items_with_info)
# 3. 创建订单对象
order = {
"id": order_id,
"customer_id": data["customer_id"],
"items": items_with_info,



被折叠的 条评论
为什么被折叠?



