Dapr学习笔记
文章转自:https://zhuanlan.zhihu.com/p/553300443?utm_id=0
Dapr(Distributed Application Runtime),为应用提供分布式能力的运行时。
Any Language、Any Framework、Any Where
1、分布式应用需求
- 服务发现:服务注册、服务治理、弹性伸缩
- 服务调用:重试、熔断、限流
- 数据状态:数据读写、读写一致性、幂等、缓存、数据流
- 发布订阅:服务解耦、异步处理
- 配置管理:服务参数、中间件参数、账号密码等
| SpingCloud | Dapr | |
|---|---|---|
| 服务发现 | Euraka、ZooKeeper、Consul、Nacos | Sidecar、虚拟机:mDNS、k8s:Service |
| 负载均衡 | Ribbon、客户端负载均衡 | Sidecar负责实现 |
| 状态存储 | 引入数据库中间件SDK自行处理 | State组件抽象 |
| 并发处理 | 实现分布式锁,自行处理 | Actor组件抽象 |
| 发布订阅 | 引入中间件SDK | Pubsub组件抽象 |
| 重试 | SpingCloud Retry | invoker参数,Sidecar负责实现 |
| 熔断 | SpingCloud Hystrix | invoker参数,Sidecar负责实现 |
| 配置管理 | SpingCloud Config | Secret组件 |
| 健康检查 | SpingBoot-Starter-actuator | Sidecar负责实现 |
| 链路追踪 |
2、Sidecar架构
Dapr 以 Sidecar 架构的方式公开其API,可以是容器、也可以是进程,不需要应用代码包含任何Dapr运行时代码。
POST http://localhost:3500/v1.0/invoke/cart/method/neworder
GET http://localhost:3500/v1.0/state/inventory/items
3、服务调用 Service Invoke

Service A 对 Service B 发起 HTTP/gRPC 的调用
- Service A先请求本地的sidecar:
- http://localhost:<dapr-port>/v1.0/invoke/<application-id>/method/<method-name>
- Service A的Dapr 使用运行在给定托管平台(hosting platform)上的名称解析组件(name resolution component ),基于application-id/method-name来发现 Service B 的位置(对端的sidecar的地址和端口,不是对端服务的地址和端口),所以这里的服务发现都是sidecar的服务发现,可以认为是“代理人”之间的对话;
- Service A的Dapr 将消息转发至 Service B 的 Dapr 边车(Sidecar);
- Service B 的 Dapr 边车将请求转发至 Service B 上的服务 (或方法) ,Service B 随后运行其相应的业务逻辑代码;
- Service B 发送响应给 Service A,响应将先发给 Service B 的边车,Service B 的 Dapr 边车将消息(应用响应结果)转发至 Service A 的 Dapr 边车;
- Service A 从Service A的Dapr边车接收响应信息;
【备注】Dapr 边车之间的所有调用考虑到性能都优先使用 gRPC,仅服务与 Dapr 边车之间的调用可以是 HTTP 或 gRPC。
3.1、服务调用负载均衡

【注意】服务调用是基于重试来判断后端服务的可用性,设置3次重试次数,即1+3次后会判断某后端服务失败。存在一定的服务不一致性情况。
4、Dapr安装 (Linux环境)
4.1、安装dapr cli (安装脚手架)
a. 如果执行的机器可以科学访问,则执行:
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
b. 如果不行,则执行:
找一台可以上外网的服务器,执行完上面的安装程序后执行:
scp /usr/local/bin/dapr root@Dest_IP:/usr/local/bin
4.2、本地初始化:dapr init

执行后检查:
# dapr -v
CLI version: 1.8.1
Runtime version: 1.8.4
说明CLI版本是1.8.1,但runtime则是最新的1.8.4
执行:docker ps

4.3、安装kubernetes环境的dapr环境
1、初始化:dapr init -k

相关pod会创建在dapr-system这个name space中:
执行:kubectl get po -n dapr-system查看pod运行情况:

同时dapr init会自动创建如下的services:

检查安装状态:
dapr status -k

2、安装数据存储Redis
和本地安装不同,kubernetes上的初始化不会自动安装状态存储。在Kubernetes上的状态存储可以有多种选择,包括Redis、CosmosDB, DynamoDB, Cassandra等,这里选择最通用的Redis作为状态存储:
#helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
#helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
#helm install redis bitnami/redis
NAME: redis
LAST DEPLOYED: Sun Aug 14 06:05:14 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: redis
CHART VERSION: 17.0.10
APP VERSION: 7.0.4
** Please be patient while the chart is being deployed **
Redis® can be accessed on the following DNS names from within your cluster:
redis-master.default.svc.cluster.local for read/write operations (port 6379)
redis-replicas.default.svc.cluster.local for read-only operations (port 6379)
To get your password run:
export REDIS_PASSWORD=$(kubectl get secret --namespace default redis -o jsonpath="{.data.redis-password}" | base64 -d)
To connect to your Redis® server:
1. Run a Redis® pod that you can use as a client:
kubectl run --namespace default redis-client --restart='Never' --env REDIS_PASSWORD=$REDIS_PASSWORD --image docker.io/bitnami/redis:7.0.4-debian-11-r11 --command -- sleep infinity
Use the following command to attach to the pod:
kubectl exec --tty -i redis-client \
--namespace default -- bash
2. Connect using the Redis® CLI:
REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h redis-master
REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h redis-replicas
To connect to your database from outside the cluster execute the following commands:
kubectl port-forward --namespace default svc/redis-master 6379:6379 &
REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h 127.0.0.1 -p 6379
基于helm的这个安装会部署一个1主3从的Redis集群:
# kubectl get po -n default | grep redi
redis-master-0 1/1 Running 0 109m
redis-replicas-0 1/1 Running 0 109m
redis-replicas-1 1/1 Running 0 108m
redis-replicas-2 1/1 Running 0 107m
4、部署statestore:
考虑到后续有计划部署dapr的实例,所以这里提前下载相关代码。查看github里dapr的quickstart里Hello-kubernetes这个例子。下载最新版本1.6.0的source code。
解压代码后,在目录quickstarts-1.6.0/hello-kubernetes/deploy 下查看文件 redis.yaml,执行:
#kubectl apply -f redis.yaml
component.dapr.io/statestore created
检查:
# dapr components -A -k
⚠ In future releases, this command will only query the "default" namespace by default. Please use the --namespace flag for a specific namespace, or the --all-namespaces (-A) flag for all namespaces.
NAMESPACE NAME TYPE VERSION SCOPES CREATED AGE
default statestore state.redis v1 2022-08-14 06:21.42 1h
5、访问Dapr Dashboard
在Dapr安装到Kubernetes上时会自动创建服务dapr-dashboard,可以为此服务设置外部访问,如基于node-port模式,则可以直接访问该dashboard:

5、测试本地部署的Dapr的 API
5.1、启动Dapr运行环境:
dapr run --app-id myapp --dapr-http-port 3500
5.2、Dapr 任务执行API
a、保存状态:即POST KV:key: name,value: Bruce Waye,对象:/state/statestore
curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "dapr test app"}]' http://localhost:3500/v1.0/state/statestore
b、查询状态:Dapr会将状态信息保存在Redis,查询redis即可获得状态数据
docker exec -it dapr_redis redis-cli
keys * output --》 "myapp||name"
hgetall "myapp||name" output --》1) "data"
2) "\"dapr test app\""
3) "version"
4) "1"
c、删除状态:
curl -v -X DELETE -H "Content-Type: application/json" http://localhost:3500/v1.0/state/statestore/name
6、部署Kubernetes上的Dapr示例应用hello-kubernetes
6.1、准备应用镜像
在4.3章节下载的quickstart目录,根据自己部署环境的docker registry情况来生成所需的node容器和python容器,下面是使用我在http://docker.io上的镜像库:
#cd quickstarts-1.6.0/hello-kubernetes/node
#docker build -t jerryjedi/hello-k8s-node:0.1 .
#cd quickstarts-1.6.0/hello-kubernetes/python
#docker build -t jerryjedi/pythonapp:0.1 .
#docker login
#docker push jerryjedi/hello-k8s-node:0.1
#docker push jerryjedi/pythonapp:0.1
6.2、部署示例应用
#cd quickstarts-1.6.0/hello-kubernetes/deploy
1、修改node.yaml和python.yaml中的image位置:
image: dapriosamples/hello-k8s-node:latest --》 image: docker.io/jerryjedi/hello-k8s-node:0.1
image: dapriosamples/hello-k8s-python:latest --》 image: docker.io/jerryjedi/pythonapp:0.1
2、修改node.yaml中的Service定义中的type:
type: LoadBalance --> type: NodePort 因为我的环境没有为kubernetes准备LoadBalance的配置
#kubectl apply -f node.yaml
#kubectl apply -f python.yaml
6.3、检查部署
应用会部署到default这个name space中,在部署成功后,会有如下的deployment和pod:
| 类型 | Node应用 | Python应用 |
|---|---|---|
| deployment | deployment.apps/nodeapp | deployment.apps/pythonapp |
| Services | service/nodeapp service/nodeapp-dapr |
service/pythonapp-dapr |
6.4、运行检查
在部署pythonapp后,这个应用会连续往node发送new order:
message = {"data": {"orderId": n}}
response = requests.post(dapr_url, json=message, timeout=5, headers = {"dapr-app-id": "nodeapp"} )
检查方法一:
查看node的日志,可以看到每隔1秒有新的order进来:
#kubectl logs --selector=app=node -c node
Got a new order! Order ID: 27
Successfully persisted state.
Got a new order! Order ID: 28
Successfully persisted state.
。。。。。。
检查方法二:
由于前面已经在node.yaml中启用了service的类型为NodePort,则可以在查看启用的外部访问端口:
#kubectl get services -n default | grep node-app
nodeapp NodePort 10.233.26.48 <none> 80:30839/TCP
在kubernetes运行的节点上访问:
#curl http://ip_nodeapp_running_node:30839/order
{"orderId":31}
检查方法三:
启动Redis Client:
#export REDIS_PASSWORD=$(kubectl get secret --namespace default redis -o jsonpath="{.data.redis-password}" | base64 --decode)
#kubectl run --namespace default redis-client --rm --tty -i --restart='Never' --env DIS_PASSWORD=$REDIS_PASSWORD --image docker.io/bitnami/redis:6.0.9-debian-10-r38 -- bash
在Redis Client的Console中查看:
#redis-cli -h redis-master -a $REDIS_PASSWORD --》 上面的REDIS_PASSWORD替换到这里
redis-master:6379> keys *
1) "nodeapp||order"
redis-master:6379> hgetall nodeapp||order
1) "data"
2) "{\"orderId\":31}"
3) "version"
4) "839"
浙公网安备 33010602011771号