欢迎来到 Dapr 快速入门指南!
Dapr 概念
如果您想了解 Dapr 的基本概念和术语,我们建议您先查看概念部分。我们的入门指南将逐步引导您完成安装、初始化、试用和开始使用 Dapr 的过程。
This is the multi-page printable view of this section. Click here to print.
Dapr CLI 是执行各种 Dapr 相关任务的主要工具。您可以使用它来:
Dapr CLI 可在 自托管 和 Kubernetes 环境中使用。
将最新的 Linux Dapr CLI 安装到 /usr/local/bin
:
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash
以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | /bin/bash -s 1.15.1
sudo
安装如果您无法使用 sudo
命令或您的用户名不在 sudoers
文件中,可以通过 DAPR_INSTALL_DIR
环境变量将 Dapr 安装到其他目录。此目录必须已存在并且当前用户可访问。
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | DAPR_INSTALL_DIR="$HOME/dapr" /bin/bash
sudo
安装特定的 CLI 版本以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
wget -q https://raw.githubusercontent.com/dapr/cli/master/install/install.sh -O - | DAPR_INSTALL_DIR="$HOME/dapr" /bin/bash -s 1.15.1
将最新的 Windows Dapr CLI 安装到 $Env:SystemDrive\dapr
并将此目录添加到用户 PATH 环境变量:
powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
注意: PATH 的更新可能在您重新启动终端应用程序之前不可见。
以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
powershell -Command "$script=iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1; $block=[ScriptBlock]::Create($script); invoke-command -ScriptBlock $block -ArgumentList 1.15.1"
如果您没有管理员权限,可以通过 DAPR_INSTALL_DIR
环境变量将 Dapr 安装到其他目录。以下脚本将在目录不存在时创建它。
$Env:DAPR_INSTALL_DIR = "<your_alt_install_dir_path>"
$script=iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1; $block=[ScriptBlock]::Create($script); invoke-command -ScriptBlock $block -ArgumentList "", "$Env:DAPR_INSTALL_DIR"
以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
$Env:DAPR_INSTALL_DIR = "<your_alt_install_dir_path>"
$script=iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1; $block=[ScriptBlock]::Create($script); invoke-command -ScriptBlock $block -ArgumentList "1.15.1", "$Env:DAPR_INSTALL_DIR"
将最新的 Windows Dapr CLI 安装到 $Env:SystemDrive\dapr
并将此目录添加到用户 PATH 环境变量:
winget install Dapr.CLI
对于预览版本:
安装最新的预览版本:
winget install Dapr.CLI.Preview
每个 Dapr CLI 版本还包括一个 Windows 安装程序。您可以手动下载 MSI:
dapr.msi
。$Env:SystemDrive\dapr
。Install
开始安装。安装完成后,您将看到最终消息。将最新的 Darwin Dapr CLI 安装到 /usr/local/bin
:
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash
以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash -s 1.15.1
对于 ARM64 Mac:
从终端安装时,原生 ARM64 二进制文件可用。
要安装 Rosetta 仿真:
softwareupdate --install-rosetta
通过 Homebrew 安装:
brew install dapr/tap/dapr-cli
对于 ARM64 Mac:
对于 ARM64 Mac,支持 Homebrew 3.0 及更高版本。更新 Homebrew 至 3.0.0 或更高版本,然后运行以下命令:
arch -arm64 brew install dapr/tap/dapr-cli
sudo
安装如果您无法使用 sudo
命令或您的用户名不在 sudoers
文件中,可以通过 DAPR_INSTALL_DIR
环境变量将 Dapr 安装到其他目录。此目录必须已存在并且当前用户可访问。
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | DAPR_INSTALL_DIR="$HOME/dapr" /bin/bash
sudo
安装特定的 CLI 版本以下示例展示如何安装 CLI 版本 1.15.1
。您还可以通过指定版本来安装候选版本(例如,1.10.0-rc.3
)。
curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | DAPR_INSTALL_DIR="$HOME/dapr" -s 1.15.1
每个 Dapr CLI 版本都包括各种操作系统和架构。您可以手动下载并安装这些二进制版本。
/usr/local/bin
。C:\dapr
的目录。通过重新启动您的终端/命令提示符并运行以下命令来验证 CLI 是否已安装:
dapr -h
输出:
__
____/ /___ _____ _____
/ __ / __ '/ __ \/ ___/
/ /_/ / /_/ / /_/ / /
\__,_/\__,_/ .___/_/
/_/
===============================
分布式应用程序运行时
用法:
dapr [命令]
可用命令:
completion 生成 shell 补全脚本
components 列出所有 Dapr 组件。支持的平台:Kubernetes
configurations 列出所有 Dapr 配置。支持的平台:Kubernetes
dashboard 启动 Dapr 仪表板。支持的平台:Kubernetes 和自托管
help 获取任何命令的帮助
init 在支持的托管平台上安装 Dapr。支持的平台:Kubernetes 和自托管
invoke 调用给定 Dapr 应用程序上的方法。支持的平台:自托管
list 列出所有 Dapr 实例。支持的平台:Kubernetes 和自托管
logs 获取应用程序的 Dapr 边车日志。支持的平台:Kubernetes
mtls 检查是否启用了 mTLS。支持的平台:Kubernetes
publish 发布一个 pub-sub 事件。支持的平台:自托管
run 运行 Dapr 并(可选)与您的应用程序并排运行。支持的平台:自托管
status 显示 Dapr 服务的健康状态。支持的平台:Kubernetes
stop 停止 Dapr 实例及其关联的应用程序。支持的平台:自托管
uninstall 卸载 Dapr 运行时。支持的平台:Kubernetes 和自托管
upgrade 升级集群中的 Dapr 控制平面安装。支持的平台:Kubernetes
version 打印 Dapr 运行时和 CLI 版本
标志:
-h, --help 获取 dapr 的帮助
-v, --version 获取 dapr 的版本
使用 "dapr [命令] --help" 获取有关命令的更多信息。
dapr init
获取并在本地安装 Dapr sidecar 二进制文件现在您已经安装了 Dapr CLI,可以使用 CLI 在本地计算机上配置 Dapr。
Dapr 作为一个附属进程与您的应用程序一起运行。在自托管模式下,这意味着它在您的本地计算机上作为一个进程运行。通过配置 Dapr,您可以:
Dapr 的配置过程包括:
推荐的开发环境需要 Docker。虽然您可以在没有 Docker 依赖的情况下配置 Dapr,但本指南的下一步假设您使用推荐的 Docker 开发环境。
您也可以安装 Podman 代替 Docker。阅读更多关于使用 Podman 配置 Dapr的信息。
如果您在运行 Docker 命令时使用 sudo
,或者安装路径是 /usr/local/bin
(默认安装路径),则需要在此快速入门中使用 sudo
。
以管理员身份运行 Windows Terminal 或命令提示符。
安装最新的 Dapr 运行时二进制文件:
dapr init
如果您在运行 Docker 命令时使用 sudo,则需要使用:
sudo dapr init
如果您在 Mac OS Silicon 上使用 Docker 安装,可能需要执行以下变通方法以使 dapr init
能够在不使用 Kubernetes 的情况下与 Docker 通信。
安装最新的 Dapr 运行时二进制文件:
dapr init
预期输出:
如果您遇到任何关于 Docker 未安装或未运行的错误消息,请参阅故障排除指南。
dapr --version
输出:
CLI version: 1.15.1
Runtime version: 1.15.5
如前所述,dapr init
命令启动了几个容器,这些容器将帮助您开始使用 Dapr。通过 daprio/dapr
、openzipkin/zipkin
和 redis
镜像验证您是否有容器实例在运行:
docker ps
输出:
在 dapr init
时,CLI 还会创建一个默认组件文件夹,其中包含几个 YAML 文件,这些文件定义了状态存储、发布/订阅和 Zipkin。Dapr sidecar 将读取这些组件并使用:
通过打开您的组件目录进行验证:
%UserProfile%\.dapr
~/.dapr
ls $HOME/.dapr
输出:
bin components config.yaml
您可以使用 PowerShell 或命令行进行验证。如果使用 PowerShell,运行:
explorer "$env:USERPROFILE\.dapr"
如果使用命令行,运行:
explorer "%USERPROFILE%\.dapr"
结果:
要安装没有任何默认配置文件或 Docker 容器的 CLI,请使用 --slim
标志。了解更多关于 init
命令及其标志的信息。
dapr init --slim
在本指南中,您将通过运行 sidecar 并直接调用状态管理 API 来模拟应用程序的操作。在使用 Dapr CLI 运行 Dapr 之后,您将:
dapr run
命令通常会运行您的应用程序和一个 Dapr sidecar。在这种情况下,由于您直接与状态管理 API 交互,它只运行 sidecar。
启动一个 Dapr sidecar,它将在端口 3500 上监听一个名为 myapp
的空白应用程序:
dapr run --app-id myapp --dapr-http-port 3500
由于上述命令没有定义自定义组件文件夹,Dapr 使用在 dapr init
流程中创建的默认组件定义。
使用一个对象更新状态。新的状态将如下所示:
[
{
"key": "name",
"value": "Bruce Wayne"
}
]
注意,状态中包含的每个对象都有一个 key
,其值为 name
。您将在下一步中使用该 key。
使用以下命令保存一个新的状态对象:
curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/statestore
Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "name", "value": "Bruce Wayne"}]' -Uri 'http://localhost:3500/v1.0/state/statestore'
使用状态管理 API 和 key name
检索您刚刚存储在状态中的对象。在同一个终端窗口中,运行以下命令:
curl http://localhost:3500/v1.0/state/statestore/name
Invoke-RestMethod -Uri 'http://localhost:3500/v1.0/state/statestore/name'
查看 Redis 容器并验证 Dapr 是否将其用作状态存储。使用以下命令与 Redis CLI 交互:
docker exec -it dapr_redis redis-cli
列出 Redis 键以查看 Dapr 如何使用您提供给 dapr run
的 app-id 作为键的前缀创建键值对:
keys *
输出:1) "myapp||name"
通过运行以下命令查看状态值:
hgetall "myapp||name"
输出:1) "data"
2) "\"Bruce Wayne\""
3) "version"
4) "1"
使用以下命令退出 Redis CLI:
exit
在同一个终端窗口中,从状态存储中删除 name
状态对象。
curl -v -X DELETE -H "Content-Type: application/json" http://localhost:3500/v1.0/state/statestore/name
Invoke-RestMethod -Method Delete -ContentType 'application/json' -Uri 'http://localhost:3500/v1.0/state/statestore/name'
您已经初始化了 Dapr 并尝试了一些构建块,现在可以浏览我们更详细的教程。
得益于我们庞大的 Dapr 社区,我们提供的教程既托管在 Dapr 文档上,也托管在我们的 GitHub 仓库上。
Dapr 文档教程 | 描述 |
---|---|
定义一个组件 | 创建一个组件定义文件以与 Secrets 构建块交互。 |
配置 State & Pub/sub | 为 Dapr 配置状态存储和发布/订阅消息代理组件。 |
GitHub 教程 | 描述 |
---|---|
Hello World | 推荐 演示如何在本地运行 Dapr,主要展示服务调用和状态管理。 |
Hello World Kubernetes | 推荐 演示如何在 Kubernetes 中运行 Dapr,主要展示服务调用和状态管理。 |
分布式计算器 | 演示一个分布式计算器应用,使用 Dapr 服务驱动 React Web 应用。主要展示多语言编程、服务调用和状态管理。 |
Pub/Sub | 演示如何使用 Dapr 启用发布/订阅应用,使用 Redis 作为发布/订阅组件的实现。 |
Bindings | 演示如何使用 Dapr 创建与其他组件的输入和输出绑定,使用 Kafka 作为绑定的实现。 |
可观测性 | 演示 Dapr 的追踪能力,使用 Zipkin 作为追踪组件。 |
Secret Store | 演示使用 Dapr Secrets API 访问密钥存储。 |
在构建应用程序时,通常需要根据所需的构建块和特定组件创建组件文件定义。
在本教程中,您将创建一个组件定义文件以与secrets构建块API交互:
创建一个名为my-components
的新目录以保存新的密钥和组件文件:
mkdir my-components
进入此目录。
cd my-components
Dapr支持多种类型的密钥存储,但在本教程中,创建一个名为mysecrets.json
的本地JSON文件,其中包含以下密钥:
{
"my-secret" : "I'm Batman"
}
创建一个新文件localSecretStore.yaml
,内容如下:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: my-secret-store
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: ./mysecrets.json
- name: nestedSeparator
value: ":"
在上述文件定义中:
type: secretstores.local.file
指定Dapr使用本地文件组件作为密钥存储。dapr run
命令的位置。启动一个Dapr sidecar,它将在端口3500上监听一个名为myapp
的空应用程序:
对于PowerShell环境:
dapr run --app-id myapp --dapr-http-port 3500 --resources-path ../
对于非PowerShell环境:
dapr run --app-id myapp --dapr-http-port 3500 --resources-path .
如果出现错误消息提示app-id
已被使用,您可能需要停止任何当前正在运行的Dapr sidecar。在运行下一个dapr run
命令之前,可以通过以下方式停止sidecar:
dapr stop
命令。在一个单独的终端中,运行:
curl http://localhost:3500/v1.0/secrets/my-secret-store/my-secret
Invoke-RestMethod -Uri 'http://localhost:3500/v1.0/secrets/my-secret-store/my-secret'
输出:
{"my-secret":"I'm Batman"}
要使用状态和发布/订阅功能,您需要配置两个组件:
您可以在以下链接找到支持的组件列表:
本教程将介绍如何使用 Redis 进行配置。
Dapr 可以使用任何 Redis 实例,无论是:
如果您已经有一个 Redis 实例,请直接跳到配置部分。
在自托管环境中,Dapr CLI 会在初始化过程中自动安装 Redis。您可以直接进行下一步。
您可以使用 Helm 在 Kubernetes 集群中创建 Redis 实例。在开始之前,请确保已安装 Helm v3。
在集群中安装 Redis:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install redis bitnami/redis --set image.tag=6.2
Dapr 的发布/订阅功能至少需要 Redis 版本 5。对于状态存储,您可以使用更低版本。
如果您在本地环境中工作,可以在 install
命令中添加 --set architecture=standalone
,以创建单副本 Redis 设置,从而节省内存和资源。
运行 kubectl get pods
查看集群中运行的 Redis 容器:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
redis-master-0 1/1 Running 0 69s
redis-replicas-0 1/1 Running 0 69s
redis-replicas-1 1/1 Running 0 22s
在 Kubernetes 中:
redis-master.default.svc.cluster.local:6379
redis
会自动创建。确保您拥有 Azure 订阅。
xxxxxx.redis.cache.windows.net:6380
。导航到 设置 下的 访问密钥。
创建一个 Kubernetes secret 来存储您的 Redis 密码:
kubectl create secret generic redis --from-literal=redis-password=*********
从 AWS Redis 部署一个 Redis 实例。
记下 AWS 门户中的 Redis 主机名以备后用。
创建一个 Kubernetes secret 来存储您的 Redis 密码:
kubectl create secret generic redis --from-literal=redis-password=*********
从 GCP Cloud MemoryStore 部署一个 MemoryStore 实例。
记下 GCP 门户中的 Redis 主机名以备后用。
创建一个 Kubernetes secret 来存储您的 Redis 密码:
kubectl create secret generic redis --from-literal=redis-password=*********
Dapr 使用组件定义来管理构建块功能。以下步骤将指导您如何将上面创建的资源连接到 Dapr,以用于状态和发布/订阅。
在自托管模式下,组件文件会自动创建在:
%USERPROFILE%\.dapr\components\
$HOME/.dapr/components
由于 Kubernetes 文件是通过 kubectl
应用的,因此可以在任何目录中创建。
创建一个名为 redis-state.yaml
的文件,并粘贴以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: default
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: default
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: <REPLACE WITH HOSTNAME FROM ABOVE - for Redis on Kubernetes it is redis-master.default.svc.cluster.local:6379>
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
请注意,上述代码示例使用了您在设置集群时创建的 Kubernetes secret。
创建一个名为 redis-pubsub.yaml
的文件,并粘贴以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: default
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: default
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: <REPLACE WITH HOSTNAME FROM ABOVE - for Redis on Kubernetes it is redis-master.default.svc.cluster.local:6379>
- name: redisPassword
secretKeyRef:
name: redis
key: redis-password
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
请注意,上述代码示例使用了您在设置集群时创建的 Kubernetes secret。
仅用于开发目的,您可以跳过创建 Kubernetes secret 并将密码直接放入 Dapr 组件文件中:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: default
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: <HOST>
- name: redisPassword
value: <PASSWORD>
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
namespace: default
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: <HOST>
- name: redisPassword
value: <PASSWORD>
# 取消注释以下内容以通过 TLS 连接到 redis 缓存实例(例如 - Azure Redis 缓存)
# - name: enableTLS
# value: true
当您运行 dapr init
时,Dapr 会在您的本地机器上创建一个默认的 redis pubsub.yaml
。通过打开您的组件目录进行验证:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
对于新的组件文件:
components
目录。--resources-path
标志为 dapr run
命令提供路径如果您在精简模式(无 Docker)下初始化了 Dapr,您需要手动创建默认目录,或者始终使用 --resources-path
指定组件目录。
运行 kubectl apply -f <FILENAME>
以应用状态和发布/订阅文件:
kubectl apply -f redis-state.yaml
kubectl apply -f redis-pubsub.yaml
通过我们的 Dapr 快速入门指南,结合代码示例,帮助您轻松掌握 Dapr。
快速入门 | 描述 |
---|---|
服务调用 | 通过 HTTP 或 gRPC 实现两个服务之间的同步通信。 |
发布和订阅 | 通过消息实现两个服务之间的异步通信。 |
工作流 | 在长时间运行的应用中协调业务流程,确保容错和状态管理。 |
状态管理 | 以键/值对形式存储服务数据,支持多种状态存储。 |
绑定 | 使用输入绑定响应外部事件,使用输出绑定执行操作。 |
参与者 | 运行微服务和简单客户端,展示 Dapr 参与者的状态化对象模式。 |
秘密管理 | 安全获取和管理敏感信息。 |
配置 | 获取配置项并监听配置更新。 |
弹性 | 为 Dapr API 请求定义和应用容错策略。 |
加密 | 使用 Dapr 的加密 API 进行数据加密和解密。 |
作业 | 使用 Dapr 的作业 API 进行作业调度、检索和删除。 |
通过 Dapr 的服务调用模块,您的应用程序可以稳定且安全地与其他应用程序进行通信。
Dapr 提供了多种服务调用的方法,您可以根据具体需求进行选择。在本教程中,您将启用结账服务,通过 HTTP 代理调用订单处理服务中的方法,具体步骤如下:
在概述文章中了解更多关于 Dapr 服务调用方法的信息。
在继续本教程之前,请选择您偏好的编程语言。
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门克隆目录的根目录,导航到快速入门目录。
cd service_invocation/python/http
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
pip3 install -r requirements.txt
cd ../checkout
pip3 install -r requirements.txt
cd ..
order-processor
和 checkout
服务使用以下命令,同时运行以下服务及其各自的 Dapr 边车:
order-processor
服务checkout
服务dapr run -f .
注意:在 Windows 中,由于未定义 Python3.exe,您可能需要在运行
dapr run -f .
之前将dapr.yaml
文件中的python3
更改为python
预期输出
== APP - order-processor == Order received : Order { orderId = 1 }
== APP - checkout == Order passed: Order { OrderId = 1 }
== APP - order-processor == Order received : Order { orderId = 2 }
== APP - checkout == Order passed: Order { OrderId = 2 }
== APP - order-processor == Order received : Order { orderId = 3 }
== APP - checkout == Order passed: Order { OrderId = 3 }
== APP - order-processor == Order received : Order { orderId = 4 }
== APP - checkout == Order passed: Order { OrderId = 4 }
== APP - order-processor == Order received : Order { orderId = 5 }
== APP - checkout == Order passed: Order { OrderId = 5 }
== APP - order-processor == Order received : Order { orderId = 6 }
== APP - checkout == Order passed: Order { OrderId = 6 }
== APP - order-processor == Order received : Order { orderId = 7 }
== APP - checkout == Order passed: Order { OrderId = 7 }
== APP - order-processor == Order received : Order { orderId = 8 }
== APP - checkout == Order passed: Order { OrderId = 8 }
== APP - order-processor == Order received : Order { orderId = 9 }
== APP - checkout == Order passed: Order { OrderId = 9 }
== APP - order-processor == Order received : Order { orderId = 10 }
== APP - checkout == Order passed: Order { OrderId = 10 }
== APP - order-processor == Order received : Order { orderId = 11 }
== APP - checkout == Order passed: Order { OrderId = 11 }
== APP - order-processor == Order received : Order { orderId = 12 }
== APP - checkout == Order passed: Order { OrderId = 12 }
== APP - order-processor == Order received : Order { orderId = 13 }
== APP - checkout == Order passed: Order { OrderId = 13 }
== APP - order-processor == Order received : Order { orderId = 14 }
== APP - checkout == Order passed: Order { OrderId = 14 }
== APP - order-processor == Order received : Order { orderId = 15 }
== APP - checkout == Order passed: Order { OrderId = 15 }
== APP - order-processor == Order received : Order { orderId = 16 }
== APP - checkout == Order passed: Order { OrderId = 16 }
== APP - order-processor == Order received : Order { orderId = 17 }
== APP - checkout == Order passed: Order { OrderId = 17 }
== APP - order-processor == Order received : Order { orderId = 18 }
== APP - checkout == Order passed: Order { OrderId = 18 }
== APP - order-processor == Order received : Order { orderId = 19 }
== APP - checkout == Order passed: Order { OrderId = 19 }
== APP - order-processor == Order received : Order { orderId = 20 }
== APP - checkout == Order passed: Order { OrderId = 20 }
Exited App successfully
在本教程中运行 dapr run -f .
使用 dapr.yaml
多应用程序运行模板文件启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用程序运行模板文件使用 dapr run -f .
运行 多应用程序运行模板文件 将启动项目中的所有应用程序。在本教程中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./order-processor/
appID: order-processor
appPort: 8001
command: ["python3", "app.py"]
- appID: checkout
appDirPath: ./checkout/
command: ["python3", "app.py"]
order-processor
服务order-processor
服务接收来自 checkout
服务的调用:
@app.route('/orders', methods=['POST'])
def getOrder():
data = request.json
print('Order received : ' + json.dumps(data), flush=True)
return json.dumps({'success': True}), 200, {
'ContentType': 'application/json'}
app.run(port=8001)
checkout
服务在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
headers = {'dapr-app-id': 'order-processor'}
result = requests.post(
url='%s/orders' % (base_url),
data=json.dumps(order),
headers=headers
)
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门克隆目录的根目录,导航到快速入门目录。
cd service_invocation/javascript/http
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
npm install
cd ../checkout
npm install
cd ..
order-processor
和 checkout
服务使用以下命令,同时运行以下服务及其各自的 Dapr 边车:
order-processor
服务checkout
服务dapr run -f .
预期输出
== APP - order-processor == Order received : Order { orderId = 1 }
== APP - checkout == Order passed: Order { OrderId = 1 }
== APP - order-processor == Order received : Order { orderId = 2 }
== APP - checkout == Order passed: Order { OrderId = 2 }
== APP - order-processor == Order received : Order { orderId = 3 }
== APP - checkout == Order passed: Order { OrderId = 3 }
== APP - order-processor == Order received : Order { orderId = 4 }
== APP - checkout == Order passed: Order { OrderId = 4 }
== APP - order-processor == Order received : Order { orderId = 5 }
== APP - checkout == Order passed: Order { OrderId = 5 }
== APP - order-processor == Order received : Order { orderId = 6 }
== APP - checkout == Order passed: Order { OrderId = 6 }
== APP - order-processor == Order received : Order { orderId = 7 }
== APP - checkout == Order passed: Order { OrderId = 7 }
== APP - order-processor == Order received : Order { orderId = 8 }
== APP - checkout == Order passed: Order { OrderId = 8 }
== APP - order-processor == Order received : Order { orderId = 9 }
== APP - checkout == Order passed: Order { OrderId = 9 }
== APP - order-processor == Order received : Order { orderId = 10 }
== APP - checkout == Order passed: Order { OrderId = 10 }
== APP - order-processor == Order received : Order { orderId = 11 }
== APP - checkout == Order passed: Order { OrderId = 11 }
== APP - order-processor == Order received : Order { orderId = 12 }
== APP - checkout == Order passed: Order { OrderId = 12 }
== APP - order-processor == Order received : Order { orderId = 13 }
== APP - checkout == Order passed: Order { OrderId = 13 }
== APP - order-processor == Order received : Order { orderId = 14 }
== APP - checkout == Order passed: Order { OrderId = 14 }
== APP - order-processor == Order received : Order { orderId = 15 }
== APP - checkout == Order passed: Order { OrderId = 15 }
== APP - order-processor == Order received : Order { orderId = 16 }
== APP - checkout == Order passed: Order { OrderId = 16 }
== APP - order-processor == Order received : Order { orderId = 17 }
== APP - checkout == Order passed: Order { OrderId = 17 }
== APP - order-processor == Order received : Order { orderId = 18 }
== APP - checkout == Order passed: Order { OrderId = 18 }
== APP - order-processor == Order received : Order { orderId = 19 }
== APP - checkout == Order passed: Order { OrderId = 19 }
== APP - order-processor == Order received : Order { orderId = 20 }
== APP - checkout == Order passed: Order { OrderId = 20 }
Exited App successfully
在本教程中运行 dapr run -f .
使用 dapr.yaml
多应用程序运行模板文件启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用程序运行模板文件使用 dapr run -f .
运行 多应用程序运行模板文件 将启动项目中的所有应用程序。在本教程中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./order-processor/
appID: order-processor
appPort: 5001
command: ["npm", "start"]
- appID: checkout
appDirPath: ./checkout/
command: ["npm", "start"]
order-processor
服务order-processor
服务接收来自 checkout
服务的调用:
app.post('/orders', (req, res) => {
console.log("Order received:", req.body);
res.sendStatus(200);
});
checkout
服务在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
let axiosConfig = {
headers: {
"dapr-app-id": "order-processor"
}
};
const res = await axios.post(`${DAPR_HOST}:${DAPR_HTTP_PORT}/orders`, order , axiosConfig);
console.log("Order passed: " + res.config.data);
在此示例中,您需要:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。仅 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门克隆目录的根目录,导航到快速入门目录。
cd service_invocation/csharp/http
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
dotnet restore
dotnet build
cd ../checkout
dotnet restore
dotnet build
cd ..
order-processor
和 checkout
服务使用以下命令,同时运行以下服务及其各自的 Dapr 边车:
order-processor
服务checkout
服务dapr run -f .
预期输出
== APP - order-processor == Order received : Order { orderId = 1 }
== APP - checkout == Order passed: Order { OrderId = 1 }
== APP - order-processor == Order received : Order { orderId = 2 }
== APP - checkout == Order passed: Order { OrderId = 2 }
== APP - order-processor == Order received : Order { orderId = 3 }
== APP - checkout == Order passed: Order { OrderId = 3 }
== APP - order-processor == Order received : Order { orderId = 4 }
== APP - checkout == Order passed: Order { OrderId = 4 }
== APP - order-processor == Order received : Order { orderId = 5 }
== APP - checkout == Order passed: Order { OrderId = 5 }
== APP - order-processor == Order received : Order { orderId = 6 }
== APP - checkout == Order passed: Order { OrderId = 6 }
== APP - order-processor == Order received : Order { orderId = 7 }
== APP - checkout == Order passed: Order { OrderId = 7 }
== APP - order-processor == Order received : Order { orderId = 8 }
== APP - checkout == Order passed: Order { OrderId = 8 }
== APP - order-processor == Order received : Order { orderId = 9 }
== APP - checkout == Order passed: Order { OrderId = 9 }
== APP - order-processor == Order received : Order { orderId = 10 }
== APP - checkout == Order passed: Order { OrderId = 10 }
== APP - order-processor == Order received : Order { orderId = 11 }
== APP - checkout == Order passed: Order { OrderId = 11 }
== APP - order-processor == Order received : Order { orderId = 12 }
== APP - checkout == Order passed: Order { OrderId = 12 }
== APP - order-processor == Order received : Order { orderId = 13 }
== APP - checkout == Order passed: Order { OrderId = 13 }
== APP - order-processor == Order received : Order { orderId = 14 }
== APP - checkout == Order passed: Order { OrderId = 14 }
== APP - order-processor == Order received : Order { orderId = 15 }
== APP - checkout == Order passed: Order { OrderId = 15 }
== APP - order-processor == Order received : Order { orderId = 16 }
== APP - checkout == Order passed: Order { OrderId = 16 }
== APP - order-processor == Order received : Order { orderId = 17 }
== APP - checkout == Order passed: Order { OrderId = 17 }
== APP - order-processor == Order received : Order { orderId = 18 }
== APP - checkout == Order passed: Order { OrderId = 18 }
== APP - order-processor == Order received : Order { orderId = 19 }
== APP - checkout == Order passed: Order { OrderId = 19 }
== APP - order-processor == Order received : Order { orderId = 20 }
== APP - checkout == Order passed: Order { OrderId = 20 }
Exited App successfully
在本教程中运行 dapr run -f .
使用 dapr.yaml
多应用程序运行模板文件启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用程序运行模板文件使用 dapr run -f .
运行 多应用程序运行模板文件 将启动项目中的所有应用程序。在本教程中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./order-processor/
appID: order-processor
appPort: 7001
command: ["dotnet", "run"]
- appID: checkout
appDirPath: ./checkout/
command: ["dotnet", "run"]
order-processor
服务order-processor
服务接收来自 checkout
服务的调用:
app.MapPost("/orders", (Order order) =>
{
Console.WriteLine("Order received : " + order);
return order.ToString();
});
checkout
服务在 checkout
服务的 Program.cs 文件中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("dapr-app-id", "order-processor");
var response = await client.PostAsync($"{baseURL}/orders", content);
Console.WriteLine("Order passed: " + order);
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门克隆目录的根目录,导航到快速入门目录。
cd service_invocation/java/http
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
mvn clean install
cd ../checkout
mvn clean install
cd ..
order-processor
和 checkout
服务使用以下命令,同时运行以下服务及其各自的 Dapr 边车:
order-processor
服务checkout
服务dapr run -f .
预期输出
== APP - order-processor == Order received : Order { orderId = 1 }
== APP - checkout == Order passed: Order { OrderId = 1 }
== APP - order-processor == Order received : Order { orderId = 2 }
== APP - checkout == Order passed: Order { OrderId = 2 }
== APP - order-processor == Order received : Order { orderId = 3 }
== APP - checkout == Order passed: Order { OrderId = 3 }
== APP - order-processor == Order received : Order { orderId = 4 }
== APP - checkout == Order passed: Order { OrderId = 4 }
== APP - order-processor == Order received : Order { orderId = 5 }
== APP - checkout == Order passed: Order { OrderId = 5 }
== APP - order-processor == Order received : Order { orderId = 6 }
== APP - checkout == Order passed: Order { OrderId = 6 }
== APP - order-processor == Order received : Order { orderId = 7 }
== APP - checkout == Order passed: Order { OrderId = 7 }
== APP - order-processor == Order received : Order { orderId = 8 }
== APP - checkout == Order passed: Order { OrderId = 8 }
== APP - order-processor == Order received : Order { orderId = 9 }
== APP - checkout == Order passed: Order { OrderId = 9 }
== APP - order-processor == Order received : Order { orderId = 10 }
== APP - checkout == Order passed: Order { OrderId = 10 }
== APP - order-processor == Order received : Order { orderId = 11 }
== APP - checkout == Order passed: Order { OrderId = 11 }
== APP - order-processor == Order received : Order { orderId = 12 }
== APP - checkout == Order passed: Order { OrderId = 12 }
== APP - order-processor == Order received : Order { orderId = 13 }
== APP - checkout == Order passed: Order { OrderId = 13 }
== APP - order-processor == Order received : Order { orderId = 14 }
== APP - checkout == Order passed: Order { OrderId = 14 }
== APP - order-processor == Order received : Order { orderId = 15 }
== APP - checkout == Order passed: Order { OrderId = 15 }
== APP - order-processor == Order received : Order { orderId = 16 }
== APP - checkout == Order passed: Order { OrderId = 16 }
== APP - order-processor == Order received : Order { orderId = 17 }
== APP - checkout == Order passed: Order { OrderId = 17 }
== APP - order-processor == Order received : Order { orderId = 18 }
== APP - checkout == Order passed: Order { OrderId = 18 }
== APP - order-processor == Order received : Order { orderId = 19 }
== APP - checkout == Order passed: Order { OrderId = 19 }
== APP - order-processor == Order received : Order { orderId = 20 }
== APP - checkout == Order passed: Order { OrderId = 20 }
Exited App successfully
在本教程中运行 dapr run -f .
使用 dapr.yaml
多应用程序运行模板文件启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用程序运行模板文件使用 dapr run -f .
运行 多应用程序运行模板文件 将启动项目中的所有应用程序。在本教程中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./order-processor/
appID: order-processor
appPort: 9001
command: ["java", "-jar", "target/OrderProcessingService-0.0.1-SNAPSHOT.jar"]
- appID: checkout
appDirPath: ./checkout/
command: ["java", "-jar", "target/CheckoutService-0.0.1-SNAPSHOT.jar"]
order-processor
服务order-processor
服务接收来自 checkout
服务的调用:
public String processOrders(@RequestBody Order body) {
System.out.println("Order received: "+ body.getOrderId());
return "CID" + body.getOrderId();
}
checkout
服务在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
.header("Content-Type", "application/json")
.header("dapr-app-id", "order-processor")
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Order passed: "+ orderId)
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门克隆目录的根目录,导航到快速入门目录。
cd service_invocation/go/http
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
go build .
cd ../checkout
go build .
cd ..
order-processor
和 checkout
服务使用以下命令,同时运行以下服务及其各自的 Dapr 边车:
order-processor
服务checkout
服务dapr run -f .
预期输出
== APP - order-processor == Order received : Order { orderId = 1 }
== APP - checkout == Order passed: Order { OrderId = 1 }
== APP - order-processor == Order received : Order { orderId = 2 }
== APP - checkout == Order passed: Order { OrderId = 2 }
== APP - order-processor == Order received : Order { orderId = 3 }
== APP - checkout == Order passed: Order { OrderId = 3 }
== APP - order-processor == Order received : Order { orderId = 4 }
== APP - checkout == Order passed: Order { OrderId = 4 }
== APP - order-processor == Order received : Order { orderId = 5 }
== APP - checkout == Order passed: Order { OrderId = 5 }
== APP - order-processor == Order received : Order { orderId = 6 }
== APP - checkout == Order passed: Order { OrderId = 6 }
== APP - order-processor == Order received : Order { orderId = 7 }
== APP - checkout == Order passed: Order { OrderId = 7 }
== APP - order-processor == Order received : Order { orderId = 8 }
== APP - checkout == Order passed: Order { OrderId = 8 }
== APP - order-processor == Order received : Order { orderId = 9 }
== APP - checkout == Order passed: Order { OrderId = 9 }
== APP - order-processor == Order received : Order { orderId = 10 }
== APP - checkout == Order passed: Order { OrderId = 10 }
== APP - order-processor == Order received : Order { orderId = 11 }
== APP - checkout == Order passed: Order { OrderId = 11 }
== APP - order-processor == Order received : Order { orderId = 12 }
== APP - checkout == Order passed: Order { OrderId = 12 }
== APP - order-processor == Order received : Order { orderId = 13 }
== APP - checkout == Order passed: Order { OrderId = 13 }
== APP - order-processor == Order received : Order { orderId = 14 }
== APP - checkout == Order passed: Order { OrderId = 14 }
== APP - order-processor == Order received : Order { orderId = 15 }
== APP - checkout == Order passed: Order { OrderId = 15 }
== APP - order-processor == Order received : Order { orderId = 16 }
== APP - checkout == Order passed: Order { OrderId = 16 }
== APP - order-processor == Order received : Order { orderId = 17 }
== APP - checkout == Order passed: Order { OrderId = 17 }
== APP - order-processor == Order received : Order { orderId = 18 }
== APP - checkout == Order passed: Order { OrderId = 18 }
== APP - order-processor == Order received : Order { orderId = 19 }
== APP - checkout == Order passed: Order { OrderId = 19 }
== APP - order-processor == Order received : Order { orderId = 20 }
== APP - checkout == Order passed: Order { OrderId = 20 }
Exited App successfully
在本教程中运行 dapr run -f .
使用 dapr.yaml
多应用程序运行模板文件启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用程序运行模板文件使用 dapr run -f .
运行 多应用程序运行模板文件 将启动项目中的所有应用程序。在本教程中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./order-processor/
appID: order-processor
appPort: 6006
command: ["go", "run", "."]
- appID: checkout
appDirPath: ./checkout/
command: ["go", "run", "."]
order-processor
服务在 order-processor
服务中,每个订单通过 HTTP POST 请求接收并由 getOrder
函数处理。
func getOrder(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
log.Printf("Order received : %s", string(data))
}
checkout
服务在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
req.Header.Add("dapr-app-id", "order-processor")
response, err := client.Do(req)
在继续本教程之前,请选择您偏好的编程语言。
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd service_invocation/python/http/order-processor
安装依赖项并构建应用程序:
pip3 install -r requirements.txt
在 Dapr 边车旁边运行 order-processor
服务。
dapr run --app-port 8001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- python3 app.py
注意:在 Windows 中,由于未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
@app.route('/orders', methods=['POST'])
def getOrder():
data = request.json
print('Order received : ' + json.dumps(data), flush=True)
return json.dumps({'success': True}), 200, {
'ContentType': 'application/json'}
app.run(port=8001)
checkout
服务在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd service_invocation/python/http/checkout
安装依赖项并构建应用程序:
pip3 install -r requirements.txt
在 Dapr 边车旁边运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- python3 app.py
注意:在 Windows 中,由于未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
headers = {'dapr-app-id': 'order-processor'}
result = requests.post(
url='%s/orders' % (base_url),
data=json.dumps(order),
headers=headers
)
您可以使用 多应用程序运行模板 运行本教程中的 Dapr 应用程序。无需为 order-processor
和 checkout
应用程序运行两个单独的 dapr run
命令,只需运行以下命令:
dapr run -f .
要停止所有应用程序,请运行:
dapr stop -f .
Dapr 在任何 Dapr 实例上调用应用程序。在代码中,边车编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。然后,Dapr 实例发现并相互通信。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd service_invocation/javascript/http/order-processor
安装依赖项:
npm install
在 Dapr 边车旁边运行 order-processor
服务。
dapr run --app-port 5001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- npm start
app.post('/orders', (req, res) => {
console.log("Order received:", req.body);
res.sendStatus(200);
});
checkout
服务在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd service_invocation/javascript/http/checkout
安装依赖项:
npm install
在 Dapr 边车旁边运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm start
在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
let axiosConfig = {
headers: {
"dapr-app-id": "order-processor"
}
};
const res = await axios.post(`${DAPR_HOST}:${DAPR_HTTP_PORT}/orders`, order , axiosConfig);
console.log("Order passed: " + res.config.data);
您可以使用 多应用程序运行模板 运行本教程中的 Dapr 应用程序。无需为 order-processor
和 checkout
应用程序运行两个单独的 dapr run
命令,只需运行以下命令:
dapr run -f .
要停止所有应用程序,请运行:
dapr stop -f .
Dapr 在任何 Dapr 实例上调用应用程序。在代码中,边车编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。然后,Dapr 实例发现并相互通信。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd service_invocation/csharp/http/order-processor
安装依赖项:
dotnet restore
dotnet build
在 Dapr 边车旁边运行 order-processor
服务。
dapr run --app-port 7001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- dotnet run
以下是订单处理器 Program.cs
文件中的工作代码块。
app.MapPost("/orders", (Order order) =>
{
Console.WriteLine("Order received : " + order);
return order.ToString();
});
checkout
服务在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd service_invocation/csharp/http/checkout
安装依赖项:
dotnet restore
dotnet build
在 Dapr 边车旁边运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- dotnet run
在 checkout
服务的 Program.cs 文件中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("dapr-app-id", "order-processor");
var response = await client.PostAsync($"{baseURL}/orders", content);
Console.WriteLine("Order passed: " + order);
您可以使用 多应用程序运行模板 运行本教程中的 Dapr 应用程序。无需为 order-processor
和 checkout
应用程序运行两个单独的 dapr run
命令,只需运行以下命令:
dapr run -f .
要停止所有应用程序,请运行:
dapr stop -f .
Dapr 在任何 Dapr 实例上调用应用程序。在代码中,边车编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。然后,Dapr 实例发现并相互通信。
checkout
服务输出:
== APP == Order passed: Order { OrderId: 1 }
== APP == Order passed: Order { OrderId: 2 }
== APP == Order passed: Order { OrderId: 3 }
== APP == Order passed: Order { OrderId: 4 }
== APP == Order passed: Order { OrderId: 5 }
== APP == Order passed: Order { OrderId: 6 }
== APP == Order passed: Order { OrderId: 7 }
== APP == Order passed: Order { OrderId: 8 }
== APP == Order passed: Order { OrderId: 9 }
== APP == Order passed: Order { OrderId: 10 }
order-processor
服务输出:
== APP == Order received: Order { OrderId: 1 }
== APP == Order received: Order { OrderId: 2 }
== APP == Order received: Order { OrderId: 3 }
== APP == Order received: Order { OrderId: 4 }
== APP == Order received: Order { OrderId: 5 }
== APP == Order received: Order { OrderId: 6 }
== APP == Order received: Order { OrderId: 7 }
== APP == Order received: Order { OrderId: 8 }
== APP == Order received: Order { OrderId: 9 }
== APP == Order received: Order { OrderId: 10 }
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd service_invocation/java/http/order-processor
安装依赖项:
mvn clean install
在 Dapr 边车旁边运行 order-processor
服务。
dapr run --app-id order-processor --app-port 9001 --app-protocol http --dapr-http-port 3501 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
public String processOrders(@RequestBody Order body) {
System.out.println("Order received: "+ body.getOrderId());
return "CID" + body.getOrderId();
}
checkout
服务在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd service_invocation/java/http/checkout
安装依赖项:
mvn clean install
在 Dapr 边车旁边运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar
在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
.header("Content-Type", "application/json")
.header("dapr-app-id", "order-processor")
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Order passed: "+ orderId)
您可以使用 多应用程序运行模板 运行本教程中的 Dapr 应用程序。无需为 order-processor
和 checkout
应用程序运行两个单独的 dapr run
命令,只需运行以下命令:
dapr run -f .
要停止所有应用程序,请运行:
dapr stop -f .
Dapr 在任何 Dapr 实例上调用应用程序。在代码中,边车编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。然后,Dapr 实例发现并相互通信。
checkout
服务输出:
== APP == Order passed: 1
== APP == Order passed: 2
== APP == Order passed: 3
== APP == Order passed: 4
== APP == Order passed: 5
== APP == Order passed: 6
== APP == Order passed: 7
== APP == Order passed: 8
== APP == Order passed: 9
== APP == Order passed: 10
order-processor
服务输出:
== APP == Order received: 1
== APP == Order received: 2
== APP == Order received: 3
== APP == Order received: 4
== APP == Order received: 5
== APP == Order received: 6
== APP == Order received: 7
== APP == Order received: 8
== APP == Order received: 9
== APP == Order received: 10
在此示例中,您需要:
克隆 快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd service_invocation/go/http/order-processor
安装依赖项:
go build .
在 Dapr 边车旁边运行 order-processor
服务。
dapr run --app-port 6006 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- go run .
每个订单通过 HTTP POST 请求接收并由 getOrder
函数处理。
func getOrder(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatal(err)
}
log.Printf("Order received : %s", string(data))
}
checkout
服务在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd service_invocation/go/http/checkout
安装依赖项:
go build .
在 Dapr 边车旁边运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- go run .
在 checkout
服务中,您会注意到无需重写应用程序代码即可使用 Dapr 的服务调用。您只需添加 dapr-app-id
头,该头指定目标服务的 ID,即可启用服务调用。
req.Header.Add("dapr-app-id", "order-processor")
response, err := client.Do(req)
您可以使用 多应用程序运行模板 运行本教程中的 Dapr 应用程序。无需为 order-processor
和 checkout
应用程序运行两个单独的 dapr run
命令,只需运行以下命令:
dapr run -f .
要停止所有应用程序,请运行:
dapr stop -f .
Dapr 在任何 Dapr 实例上调用应用程序。在代码中,边车编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。然后,Dapr 实例发现并相互通信。
checkout
服务输出:
== APP == Order passed: {"orderId":1}
== APP == Order passed: {"orderId":2}
== APP == Order passed: {"orderId":3}
== APP == Order passed: {"orderId":4}
== APP == Order passed: {"orderId":5}
== APP == Order passed: {"orderId":6}
== APP == Order passed: {"orderId":7}
== APP == Order passed: {"orderId":8}
== APP == Order passed: {"orderId":9}
== APP == Order passed: {"orderId":10}
order-processor
服务输出:
== APP == Order received : {"orderId":1}
== APP == Order received : {"orderId":2}
== APP == Order received : {"orderId":3}
== APP == Order received : {"orderId":4}
== APP == Order received : {"orderId":5}
== APP == Order received : {"orderId":6}
== APP == Order received : {"orderId":7}
== APP == Order received : {"orderId":8}
== APP == Order received : {"orderId":9}
== APP == Order received : {"orderId":10}
我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的 discord 频道进行讨论。
我们来了解一下 Dapr 的发布和订阅 (Pub/sub) 构建块。在这个快速入门中,您将运行发布者和订阅者微服务,以演示 Dapr 如何实现 Pub/sub 模式。
您可以通过以下两种方式尝试此 Pub/sub 快速入门:
在继续快速入门之前,请选择您偏好的 Dapr SDK 语言。
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门目录的根目录,导航到 pub/sub 目录:
cd pub_sub/python/sdk
为 order-processor
和 checkout
应用安装依赖项:
cd ./checkout
pip3 install -r requirements.txt
cd ..
cd ./order-processor
pip3 install -r requirements.txt
cd ..
cd ./order-processor-fastapi
pip3 install -r requirements.txt
cd ..
使用以下命令,同时运行以下服务及其各自的 Dapr sidecar:
order-processor
订阅者checkout
发布者dapr run -f .
注意:由于 Windows 中未定义 Python3.exe,您可能需要在运行
dapr run -f .
之前将dapr.yaml
文件中的python3
更改为python
。
预期输出
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
Exited App successfully
当您在 Dapr 安装期间运行 dapr init
时,以下 YAML 文件已在 .dapr/components
目录中生成:
在此快速入门中运行 dapr run -f .
启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../components/
apps:
- appID: order-processor-sdk
appDirPath: ./order-processor/
appPort: 6001
command: ["uvicorn", "app:app"]
- appID: checkout-sdk
appDirPath: ./checkout/
command: ["python3", "app.py"]
pubsub.yaml
组件文件使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在组件 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。order-processor
订阅者在 order-processor
订阅者中,您订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
# 注册 Dapr pub/sub 订阅
@app.route('/dapr/subscribe', methods=['GET'])
def subscribe():
subscriptions = [{
'pubsubname': 'orderpubsub',
'topic': 'orders',
'route': 'orders'
}]
print('Dapr pub/sub 已订阅: ' + json.dumps(subscriptions))
return jsonify(subscriptions)
# Dapr 订阅在 /dapr/subscribe 中设置此路由
@app.route('/orders', methods=['POST'])
def orders_subscriber():
event = from_http(request.headers, request.get_data())
print('订阅者收到: ' + event.data['orderid'], flush=True)
return json.dumps({'success': True}), 200, {
'ContentType': 'application/json'}
app.run(port=5001)
checkout
发布者在 checkout
发布者中,您将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
with DaprClient() as client:
# 使用 Dapr PubSub 发布事件/消息
result = client.publish_event(
pubsub_name='orderpubsub',
topic_name='orders',
data=json.dumps(order),
data_content_type='application/json',
)
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门目录的根目录,导航到 pub/sub 目录:
cd pub_sub/javascript/sdk
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
npm install
cd ..
cd ./checkout
npm install
cd ..
使用以下命令,同时运行以下服务及其各自的 Dapr sidecar:
order-processor
订阅者checkout
发布者dapr run -f .
预期输出
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
Exited App successfully
当您在 Dapr 安装期间运行 dapr init
时,以下 YAML 文件已在 .dapr/components
目录中生成:
在此快速入门中运行 dapr run -f .
启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../components/
apps:
- appID: order-processor
appDirPath: ./order-processor/
appPort: 5002
command: ["npm", "run", "start"]
- appID: checkout-sdk
appDirPath: ./checkout/
command: ["npm", "run", "start"]
pubsub.yaml
组件文件使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在组件 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。order-processor
订阅者在 order-processor
订阅者中,您订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
server.pubsub.subscribe("orderpubsub", "orders", (data) => console.log("Subscriber received: " + JSON.stringify(data)));
checkout
发布者在 checkout
发布者服务中,您将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
const client = new DaprClient();
await client.pubsub.publish(PUBSUB_NAME, PUBSUB_TOPIC, order);
console.log("Published data: " + JSON.stringify(order));
对于此示例,您将需要:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。仅 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门目录的根目录,导航到 pub/sub 目录:
cd pub_sub/csharp/sdk
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
dotnet restore
dotnet build
cd ../checkout
dotnet restore
dotnet build
cd ..
使用以下命令,同时运行以下服务及其各自的 Dapr sidecar:
order-processor
订阅者checkout
发布者dapr run -f .
预期输出
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
Exited App successfully
当您在 Dapr 安装期间运行 dapr init
时,以下 YAML 文件已在 .dapr/components
目录中生成:
在此快速入门中运行 dapr run -f .
启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../components/
apps:
- appID: order-processor
appDirPath: ./order-processor/
appPort: 7006
command: ["dotnet", "run"]
- appID: checkout-sdk
appDirPath: ./checkout/
command: ["dotnet", "run"]
pubsub.yaml
组件文件使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在组件 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。order-processor
订阅者在 order-processor
订阅者中,您订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
// Dapr 订阅在 [Topic] 中将 orders 主题路由到此路由
app.MapPost("/orders", [Topic("orderpubsub", "orders")] (Order order) => {
Console.WriteLine("订阅者收到: " + order);
return Results.Ok(order);
});
public record Order([property: JsonPropertyName("orderId")] int OrderId);
checkout
发布者在 checkout
发布者中,您将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
using var client = new DaprClientBuilder().Build();
await client.PublishEventAsync("orderpubsub", "orders", order);
Console.WriteLine("Published data: " + order);
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门目录的根目录,导航到 pub/sub 目录:
cd pub_sub/java/sdk
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
mvn clean install
cd ..
cd ./checkout
mvn clean install
cd ..
使用以下命令,同时运行以下服务及其各自的 Dapr sidecar:
order-processor
订阅者checkout
发布者dapr run -f .
预期输出
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
Exited App successfully
当您在 Dapr 安装期间运行 dapr init
时,以下 YAML 文件已在 .dapr/components
目录中生成:
在此快速入门中运行 dapr run -f .
启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../components/
apps:
- appID: order-processor-sdk
appDirPath: ./order-processor/target/
appPort: 8080
command: ["java", "-jar", "OrderProcessingService-0.0.1-SNAPSHOT.jar"]
- appID: checkout-sdk
appDirPath: ./checkout/target/
command: ["java", "-jar", "CheckoutService-0.0.1-SNAPSHOT.jar"]
pubsub.yaml
组件文件使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在组件 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。order-processor
订阅者在 order-processor
订阅者中,您订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
@Topic(name = "orders", pubsubName = "orderpubsub")
@PostMapping(path = "/orders", consumes = MediaType.ALL_VALUE)
public Mono<ResponseEntity> getCheckout(@RequestBody(required = false) CloudEvent<Order> cloudEvent) {
return Mono.fromSupplier(() -> {
try {
logger.info("订阅者收到: " + cloudEvent.getData().getOrderId());
return ResponseEntity.ok("SUCCESS");
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
checkout
发布者在 checkout
发布者中,您将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
DaprClient client = new DaprClientBuilder().build();
client.publishEvent(
PUBSUB_NAME,
TOPIC_NAME,
order).block();
logger.info("Published data: " + order.getOrderId());
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
从快速入门目录的根目录,导航到 pub/sub 目录:
cd pub_sub/go/sdk
为 order-processor
和 checkout
应用安装依赖项:
cd ./order-processor
go build .
cd ../checkout
go build .
cd ..
使用以下命令,同时运行以下服务及其各自的 Dapr sidecar:
order-processor
订阅者checkout
发布者dapr run -f .
预期输出
== APP - checkout-sdk == Published data: Order { OrderId = 1 }
== APP - order-processor == Subscriber received : Order { OrderId = 1 }
== APP - checkout-sdk == Published data: Order { OrderId = 2 }
== APP - order-processor == Subscriber received : Order { OrderId = 2 }
== APP - checkout-sdk == Published data: Order { OrderId = 3 }
== APP - order-processor == Subscriber received : Order { OrderId = 3 }
== APP - checkout-sdk == Published data: Order { OrderId = 4 }
== APP - order-processor == Subscriber received : Order { OrderId = 4 }
== APP - checkout-sdk == Published data: Order { OrderId = 5 }
== APP - order-processor == Subscriber received : Order { OrderId = 5 }
== APP - checkout-sdk == Published data: Order { OrderId = 6 }
== APP - order-processor == Subscriber received : Order { OrderId = 6 }
== APP - checkout-sdk == Published data: Order { OrderId = 7 }
== APP - order-processor == Subscriber received : Order { OrderId = 7 }
== APP - checkout-sdk == Published data: Order { OrderId = 8 }
== APP - order-processor == Subscriber received : Order { OrderId = 8 }
== APP - checkout-sdk == Published data: Order { OrderId = 9 }
== APP - order-processor == Subscriber received : Order { OrderId = 9 }
== APP - checkout-sdk == Published data: Order { OrderId = 10 }
== APP - order-processor == Subscriber received : Order { OrderId = 10 }
Exited App successfully
当您在 Dapr 安装期间运行 dapr init
时,以下 YAML 文件已在 .dapr/components
目录中生成:
在此快速入门中运行 dapr run -f .
启动了 订阅者 和 发布者 应用程序。
dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../components/
apps:
- appID: order-processor
appDirPath: ./order-processor/
appPort: 6005
command: ["go", "run", "."]
- appID: checkout-sdk
appDirPath: ./checkout/
command: ["go", "run", "."]
pubsub.yaml
组件文件使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在组件 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。order-processor
订阅者在 order-processor
订阅者中,您订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
fmt.Println("订阅者收到: ", e.Data)
return false, nil
}
checkout
发布者在 checkout
发布者中,您将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
client, err := dapr.NewClient()
if err := client.PublishEvent(ctx, PUBSUB_NAME, PUBSUB_TOPIC, []byte(order)); err != nil {
panic(err)
}
fmt.Println("Published data: ", order)
在继续快速入门之前,请选择您偏好的 Dapr SDK 语言。
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd pub_sub/python/sdk/order-processor
安装依赖项:
pip3 install -r requirements.txt
在 Dapr sidecar 的旁边运行 order-processor
订阅者服务。
dapr run --app-id order-processor --resources-path ../../../components/ --app-port 6002 -- python3 app.py
注意:由于 Windows 中未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
在 order-processor
订阅者中,我们订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
# 注册 Dapr pub/sub 订阅
@app.route('/dapr/subscribe', methods=['GET'])
def subscribe():
subscriptions = [{
'pubsubname': 'orderpubsub',
'topic': 'orders',
'route': 'orders'
}]
print('Dapr pub/sub 已订阅: ' + json.dumps(subscriptions))
return jsonify(subscriptions)
# Dapr 订阅在 /dapr/subscribe 中设置此路由
@app.route('/orders', methods=['POST'])
def orders_subscriber():
event = from_http(request.headers, request.get_data())
print('订阅者收到: ' + event.data['orderid'], flush=True)
return json.dumps({'success': True}), 200, {
'ContentType': 'application/json'}
app.run(port=5001)
在新的终端窗口中,导航到 checkout
目录。
cd pub_sub/python/sdk/checkout
安装依赖项:
pip3 install -r requirements.txt
在 Dapr sidecar 的旁边运行 checkout
发布者服务。
dapr run --app-id checkout --resources-path ../../../components/ -- python3 app.py
注意:由于 Windows 中未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
在 checkout
发布者中,我们将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
with DaprClient() as client:
# 使用 Dapr PubSub 发布事件/消息
result = client.publish_event(
pubsub_name='orderpubsub',
topic_name='orders',
data=json.dumps(order),
data_content_type='application/json',
)
发布者将订单发送到 Dapr sidecar,而订阅者接收它们。
发布者输出:
== APP == INFO:root:Published data: {"orderId": 1}
== APP == INFO:root:Published data: {"orderId": 2}
== APP == INFO:root:Published data: {"orderId": 3}
== APP == INFO:root:Published data: {"orderId": 4}
== APP == INFO:root:Published data: {"orderId": 5}
== APP == INFO:root:Published data: {"orderId": 6}
== APP == INFO:root:Published data: {"orderId": 7}
== APP == INFO:root:Published data: {"orderId": 8}
== APP == INFO:root:Published data: {"orderId": 9}
== APP == INFO:root:Published data: {"orderId": 10}
订阅者输出:
== APP == INFO:root:Subscriber received: {"orderId": 1}
== APP == INFO:root:Subscriber received: {"orderId": 2}
== APP == INFO:root:Subscriber received: {"orderId": 3}
== APP == INFO:root:Subscriber received: {"orderId": 4}
== APP == INFO:root:Subscriber received: {"orderId": 5}
== APP == INFO:root:Subscriber received: {"orderId": 6}
== APP == INFO:root:Subscriber received: {"orderId": 7}
== APP == INFO:root:Subscriber received: {"orderId": 8}
== APP == INFO:root:Subscriber received: {"orderId": 9}
== APP == INFO:root:Subscriber received: {"orderId": 10}
pubsub.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis pubsub.yaml
并在您的本地机器上运行一个 Redis 容器,位置:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd pub_sub/javascript/sdk/order-processor
安装依赖项,其中将包括来自 JavaScript SDK 的 @dapr/dapr
包:
npm install
验证服务目录中包含以下文件:
package.json
package-lock.json
在 Dapr sidecar 的旁边运行 order-processor
订阅者服务。
dapr run --app-port 5002 --app-id order-processing --app-protocol http --dapr-http-port 3501 --resources-path ../../../components -- npm run start
在 order-processor
订阅者中,我们订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
server.pubsub.subscribe("orderpubsub", "orders", (data) => console.log("Subscriber received: " + JSON.stringify(data)));
在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd pub_sub/javascript/sdk/checkout
安装依赖项,其中将包括来自 JavaScript SDK 的 @dapr/dapr
包:
npm install
验证服务目录中包含以下文件:
package.json
package-lock.json
在 Dapr sidecar 的旁边运行 checkout
发布者服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 --resources-path ../../../components -- npm run start
在 checkout
发布者服务中,我们将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
const client = new DaprClient();
await client.pubsub.publish(PUBSUB_NAME, PUBSUB_TOPIC, order);
console.log("Published data: " + JSON.stringify(order));
请注意,如上代码中所指定,发布者将一个随机数推送到 Dapr sidecar,而订阅者接收它。
发布者输出:
== APP == Published data: {"orderId":1}
== APP == Published data: {"orderId":2}
== APP == Published data: {"orderId":3}
== APP == Published data: {"orderId":4}
== APP == Published data: {"orderId":5}
== APP == Published data: {"orderId":6}
== APP == Published data: {"orderId":7}
== APP == Published data: {"orderId":8}
== APP == Published data: {"orderId":9}
== APP == Published data: {"orderId":10}
订阅者输出:
== APP == Subscriber received: {"orderId":1}
== APP == Subscriber received: {"orderId":2}
== APP == Subscriber received: {"orderId":3}
== APP == Subscriber received: {"orderId":4}
== APP == Subscriber received: {"orderId":5}
== APP == Subscriber received: {"orderId":6}
== APP == Subscriber received: {"orderId":7}
== APP == Subscriber received: {"orderId":8}
== APP == Subscriber received: {"orderId":9}
== APP == Subscriber received: {"orderId":10}
pubsub.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis pubsub.yaml
并在您的本地机器上运行一个 Redis 容器,位置:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd pub_sub/csharp/sdk/order-processor
恢复 NuGet 包:
dotnet restore
dotnet build
在 Dapr sidecar 的旁边运行 order-processor
订阅者服务。
dapr run --app-id order-processor --resources-path ../../../components --app-port 7006 -- dotnet run
在 order-processor
订阅者中,我们订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
// Dapr 订阅在 [Topic] 中将 orders 主题路由到此路由
app.MapPost("/orders", [Topic("orderpubsub", "orders")] (Order order) => {
Console.WriteLine("订阅者收到: " + order);
return Results.Ok(order);
});
public record Order([property: JsonPropertyName("orderId")] int OrderId);
在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd pub_sub/csharp/sdk/checkout
恢复 NuGet 包:
dotnet restore
dotnet build
在 Dapr sidecar 的旁边运行 checkout
发布者服务。
dapr run --app-id checkout --resources-path ../../../components -- dotnet run
在 checkout
发布者中,我们将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
using var client = new DaprClientBuilder().Build();
await client.PublishEventAsync("orderpubsub", "orders", order);
Console.WriteLine("Published data: " + order);
请注意,如上代码中所指定,发布者将一个随机数推送到 Dapr sidecar,而订阅者接收它。
发布者输出:
== APP == Published data: Order { OrderId = 1 }
== APP == Published data: Order { OrderId = 2 }
== APP == Published data: Order { OrderId = 3 }
== APP == Published data: Order { OrderId = 4 }
== APP == Published data: Order { OrderId = 5 }
== APP == Published data: Order { OrderId = 6 }
== APP == Published data: Order { OrderId = 7 }
== APP == Published data: Order { OrderId = 8 }
== APP == Published data: Order { OrderId = 9 }
== APP == Published data: Order { OrderId = 10 }
订阅者输出:
== APP == Subscriber received: Order { OrderId = 1 }
== APP == Subscriber received: Order { OrderId = 2 }
== APP == Subscriber received: Order { OrderId = 3 }
== APP == Subscriber received: Order { OrderId = 4 }
== APP == Subscriber received: Order { OrderId = 5 }
== APP == Subscriber received: Order { OrderId = 6 }
== APP == Subscriber received: Order { OrderId = 7 }
== APP == Subscriber received: Order { OrderId = 8 }
== APP == Subscriber received: Order { OrderId = 9 }
== APP == Subscriber received: Order { OrderId = 10 }
pubsub.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis pubsub.yaml
并在您的本地机器上运行一个 Redis 容器,位置:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd pub_sub/java/sdk/order-processor
安装依赖项:
mvn clean install
在 Dapr sidecar 的旁边运行 order-processor
订阅者服务。
dapr run --app-port 8080 --app-id order-processor --resources-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
在 order-processor
订阅者中,我们订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
@Topic(name = "orders", pubsubName = "orderpubsub")
@PostMapping(path = "/orders", consumes = MediaType.ALL_VALUE)
public Mono<ResponseEntity> getCheckout(@RequestBody(required = false) CloudEvent<Order> cloudEvent) {
return Mono.fromSupplier(() -> {
try {
logger.info("订阅者收到: " + cloudEvent.getData().getOrderId());
return ResponseEntity.ok("SUCCESS");
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd pub_sub/java/sdk/checkout
安装依赖项:
mvn clean install
在 Dapr sidecar 的旁边运行 checkout
发布者服务。
dapr run --app-id checkout --resources-path ../../../components -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar
在 checkout
发布者中,我们将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
DaprClient client = new DaprClientBuilder().build();
client.publishEvent(
PUBSUB_NAME,
TOPIC_NAME,
order).block();
logger.info("Published data: " + order.getOrderId());
请注意,如上代码中所指定,发布者将一个随机数推送到 Dapr sidecar,而订阅者接收它。
发布者输出:
== APP == 7194 [main] INFO com.service.CheckoutServiceApplication - Published data: 1
== APP == 12213 [main] INFO com.service.CheckoutServiceApplication - Published data: 2
== APP == 17233 [main] INFO com.service.CheckoutServiceApplication - Published data: 3
== APP == 22252 [main] INFO com.service.CheckoutServiceApplication - Published data: 4
== APP == 27276 [main] INFO com.service.CheckoutServiceApplication - Published data: 5
== APP == 32320 [main] INFO com.service.CheckoutServiceApplication - Published data: 6
== APP == 37340 [main] INFO com.service.CheckoutServiceApplication - Published data: 7
== APP == 42356 [main] INFO com.service.CheckoutServiceApplication - Published data: 8
== APP == 47386 [main] INFO com.service.CheckoutServiceApplication - Published data: 9
== APP == 52410 [main] INFO com.service.CheckoutServiceApplication - Published data: 10
订阅者输出:
== APP == 2022-03-07 13:31:19.551 INFO 43512 --- [nio-8080-exec-5] c.s.c.OrderProcessingServiceController : 订阅者收到: 1
== APP == 2022-03-07 13:31:19.552 INFO 43512 --- [nio-8080-exec-9] c.s.c.OrderProcessingServiceController : 订阅者收到: 2
== APP == 2022-03-07 13:31:19.551 INFO 43512 --- [nio-8080-exec-6] c.s.c.OrderProcessingServiceController : 订阅者收到: 3
== APP == 2022-03-07 13:31:19.552 INFO 43512 --- [nio-8080-exec-2] c.s.c.OrderProcessingServiceController : 订阅者收到: 4
== APP == 2022-03-07 13:31:19.553 INFO 43512 --- [nio-8080-exec-2] c.s.c.OrderProcessingServiceController : 订阅者收到: 5
== APP == 2022-03-07 13:31:19.553 INFO 43512 --- [nio-8080-exec-9] c.s.c.OrderProcessingServiceController : 订阅者收到: 6
== APP == 2022-03-07 13:31:22.849 INFO 43512 --- [nio-8080-exec-3] c.s.c.OrderProcessingServiceController : 订阅者收到: 7
== APP == 2022-03-07 13:31:27.866 INFO 43512 --- [nio-8080-exec-6] c.s.c.OrderProcessingServiceController : 订阅者收到: 8
== APP == 2022-03-07 13:31:32.895 INFO 43512 --- [nio-8080-exec-6] c.s.c.OrderProcessingServiceController : 订阅者收到: 9
== APP == 2022-03-07 13:31:37.919 INFO 43512 --- [nio-8080-exec-2] c.s.c.OrderProcessingServiceController : 订阅者收到: 10
pubsub.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis pubsub.yaml
并在您的本地机器上运行一个 Redis 容器,位置:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
scopes:
- orderprocessing
- checkout
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,从快速入门克隆目录的根目录导航到 order-processor
目录。
cd pub_sub/go/sdk/order-processor
安装依赖项并构建应用程序:
go build .
在 Dapr sidecar 的旁边运行 order-processor
订阅者服务。
dapr run --app-port 6005 --app-id order-processor-sdk --app-protocol http --dapr-http-port 3501 --resources-path ../../../components -- go run .
在 order-processor
订阅者中,我们订阅了名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
func eventHandler(ctx context.Context, e *common.TopicEvent) (retry bool, err error) {
fmt.Println("订阅者收到: ", e.Data)
return false, nil
}
在新的终端窗口中,从快速入门克隆目录的根目录导航到 checkout
目录。
cd pub_sub/go/sdk/checkout
安装依赖项并构建应用程序:
go build .
在 Dapr sidecar 的旁边运行 checkout
发布者服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 --resources-path ../../../components -- go run .
在 checkout
发布者中,我们将 orderId 消息发布到名为 orderpubsub
的 Redis 实例 (如 pubsub.yaml
组件中定义的) 和主题 orders
。服务一启动,它就会在循环中发布:
client, err := dapr.NewClient()
if err := client.PublishEvent(ctx, PUBSUB_NAME, PUBSUB_TOPIC, []byte(order)); err != nil {
panic(err)
}
fmt.Println("Published data: ", order)
请注意,如上代码中所指定,发布者将一个编号消息推送到 Dapr sidecar,而订阅者接收它。
发布者输出:
== APP == dapr client initializing for: 127.0.0.1:63293
== APP == Published data: {"orderId":1}
== APP == Published data: {"orderId":2}
== APP == Published data: {"orderId":3}
== APP == Published data: {"orderId":4}
== APP == Published data: {"orderId":5}
== APP == Published data: {"orderId":6}
== APP == Published data: {"orderId":7}
== APP == Published data: {"orderId":8}
== APP == Published data: {"orderId":9}
== APP == Published data: {"orderId":10}
订阅者输出:
== APP == 订阅者收到: {"orderId":1}
== APP == 订阅者收到: {"orderId":2}
== APP == 订阅者收到: {"orderId":3}
== APP == 订阅者收到: {"orderId":4}
== APP == 订阅者收到: {"orderId":5}
== APP == 订阅者收到: {"orderId":6}
== APP == 订阅者收到: {"orderId":7}
== APP == 订阅者收到: {"orderId":8}
== APP == 订阅者收到: {"orderId":9}
== APP == 订阅者收到: {"orderId":10}
注意:接收的顺序可能会有所不同。
pubsub.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis pubsub.yaml
并在您的本地机器上运行一个 Redis 容器,位置:
%UserProfile%\.dapr\components\pubsub.yaml
~/.dapr/components/pubsub.yaml
使用 pubsub.yaml
组件,您可以轻松地更换底层组件而无需更改应用程序代码。
此快速入门中包含的 Redis pubsub.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
scopes:
- orderprocessing
- checkout
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式。spec/metadata
定义了与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进的建议吗?
加入我们的 discord 频道 讨论。
让我们来了解一下 Dapr 的工作流构建块。在这个快速入门中,您将创建一个简单的控制台应用程序,演示 Dapr 的工作流编程模型和管理 API。
在本指南中,您将:
order-processor
应用程序。在继续快速入门之前,请选择您偏好的 Dapr SDK 语言版本。
order-processor
控制台应用程序启动并管理 order_processing_workflow
,该工作流模拟从商店购买商品。工作流由五个独特的工作流活动或任务组成:
notify_activity
:使用记录器在整个工作流过程中打印消息。这些消息通知您:process_payment_activity
:处理和授权付款。verify_inventory_activity
:检查状态存储以确保有足够的库存可供购买。update_inventory_activity
:从状态存储中移除请求的商品,并使用新的剩余库存值更新存储。request_approval_activity
:如果付款金额超过 50,000 美元,则寻求经理的批准。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在新的终端窗口中,导航到 order-processor
目录:
cd workflows/python/sdk/order-processor
安装 Dapr Python SDK 包:
pip3 install -r requirements.txt
返回到 python/sdk
目录:
cd ..
在终端中,使用 Multi-App Run 启动订单处理器应用程序及其 Dapr 边车。从 python/sdk
目录运行以下命令:
dapr run -f .
这将启动具有唯一工作流 ID 的 order-processor
应用程序并运行工作流活动。
预期输出:
== APP == Starting order workflow, purchasing 10 of cars
== APP == 2023-06-06 09:35:52.945 durabletask-worker INFO: Successfully connected to 127.0.0.1:65406. Waiting for work items...
== APP == INFO:NotifyActivity:Received order f4e1926e-3721-478d-be8a-f5bebd1995da for 10 cars at $150000 !
== APP == INFO:VerifyInventoryActivity:Verifying inventory for order f4e1926e-3721-478d-be8a-f5bebd1995da of 10 cars
== APP == INFO:VerifyInventoryActivity:There are 100 Cars available for purchase
== APP == INFO:RequestApprovalActivity:Requesting approval for payment of 165000 USD for 10 cars
== APP == 2023-06-06 09:36:05.969 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da Event raised: manager_approval
== APP == INFO:NotifyActivity:Payment for order f4e1926e-3721-478d-be8a-f5bebd1995da has been approved!
== APP == INFO:ProcessPaymentActivity:Processing payment: f4e1926e-3721-478d-be8a-f5bebd1995da for 10 cars at 150000 USD
== APP == INFO:ProcessPaymentActivity:Payment for request ID f4e1926e-3721-478d-be8a-f5bebd1995da processed successfully
== APP == INFO:UpdateInventoryActivity:Checking inventory for order f4e1926e-3721-478d-be8a-f5bebd1995da for 10 cars
== APP == INFO:UpdateInventoryActivity:There are now 90 cars left in stock
== APP == INFO:NotifyActivity:Order f4e1926e-3721-478d-be8a-f5bebd1995da has completed!
== APP == 2023-06-06 09:36:06.106 durabletask-worker INFO: f4e1926e-3721-478d-be8a-f5bebd1995da: Orchestration completed with status: COMPLETED
== APP == Workflow completed! Result: Completed
== APP == Purchase of item is Completed
运行 dapr init
会启动 openzipkin/zipkin Docker 容器。如果容器已停止运行,请使用以下命令启动 Zipkin Docker 容器:
docker run -d -p 9411:9411 openzipkin/zipkin
在 Zipkin Web UI 中查看工作流跟踪跨度(通常位于 http://localhost:9411/zipkin/
)。
当您运行 dapr run -f .
时:
f4e1926e-3721-478d-be8a-f5bebd1995da
),并调度了工作流。NotifyActivity
工作流活动发送通知,表示已收到 10 辆车的订单。ReserveInventoryActivity
工作流活动检查库存数据,确定您是否可以供应订购的商品,并响应库存中的汽车数量。ProcessPaymentActivity
工作流活动开始处理订单 f4e1926e-3721-478d-be8a-f5bebd1995da
的付款,并确认是否成功。UpdateInventoryActivity
工作流活动在订单处理后更新库存中的当前可用汽车。NotifyActivity
工作流活动发送通知,表示订单 f4e1926e-3721-478d-be8a-f5bebd1995da
已完成。order-processor/app.py
在应用程序的程序文件中,您将会:
class WorkflowConsoleApp:
def main(self):
# Register workflow and activities
workflowRuntime = WorkflowRuntime(settings.DAPR_RUNTIME_HOST, settings.DAPR_GRPC_PORT)
workflowRuntime.register_workflow(order_processing_workflow)
workflowRuntime.register_activity(notify_activity)
workflowRuntime.register_activity(requst_approval_activity)
workflowRuntime.register_activity(verify_inventory_activity)
workflowRuntime.register_activity(process_payment_activity)
workflowRuntime.register_activity(update_inventory_activity)
workflowRuntime.start()
print("==========Begin the purchase of item:==========", flush=True)
item_name = default_item_name
order_quantity = 10
total_cost = int(order_quantity) * baseInventory[item_name].per_item_cost
order = OrderPayload(item_name=item_name, quantity=int(order_quantity), total_cost=total_cost)
# Start Workflow
print(f'Starting order workflow, purchasing {order_quantity} of {item_name}', flush=True)
start_resp = daprClient.start_workflow(workflow_component=workflow_component,
workflow_name=workflow_name,
input=order)
_id = start_resp.instance_id
def prompt_for_approval(daprClient: DaprClient):
daprClient.raise_workflow_event(instance_id=_id, workflow_component=workflow_component,
event_name="manager_approval", event_data={'approval': True})
approval_seeked = False
start_time = datetime.now()
while True:
time_delta = datetime.now() - start_time
state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
if not state:
print("Workflow not found!") # not expected
elif state.runtime_status == "Completed" or\
state.runtime_status == "Failed" or\
state.runtime_status == "Terminated":
print(f'Workflow completed! Result: {state.runtime_status}', flush=True)
break
if time_delta.total_seconds() >= 10:
state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
if total_cost > 50000 and (
state.runtime_status != "Completed" or
state.runtime_status != "Failed" or
state.runtime_status != "Terminated"
) and not approval_seeked:
approval_seeked = True
threading.Thread(target=prompt_for_approval(daprClient), daemon=True).start()
print("Purchase of item is ", state.runtime_status, flush=True)
def restock_inventory(self, daprClient: DaprClient, baseInventory):
for key, item in baseInventory.items():
print(f'item: {item}')
item_str = f'{{"name": "{item.item_name}", "quantity": {item.quantity},\
"per_item_cost": {item.per_item_cost}}}'
daprClient.save_state("statestore-actors", key, item_str)
if __name__ == '__main__':
app = WorkflowConsoleApp()
app.main()
order-processor/workflow.py
在 workflow.py
中,工作流被定义为一个类,包含其所有相关任务(由工作流活动确定)。
def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: OrderPayload):
"""Defines the order processing workflow.
When the order is received, the inventory is checked to see if there is enough inventory to
fulfill the order. If there is enough inventory, the payment is processed and the inventory is
updated. If there is not enough inventory, the order is rejected.
If the total order is greater than $50,000, the order is sent to a manager for approval.
"""
order_id = ctx.instance_id
order_payload=json.loads(order_payload_str)
yield ctx.call_activity(notify_activity,
input=Notification(message=('Received order ' +order_id+ ' for '
+f'{order_payload["quantity"]}' +' ' +f'{order_payload["item_name"]}'
+' at $'+f'{order_payload["total_cost"]}' +' !')))
result = yield ctx.call_activity(verify_inventory_activity,
input=InventoryRequest(request_id=order_id,
item_name=order_payload["item_name"],
quantity=order_payload["quantity"]))
if not result.success:
yield ctx.call_activity(notify_activity,
input=Notification(message='Insufficient inventory for '
+f'{order_payload["item_name"]}'+'!'))
return OrderResult(processed=False)
if order_payload["total_cost"] > 50000:
yield ctx.call_activity(requst_approval_activity, input=order_payload)
approval_task = ctx.wait_for_external_event("manager_approval")
timeout_event = ctx.create_timer(timedelta(seconds=200))
winner = yield when_any([approval_task, timeout_event])
if winner == timeout_event:
yield ctx.call_activity(notify_activity,
input=Notification(message='Payment for order '+order_id
+' has been cancelled due to timeout!'))
return OrderResult(processed=False)
approval_result = yield approval_task
if approval_result["approval"]:
yield ctx.call_activity(notify_activity, input=Notification(
message=f'Payment for order {order_id} has been approved!'))
else:
yield ctx.call_activity(notify_activity, input=Notification(
message=f'Payment for order {order_id} has been rejected!'))
return OrderResult(processed=False)
yield ctx.call_activity(process_payment_activity, input=PaymentRequest(
request_id=order_id, item_being_purchased=order_payload["item_name"],
amount=order_payload["total_cost"], quantity=order_payload["quantity"]))
try:
yield ctx.call_activity(update_inventory_activity,
input=PaymentRequest(request_id=order_id,
item_being_purchased=order_payload["item_name"],
amount=order_payload["total_cost"],
quantity=order_payload["quantity"]))
except Exception:
yield ctx.call_activity(notify_activity,
input=Notification(message=f'Order {order_id} Failed!'))
return OrderResult(processed=False)
yield ctx.call_activity(notify_activity, input=Notification(
message=f'Order {order_id} has completed!'))
return OrderResult(processed=True)
order-processor
控制台应用程序启动并管理订单处理工作流的生命周期,该工作流在状态存储中存储和检索数据。工作流由四个工作流活动或任务组成:
notifyActivity
:使用记录器在整个工作流过程中打印消息。这些消息通知用户库存不足、付款无法处理等。reserveInventoryActivity
:检查状态存储以确保有足够的库存可供购买。requestApprovalActivity
:请求超过某个阈值的订单的批准processPaymentActivity
:处理和授权付款。updateInventoryActivity
:使用新的剩余库存值更新状态存储。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在新的终端窗口中,导航到 order-processor
目录:
cd workflows/javascript/sdk/order-processor
安装依赖项:
cd ./javascript/sdk
npm install
npm run build
在终端中,使用 Multi-App Run 启动订单处理器应用程序及其 Dapr 边车。从 javascript/sdk
目录运行以下命令:
dapr run -f .
这将启动具有唯一工作流 ID 的 order-processor
应用程序并运行工作流活动。
预期输出:
== APP - workflowApp == == APP == Orchestration scheduled with ID: 0c332155-1e02-453a-a333-28cfc7777642
== APP - workflowApp == == APP == Waiting 30 seconds for instance 0c332155-1e02-453a-a333-28cfc7777642 to complete...
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 0 history event...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, EXECUTIONSTARTED=1]
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Waiting for 1 task(s) and 0 event(s) to complete...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Returning 1 action(s)
== APP - workflowApp == == APP == Received "Activity Request" work item
== APP - workflowApp == == APP == Received order 0c332155-1e02-453a-a333-28cfc7777642 for 10 item1 at a total cost of 100
== APP - workflowApp == == APP == Activity notifyActivity completed with output undefined (0 chars)
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 3 history event...
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, TASKCOMPLETED=1]
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Waiting for 1 task(s) and 0 event(s) to complete...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Returning 1 action(s)
== APP - workflowApp == == APP == Received "Activity Request" work item
== APP - workflowApp == == APP == Reserving inventory for 0c332155-1e02-453a-a333-28cfc7777642 of 10 item1
== APP - workflowApp == == APP == 2024-02-16T03:15:59.498Z INFO [HTTPClient, HTTPClient] Sidecar Started
== APP - workflowApp == == APP == There are 100 item1 in stock
== APP - workflowApp == == APP == Activity reserveInventoryActivity completed with output {"success":true,"inventoryItem":{"perItemCost":100,"quantity":100,"itemName":"item1"}} (86 chars)
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 6 history event...
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, TASKCOMPLETED=1]
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Waiting for 1 task(s) and 0 event(s) to complete...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Returning 1 action(s)
== APP - workflowApp == == APP == Received "Activity Request" work item
== APP - workflowApp == == APP == Processing payment for order item1
== APP - workflowApp == == APP == Payment of 100 for 10 item1 processed successfully
== APP - workflowApp == == APP == Activity processPaymentActivity completed with output true (4 chars)
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 9 history event...
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, TASKCOMPLETED=1]
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Waiting for 1 task(s) and 0 event(s) to complete...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Returning 1 action(s)
== APP - workflowApp == == APP == Received "Activity Request" work item
== APP - workflowApp == == APP == Updating inventory for 0c332155-1e02-453a-a333-28cfc7777642 of 10 item1
== APP - workflowApp == == APP == Inventory updated for 0c332155-1e02-453a-a333-28cfc7777642, there are now 90 item1 in stock
== APP - workflowApp == == APP == Activity updateInventoryActivity completed with output {"success":true,"inventoryItem":{"perItemCost":100,"quantity":90,"itemName":"item1"}} (85 chars)
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 12 history event...
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, TASKCOMPLETED=1]
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Waiting for 1 task(s) and 0 event(s) to complete...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Returning 1 action(s)
== APP - workflowApp == == APP == Received "Activity Request" work item
== APP - workflowApp == == APP == order 0c332155-1e02-453a-a333-28cfc7777642 processed successfully!
== APP - workflowApp == == APP == Activity notifyActivity completed with output undefined (0 chars)
== APP - workflowApp == == APP == Received "Orchestrator Request" work item with instance id '0c332155-1e02-453a-a333-28cfc7777642'
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Rebuilding local state with 15 history event...
== APP - workflowApp == == APP == Processing order 0c332155-1e02-453a-a333-28cfc7777642...
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Processing 2 new history event(s): [ORCHESTRATORSTARTED=1, TASKCOMPLETED=1]
== APP - workflowApp == == APP == Order 0c332155-1e02-453a-a333-28cfc7777642 processed successfully!
== APP - workflowApp == == APP == 0c332155-1e02-453a-a333-28cfc7777642: Orchestration completed with status COMPLETED
== APP - workflowApp == time="2024-02-15T21:15:59.5589687-06:00" level=info msg="0c332155-1e02-453a-a333-28cfc7777642: 'orderProcessingWorkflow' completed with a COMPLETED status." app_id=activity-sequence-workflow instance=kaibocai-devbox scope=wfengine.backend type=log ver=1.12.4
== APP - workflowApp == == APP == Instance 0c332155-1e02-453a-a333-28cfc7777642 completed
运行 dapr init
会启动 openzipkin/zipkin Docker 容器。如果容器已停止运行,请使用以下命令启动 Zipkin Docker 容器:
docker run -d -p 9411:9411 openzipkin/zipkin
在 Zipkin Web UI 中查看工作流跟踪跨度(通常位于 http://localhost:9411/zipkin/
)。
当您运行 dapr run -f .
时:
0c332155-1e02-453a-a333-28cfc7777642
),并调度了工作流。notifyActivity
工作流活动发送通知,表示已收到 10 辆车的订单。reserveInventoryActivity
工作流活动检查库存数据,确定您是否可以供应订购的商品,并响应库存中的汽车数量。processPaymentActivity
工作流活动开始处理订单 0c332155-1e02-453a-a333-28cfc7777642
的付款,并确认是否成功。updateInventoryActivity
工作流活动在订单处理后更新库存中的当前可用汽车。notifyActivity
工作流活动发送通知,表示订单 0c332155-1e02-453a-a333-28cfc7777642
已完成。order-processor/workflowApp.ts
在应用程序文件中,您将会:
import { DaprWorkflowClient, WorkflowRuntime, DaprClient } from "@dapr/dapr-dev";
import { InventoryItem, OrderPayload } from "./model";
import { notifyActivity, orderProcessingWorkflow, processPaymentActivity, requestApprovalActivity, reserveInventoryActivity, updateInventoryActivity } from "./orderProcessingWorkflow";
async function start() {
// Update the gRPC client and worker to use a local address and port
const workflowClient = new DaprWorkflowClient();
const workflowWorker = new WorkflowRuntime();
const daprClient = new DaprClient();
const storeName = "statestore";
const inventory = new InventoryItem("item1", 100, 100);
const key = inventory.itemName;
await daprClient.state.save(storeName, [
{
key: key,
value: inventory,
}
]);
const order = new OrderPayload("item1", 100, 10);
workflowWorker
.registerWorkflow(orderProcessingWorkflow)
.registerActivity(notifyActivity)
.registerActivity(reserveInventoryActivity)
.registerActivity(requestApprovalActivity)
.registerActivity(processPaymentActivity)
.registerActivity(updateInventoryActivity);
// Wrap the worker startup in a try-catch block to handle any errors during startup
try {
await workflowWorker.start();
console.log("Workflow runtime started successfully");
} catch (error) {
console.error("Error starting workflow runtime:", error);
}
// Schedule a new orchestration
try {
const id = await workflowClient.scheduleNewWorkflow(orderProcessingWorkflow, order);
console.log(`Orchestration scheduled with ID: ${id}`);
// Wait for orchestration completion
const state = await workflowClient.waitForWorkflowCompletion(id, undefined, 30);
console.log(`Orchestration completed! Result: ${state?.serializedOutput}`);
} catch (error) {
console.error("Error scheduling or waiting for orchestration:", error);
throw error;
}
await workflowWorker.stop();
await workflowClient.stop();
}
start().catch((e) => {
console.error(e);
process.exit(1);
});
order-processor
控制台应用程序启动并管理订单处理工作流的生命周期,该工作流在状态存储中存储和检索数据。工作流由四个工作流活动或任务组成:
NotifyActivity
:使用记录器在整个工作流过程中打印消息ReserveInventoryActivity
:检查状态存储以确保有足够的库存可供购买ProcessPaymentActivity
:处理和授权付款UpdateInventoryActivity
:从状态存储中移除请求的商品,并使用新的剩余库存值更新存储对于此示例,您将需要:
注意: .NET 7 是 Dapr v1.15 中 Dapr.Workflows 支持的最低版本。只有 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在新的终端窗口中,导航到 order-processor
目录:
cd workflows/csharp/sdk/order-processor
安装依赖项:
dotnet restore
dotnet build
返回到 csharp/sdk
目录:
cd ..
在终端中,使用 Multi-App Run 启动订单处理器应用程序及其 Dapr 边车。从 csharp/sdk
目录运行以下命令:
dapr run -f .
这将启动具有唯一工作流 ID 的 order-processor
应用程序并运行工作流活动。
预期输出:
== APP == Starting workflow 6d2abcc9 purchasing 10 Cars
== APP == info: Microsoft.DurableTask.Client.Grpc.GrpcDurableTaskClient[40]
== APP == Scheduling new OrderProcessingWorkflow orchestration with instance ID '6d2abcc9' and 47 bytes of input data.
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
== APP == Received order 6d2abcc9 for 10 Cars at $15000
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
== APP == Reserving inventory for order 6d2abcc9 of 10 Cars
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
== APP == There are: 100, Cars available for purchase
== APP == Your workflow has started. Here is the status of the workflow: Dapr.Workflow.WorkflowState
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
== APP == Processing payment: 6d2abcc9 for 10 Cars at $15000
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
== APP == Payment for request ID '6d2abcc9' processed successfully
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
== APP == Checking Inventory for: Order# 6d2abcc9 for 10 Cars
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
== APP == There are now: 90 Cars left in stock
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
== APP == Order 6d2abcc9 has completed!
== APP == Workflow Status: Completed
运行 dapr init
会启动 openzipkin/zipkin Docker 容器。如果容器已停止运行,请使用以下命令启动 Zipkin Docker 容器:
docker run -d -p 9411:9411 openzipkin/zipkin
在 Zipkin Web UI 中查看工作流跟踪跨度(通常位于 http://localhost:9411/zipkin/
)。
当您运行 dapr run -f .
时:
6d2abcc9
),并调度了工作流。NotifyActivity
工作流活动发送通知,表示已收到 10 辆车的订单。ReserveInventoryActivity
工作流活动检查库存数据,确定您是否可以供应订购的商品,并响应库存中的汽车数量。ProcessPaymentActivity
工作流活动开始处理订单 6d2abcc9
的付款,并确认是否成功。UpdateInventoryActivity
工作流活动在订单处理后更新库存中的当前可用汽车。NotifyActivity
工作流活动发送通知,表示订单 6d2abcc9
已完成。order-processor/Program.cs
在应用程序的程序文件中,您将会:
using Dapr.Client;
using Dapr.Workflow;
//...
{
services.AddDaprWorkflow(options =>
{
// Note that it's also possible to register a lambda function as the workflow
// or activity implementation instead of a class.
options.RegisterWorkflow<OrderProcessingWorkflow>();
// These are the activities that get invoked by the workflow(s).
options.RegisterActivity<NotifyActivity>();
options.RegisterActivity<ReserveInventoryActivity>();
options.RegisterActivity<ProcessPaymentActivity>();
options.RegisterActivity<UpdateInventoryActivity>();
});
};
//...
// Generate a unique ID for the workflow
string orderId = Guid.NewGuid().ToString()[..8];
string itemToPurchase = "Cars";
int ammountToPurchase = 10;
// Construct the order
OrderPayload orderInfo = new OrderPayload(itemToPurchase, 15000, ammountToPurchase);
// Start the workflow
Console.WriteLine("Starting workflow {0} purchasing {1} {2}", orderId, ammountToPurchase, itemToPurchase);
await daprWorkflowClient.ScheduleNewWorkflowAsync(
name: nameof(OrderProcessingWorkflow),
input: orderInfo,
instanceId: orderId);
// Wait for the workflow to start and confirm the input
WorkflowState state = await daprWorkflowClient.WaitForWorkflowStartAsync(
instanceId: orderId);
Console.WriteLine($"{nameof(OrderProcessingWorkflow)} (ID = {orderId}) started successfully with {state.ReadInputAs<OrderPayload>()}");
// Wait for the workflow to complete
using var ctx = new CancellationTokenSource(TimeSpan.FromSeconds(5));
state = await daprClient.WaitForWorkflowCompletionAsync(
instanceId: orderId,
cancellation: ctx.Token);
Console.WriteLine("Workflow Status: {0}", state.ReadCustomStatusAs<string>());
order-processor/Workflows/OrderProcessingWorkflow.cs
在 OrderProcessingWorkflow.cs
中,工作流被定义为一个类,包含其所有相关任务(由工作流活动确定)。
using Dapr.Workflow;
//...
class OrderProcessingWorkflow : Workflow<OrderPayload, OrderResult>
{
public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
{
string orderId = context.InstanceId;
// Notify the user that an order has come through
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}"));
string requestId = context.InstanceId;
// Determine if there is enough of the item available for purchase by checking the inventory
InventoryResult result = await context.CallActivityAsync<InventoryResult>(
nameof(ReserveInventoryActivity),
new InventoryRequest(RequestId: orderId, order.Name, order.Quantity));
// If there is insufficient inventory, fail and let the user know
if (!result.Success)
{
// End the workflow here since we don't have sufficient inventory
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Insufficient inventory for {order.Name}"));
return new OrderResult(Processed: false);
}
// There is enough inventory available so the user can purchase the item(s). Process their payment
await context.CallActivityAsync(
nameof(ProcessPaymentActivity),
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
try
{
// There is enough inventory available so the user can purchase the item(s). Process their payment
await context.CallActivityAsync(
nameof(UpdateInventoryActivity),
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
}
catch (WorkflowTaskFailedException)
{
// Let them know their payment was processed
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Order {orderId} Failed! You are now getting a refund"));
return new OrderResult(Processed: false);
}
// Let them know their payment was processed
await context.CallActivityAsync(
nameof(NotifyActivity),
new Notification($"Order {orderId} has completed!"));
// End the workflow with a success result
return new OrderResult(Processed: true);
}
}
order-processor/Activities
目录Activities
目录包含工作流使用的四个工作流活动,定义在以下文件中:
NotifyActivity.cs
ReserveInventoryActivity.cs
ProcessPaymentActivity.cs
UpdateInventoryActivity.cs
order-processor
控制台应用程序启动并管理订单处理工作流的生命周期,该工作流在状态存储中存储和检索数据。工作流由四个工作流活动或任务组成:
NotifyActivity
:使用记录器在整个工作流过程中打印消息RequestApprovalActivity
:请求处理付款的批准ReserveInventoryActivity
:检查状态存储以确保有足够的库存可供购买ProcessPaymentActivity
:处理和授权付款UpdateInventoryActivity
:从状态存储中移除请求的商品,并使用新的剩余库存值更新存储对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
导航到 order-processor
目录:
cd workflows/java/sdk/order-processor
安装依赖项:
mvn clean install
返回到 java/sdk
目录:
cd ..
在终端中,使用 Multi-App Run 启动订单处理器应用程序及其 Dapr 边车。从 java/sdk
目录运行以下命令:
cd workflows/java/sdk
dapr run -f .
这将启动具有唯一工作流 ID 的 order-processor
应用程序并运行工作流活动。
预期输出:
== APP == *** Welcome to the Dapr Workflow console app sample!
== APP == *** Using this app, you can place orders that start workflows.
== APP == Start workflow runtime
== APP == Sep 20, 2023 3:23:05 PM com.microsoft.durabletask.DurableTaskGrpcWorker startAndBlock
== APP == INFO: Durable Task worker is connecting to sidecar at 127.0.0.1:50001.
== APP == ==========Begin the purchase of item:==========
== APP == Starting order workflow, purchasing 10 of cars
== APP == scheduled new workflow instance of OrderProcessingWorkflow with instance ID: edceba90-9c45-4be8-ad40-60d16e060797
== APP == [Thread-0] INFO io.dapr.workflows.WorkflowContext - Starting Workflow: io.dapr.quickstarts.workflows.OrderProcessingWorkflow
== APP == [Thread-0] INFO io.dapr.workflows.WorkflowContext - Instance ID(order ID): edceba90-9c45-4be8-ad40-60d16e060797
== APP == [Thread-0] INFO io.dapr.workflows.WorkflowContext - Current Orchestration Time: 2023-09-20T19:23:09.755Z
== APP == [Thread-0] INFO io.dapr.workflows.WorkflowContext - Received Order: OrderPayload [itemName=cars, totalCost=150000, quantity=10]
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.NotifyActivity - Received Order: OrderPayload [itemName=cars, totalCost=150000, quantity=10]
== APP == workflow instance edceba90-9c45-4be8-ad40-60d16e060797 started
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.ReserveInventoryActivity - Reserving inventory for order 'edceba90-9c45-4be8-ad40-60d16e060797' of 10 cars
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.ReserveInventoryActivity - There are 100 cars available for purchase
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.ReserveInventoryActivity - Reserved inventory for order 'edceba90-9c45-4be8-ad40-60d16e060797' of 10 cars
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.RequestApprovalActivity - Requesting approval for order: OrderPayload [itemName=cars, totalCost=150000, quantity=10]
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.RequestApprovalActivity - Approved requesting approval for order: OrderPayload [itemName=cars, totalCost=150000, quantity=10]
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.ProcessPaymentActivity - Processing payment: edceba90-9c45-4be8-ad40-60d16e060797 for 10 cars at $150000
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.ProcessPaymentActivity - Payment for request ID 'edceba90-9c45-4be8-ad40-60d16e060797' processed successfully
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.UpdateInventoryActivity - Updating inventory for order 'edceba90-9c45-4be8-ad40-60d16e060797' of 10 cars
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.UpdateInventoryActivity - Updated inventory for order 'edceba90-9c45-4be8-ad40-60d16e060797': there are now 90 cars left in stock
== APP == [Thread-0] INFO io.dapr.quickstarts.workflows.activities.NotifyActivity - Order completed! : edceba90-9c45-4be8-ad40-60d16e060797
== APP == workflow instance edceba90-9c45-4be8-ad40-60d16e060797 completed, out is: {"processed":true}
运行 dapr init
会启动 openzipkin/zipkin Docker 容器。如果容器已停止运行,请使用以下命令启动 Zipkin Docker 容器:
docker run -d -p 9411:9411 openzipkin/zipkin
在 Zipkin Web UI 中查看工作流跟踪跨度(通常位于 http://localhost:9411/zipkin/
)。
当您运行 dapr run -f .
时:
edceba90-9c45-4be8-ad40-60d16e060797
),并调度了工作流。NotifyActivity
工作流活动发送通知,表示已收到 10 辆车的订单。ReserveInventoryActivity
工作流活动检查库存数据,确定您是否可以供应订购的商品,并响应库存中的汽车数量。ProcessPaymentActivity
工作流活动开始处理订单 edceba90-9c45-4be8-ad40-60d16e060797
的付款,并确认是否成功。UpdateInventoryActivity
工作流活动在订单处理后更新库存中的当前可用汽车。NotifyActivity
工作流活动发送通知,表示订单 edceba90-9c45-4be8-ad40-60d16e060797
已完成。order-processor/WorkflowConsoleApp.java
在应用程序的程序文件中,您将会:
package io.dapr.quickstarts.workflows;
import io.dapr.client.DaprClient;
import io.dapr.client.DaprClientBuilder;
import io.dapr.workflows.client.DaprWorkflowClient;
public class WorkflowConsoleApp {
private static final String STATE_STORE_NAME = "statestore-actors";
// ...
public static void main(String[] args) throws Exception {
System.out.println("*** Welcome to the Dapr Workflow console app sample!");
System.out.println("*** Using this app, you can place orders that start workflows.");
// Wait for the sidecar to become available
Thread.sleep(5 * 1000);
// Register the OrderProcessingWorkflow and its activities with the builder.
WorkflowRuntimeBuilder builder = new WorkflowRuntimeBuilder().registerWorkflow(OrderProcessingWorkflow.class);
builder.registerActivity(NotifyActivity.class);
builder.registerActivity(ProcessPaymentActivity.class);
builder.registerActivity(RequestApprovalActivity.class);
builder.registerActivity(ReserveInventoryActivity.class);
builder.registerActivity(UpdateInventoryActivity.class);
// Build the workflow runtime
try (WorkflowRuntime runtime = builder.build()) {
System.out.println("Start workflow runtime");
runtime.start(false);
}
InventoryItem inventory = prepareInventoryAndOrder();
DaprWorkflowClient workflowClient = new DaprWorkflowClient();
try (workflowClient) {
executeWorkflow(workflowClient, inventory);
}
}
// Start the workflow runtime, pulling and executing tasks
private static void executeWorkflow(DaprWorkflowClient workflowClient, InventoryItem inventory) {
System.out.println("==========Begin the purchase of item:==========");
String itemName = inventory.getName();
int orderQuantity = inventory.getQuantity();
int totalcost = orderQuantity * inventory.getPerItemCost();
OrderPayload order = new OrderPayload();
order.setItemName(itemName);
order.setQuantity(orderQuantity);
order.setTotalCost(totalcost);
System.out.println("Starting order workflow, purchasing " + orderQuantity + " of " + itemName);
String instanceId = workflowClient.scheduleNewWorkflow(OrderProcessingWorkflow.class, order);
System.out.printf("scheduled new workflow instance of OrderProcessingWorkflow with instance ID: %s%n",
instanceId);
// Check workflow instance start status
try {
workflowClient.waitForInstanceStart(instanceId, Duration.ofSeconds(10), false);
System.out.printf("workflow instance %s started%n", instanceId);
} catch (TimeoutException e) {
System.out.printf("workflow instance %s did not start within 10 seconds%n", instanceId);
return;
}
// Check workflow instance complete status
try {
WorkflowInstanceStatus workflowStatus = workflowClient.waitForInstanceCompletion(instanceId,
Duration.ofSeconds(30),
true);
if (workflowStatus != null) {
System.out.printf("workflow instance %s completed, out is: %s %n", instanceId,
workflowStatus.getSerializedOutput());
} else {
System.out.printf("workflow instance %s not found%n", instanceId);
}
} catch (TimeoutException e) {
System.out.printf("workflow instance %s did not complete within 30 seconds%n", instanceId);
}
}
private static InventoryItem prepareInventoryAndOrder() {
// prepare 100 cars in inventory
InventoryItem inventory = new InventoryItem();
inventory.setName("cars");
inventory.setPerItemCost(15000);
inventory.setQuantity(100);
DaprClient daprClient = new DaprClientBuilder().build();
restockInventory(daprClient, inventory);
// prepare order for 10 cars
InventoryItem order = new InventoryItem();
order.setName("cars");
order.setPerItemCost(15000);
order.setQuantity(10);
return order;
}
private static void restockInventory(DaprClient daprClient, InventoryItem inventory) {
String key = inventory.getName();
daprClient.saveState(STATE_STORE_NAME, key, inventory).block();
}
}
OrderProcessingWorkflow.java
在 OrderProcessingWorkflow.java
中,工作流被定义为一个类,包含其所有相关任务(由工作流活动确定)。
package io.dapr.quickstarts.workflows;
import io.dapr.workflows.Workflow;
public class OrderProcessingWorkflow extends Workflow {
@Override
public WorkflowStub create() {
return ctx -> {
Logger logger = ctx.getLogger();
String orderId = ctx.getInstanceId();
logger.info("Starting Workflow: " + ctx.getName());
logger.info("Instance ID(order ID): " + orderId);
logger.info("Current Orchestration Time: " + ctx.getCurrentInstant());
OrderPayload order = ctx.getInput(OrderPayload.class);
logger.info("Received Order: " + order.toString());
OrderResult orderResult = new OrderResult();
orderResult.setProcessed(false);
// Notify the user that an order has come through
Notification notification = new Notification();
notification.setMessage("Received Order: " + order.toString());
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
// Determine if there is enough of the item available for purchase by checking
// the inventory
InventoryRequest inventoryRequest = new InventoryRequest();
inventoryRequest.setRequestId(orderId);
inventoryRequest.setItemName(order.getItemName());
inventoryRequest.setQuantity(order.getQuantity());
InventoryResult inventoryResult = ctx.callActivity(ReserveInventoryActivity.class.getName(),
inventoryRequest, InventoryResult.class).await();
// If there is insufficient inventory, fail and let the user know
if (!inventoryResult.isSuccess()) {
notification.setMessage("Insufficient inventory for order : " + order.getItemName());
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
ctx.complete(orderResult);
return;
}
// Require orders over a certain threshold to be approved
if (order.getTotalCost() > 5000) {
ApprovalResult approvalResult = ctx.callActivity(RequestApprovalActivity.class.getName(),
order, ApprovalResult.class).await();
if (approvalResult != ApprovalResult.Approved) {
notification.setMessage("Order " + order.getItemName() + " was not approved.");
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
ctx.complete(orderResult);
return;
}
}
// There is enough inventory available so the user can purchase the item(s).
// Process their payment
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.setRequestId(orderId);
paymentRequest.setItemBeingPurchased(order.getItemName());
paymentRequest.setQuantity(order.getQuantity());
paymentRequest.setAmount(order.getTotalCost());
boolean isOK = ctx.callActivity(ProcessPaymentActivity.class.getName(),
paymentRequest, boolean.class).await();
if (!isOK) {
notification.setMessage("Payment failed for order : " + orderId);
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
ctx.complete(orderResult);
return;
}
inventoryResult = ctx.callActivity(UpdateInventoryActivity.class.getName(),
inventoryRequest, InventoryResult.class).await();
if (!inventoryResult.isSuccess()) {
// If there is an error updating the inventory, refund the user
// paymentRequest.setAmount(-1 * paymentRequest.getAmount());
// ctx.callActivity(ProcessPaymentActivity.class.getName(),
// paymentRequest).await();
// Let users know their payment processing failed
notification.setMessage("Order failed to update inventory! : " + orderId);
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
ctx.complete(orderResult);
return;
}
// Let user know their order was processed
notification.setMessage("Order completed! : " + orderId);
ctx.callActivity(NotifyActivity.class.getName(), notification).await();
// Complete the workflow with order result is processed
orderResult.setProcessed(true);
ctx.complete(orderResult);
};
}
}
activities
目录Activities
目录包含工作流使用的四个工作流活动,定义在以下文件中:
order-processor
控制台应用程序启动并管理 OrderProcessingWorkflow
工作流,该工作流模拟从商店购买商品。工作流由五个独特的工作流活动或任务组成:
NotifyActivity
:使用记录器在整个工作流过程中打印消息。这些消息通知您:ProcessPaymentActivity
:处理和授权付款。VerifyInventoryActivity
:检查状态存储以确保有足够的库存可供购买。UpdateInventoryActivity
:从状态存储中移除请求的商品,并使用新的剩余库存值更新存储。RequestApprovalActivity
:如果付款金额超过 50,000 美元,则寻求经理的批准。对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在新的终端窗口中,导航到 sdk
目录:
cd workflows/go/sdk
在终端中,使用 Multi-App Run 启动订单处理器应用程序及其 Dapr 边车。从 go/sdk
目录运行以下命令:
dapr run -f .
这将启动具有唯一工作流 ID 的 order-processor
应用程序并运行工作流活动。
预期输出:
== APP - order-processor == *** Welcome to the Dapr Workflow console app sample!
== APP - order-processor == *** Using this app, you can place orders that start workflows.
== APP - order-processor == dapr client initializing for: 127.0.0.1:50056
== APP - order-processor == adding base stock item: paperclip
== APP - order-processor == 2024/02/01 12:59:52 work item listener started
== APP - order-processor == INFO: 2024/02/01 12:59:52 starting background processor
== APP - order-processor == adding base stock item: cars
== APP - order-processor == adding base stock item: computers
== APP - order-processor == ==========Begin the purchase of item:==========
== APP - order-processor == NotifyActivity: Received order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 cars - $150000
== APP - order-processor == VerifyInventoryActivity: Verifying inventory for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 of 10 cars
== APP - order-processor == VerifyInventoryActivity: There are 100 cars available for purchase
== APP - order-processor == RequestApprovalActivity: Requesting approval for payment of 150000USD for 10 cars
== APP - order-processor == NotifyActivity: Payment for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 has been approved!
== APP - order-processor == ProcessPaymentActivity: 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 - cars (150000USD)
== APP - order-processor == UpdateInventoryActivity: Checking Inventory for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 * cars
== APP - order-processor == UpdateInventoryActivity: There are now 90 cars left in stock
== APP - order-processor == NotifyActivity: Order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 has completed!
== APP - order-processor == Workflow completed - result: COMPLETED
== APP - order-processor == Purchase of item is complete
使用 CTRL+C
或以下命令停止 Dapr 工作流:
dapr stop -f .
运行 dapr init
会启动 openzipkin/zipkin Docker 容器。如果容器已停止运行,请使用以下命令启动 Zipkin Docker 容器:
docker run -d -p 9411:9411 openzipkin/zipkin
在 Zipkin Web UI 中查看工作流跟踪跨度(通常位于 http://localhost:9411/zipkin/
)。
当您运行 dapr run
时:
48ee83b7-5d80-48d5-97f9-6b372f5480a5
),并调度了工作流。NotifyActivity
工作流活动发送通知,表示已收到 10 辆车的订单。ReserveInventoryActivity
工作流活动检查库存数据,确定您是否可以供应订购的商品,并响应库存中的汽车数量。ProcessPaymentActivity
工作流活动开始处理订单 48ee83b7-5d80-48d5-97f9-6b372f5480a5
的付款,并确认是否成功。UpdateInventoryActivity
工作流活动在订单处理后更新库存中的当前可用汽车。NotifyActivity
工作流活动发送通知,表示订单 48ee83b7-5d80-48d5-97f9-6b372f5480a5
已完成。order-processor/main.go
在应用程序的程序文件中,您将会:
func main() {
fmt.Println("*** Welcome to the Dapr Workflow console app sample!")
fmt.Println("*** Using this app, you can place orders that start workflows.")
// ...
// Register workflow and activities
if err := w.RegisterWorkflow(OrderProcessingWorkflow); err != nil {
log.Fatal(err)
}
if err := w.RegisterActivity(NotifyActivity); err != nil {
log.Fatal(err)
}
if err := w.RegisterActivity(RequestApprovalActivity); err != nil {
log.Fatal(err)
}
if err := w.RegisterActivity(VerifyInventoryActivity); err != nil {
log.Fatal(err)
}
if err := w.RegisterActivity(ProcessPaymentActivity); err != nil {
log.Fatal(err)
}
if err := w.RegisterActivity(UpdateInventoryActivity); err != nil {
log.Fatal(err)
}
// Build and start workflow runtime, pulling and executing tasks
if err := w.Start(); err != nil {
log.Fatal(err)
}
daprClient, err := client.NewClient()
if err != nil {
log.Fatalf("failed to initialise dapr client: %v", err)
}
wfClient, err := workflow.NewClient(workflow.WithDaprClient(daprClient))
if err != nil {
log.Fatalf("failed to initialise workflow client: %v", err)
}
// Check inventory
inventory := []InventoryItem{
{ItemName: "paperclip", PerItemCost: 5, Quantity: 100},
{ItemName: "cars", PerItemCost: 15000, Quantity: 100},
{ItemName: "computers", PerItemCost: 500, Quantity: 100},
}
if err := restockInventory(daprClient, inventory); err != nil {
log.Fatalf("failed to restock: %v", err)
}
fmt.Println("==========Begin the purchase of item:==========")
itemName := defaultItemName
orderQuantity := 10
totalCost := inventory[1].PerItemCost * orderQuantity
orderPayload := OrderPayload{
ItemName: itemName,
Quantity: orderQuantity,
TotalCost: totalCost,
}
// Start workflow events, like receiving order, verifying inventory, and processing payment
id, err := wfClient.ScheduleNewWorkflow(context.Background(), workflowName, workflow.WithInput(orderPayload))
if err != nil {
log.Fatalf("failed to start workflow: %v", err)
}
// ...
// Notification that workflow has completed or failed
for {
timeDelta := time.Since(startTime)
metadata, err := wfClient.FetchWorkflowMetadata(context.Background(), id)
if err != nil {
log.Fatalf("failed to fetch workflow: %v", err)
}
if (metadata.RuntimeStatus == workflow.StatusCompleted) || (metadata.RuntimeStatus == workflow.StatusFailed) || (metadata.RuntimeStatus == workflow.StatusTerminated) {
fmt.Printf("Workflow completed - result: %v\n", metadata.RuntimeStatus.String())
break
}
if timeDelta.Seconds() >= 10 {
metadata, err := wfClient.FetchWorkflowMetadata(context.Background(), id)
if err != nil {
log.Fatalf("failed to fetch workflow: %v", err)
}
if totalCost > 50000 && !approvalSought && ((metadata.RuntimeStatus != workflow.StatusCompleted) || (metadata.RuntimeStatus != workflow.StatusFailed) || (metadata.RuntimeStatus != workflow.StatusTerminated)) {
approvalSought = true
promptForApproval(id)
}
}
// Sleep to not DoS the dapr dev instance
time.Sleep(time.Second)
}
fmt.Println("Purchase of item is complete")
}
// Request approval (RequestApprovalActivity)
func promptForApproval(id string) {
wfClient, err := workflow.NewClient()
if err != nil {
log.Fatalf("failed to initialise wfClient: %v", err)
}
if err := wfClient.RaiseEvent(context.Background(), id, "manager_approval"); err != nil {
log.Fatal(err)
}
}
// Update inventory for remaining stock (UpdateInventoryActivity)
func restockInventory(daprClient client.Client, inventory []InventoryItem) error {
for _, item := range inventory {
itemSerialized, err := json.Marshal(item)
if err != nil {
return err
}
fmt.Printf("adding base stock item: %s\n", item.ItemName)
if err := daprClient.SaveState(context.Background(), stateStoreName, item.ItemName, itemSerialized, nil); err != nil {
return err
}
}
return nil
}
同时,OrderProcessingWorkflow
及其活动在 workflow.go
中定义为方法
我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的Discord 频道进行讨论。
本文将介绍 Dapr 的状态管理模块。在本快速入门指南中,您将学习如何使用 Redis 状态存储来保存、获取和删除状态。您可以选择以下两种方式之一:
虽然本示例使用了 Redis,您也可以替换为其他支持的状态存储。
在开始之前,请选择您偏好的编程语言对应的 Dapr SDK。
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/python/sdk/order-processor
安装依赖项:
pip3 install -r requirements.txt
使用 多应用运行在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run -f .
注意:在 Windows 系统中,由于未定义 Python3.exe,您可能需要在运行
dapr run -f .
之前将dapr.yaml
文件中的python3
修改为python
。
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
with DaprClient() as client:
# 将状态保存到状态存储中
client.save_state(DAPR_STORE_NAME, orderId, str(order))
logging.info('Saving Order: %s', order)
# 从状态存储中获取状态
result = client.get_state(DAPR_STORE_NAME, orderId)
logging.info('Result after get: ' + str(result.data))
# 从状态存储中删除状态
client.delete_state(store_name=DAPR_STORE_NAME, key=orderId)
logging.info('Deleting Order: %s', order)
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == INFO:root:Saving Order: {'orderId': '1'}
== APP == INFO:root:Result after get: b"{'orderId': '1'}"
== APP == INFO:root:Deleting Order: {'orderId': '1'}
== APP == INFO:root:Saving Order: {'orderId': '2'}
== APP == INFO:root:Result after get: b"{'orderId': '2'}"
== APP == INFO:root:Deleting Order: {'orderId': '2'}
== APP == INFO:root:Saving Order: {'orderId': '3'}
== APP == INFO:root:Result after get: b"{'orderId': '3'}"
== APP == INFO:root:Deleting Order: {'orderId': '3'}
== APP == INFO:root:Saving Order: {'orderId': '4'}
== APP == INFO:root:Result after get: b"{'orderId': '4'}"
== APP == INFO:root:Deleting Order: {'orderId': '4'}
dapr.yaml
多应用运行模板文件当您运行 dapr init
时,Dapr 会创建一个名为 dapr.yaml
的默认多应用运行模板文件。运行 dapr run -f
会启动项目中的所有应用程序。在此示例中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../resources/
apps:
- appID: order-processor
appDirPath: ./order-processor/
command: ["python3" , "app.py"]
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 还会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/javascript/sdk/order-processor
安装依赖项:
npm install
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run -f .
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
const client = new DaprClient()
// 将状态保存到状态存储中
await client.state.save(DAPR_STATE_STORE_NAME, order)
console.log("Saving Order: ", order)
// 从状态存储中获取状态
const savedOrder = await client.state.get(DAPR_STATE_STORE_NAME, order.orderId)
console.log("Getting Order: ", savedOrder)
// 从状态存储中删除状态
await client.state.delete(DAPR_STATE_STORE_NAME, order.orderId)
console.log("Deleting Order: ", order)
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == > order-processor@1.0.0 start
== APP == > node index.js
== APP == Saving Order: { orderId: 1 }
== APP == Saving Order: { orderId: 2 }
== APP == Saving Order: { orderId: 3 }
== APP == Saving Order: { orderId: 4 }
== APP == Saving Order: { orderId: 5 }
== APP == Getting Order: { orderId: 1 }
== APP == Deleting Order: { orderId: 1 }
== APP == Getting Order: { orderId: 2 }
== APP == Deleting Order: { orderId: 2 }
== APP == Getting Order: { orderId: 3 }
== APP == Deleting Order: { orderId: 3 }
== APP == Getting Order: { orderId: 4 }
== APP == Deleting Order: { orderId: 4 }
== APP == Getting Order: { orderId: 5 }
== APP == Deleting Order: { orderId: 5 }
dapr.yaml
多应用运行模板文件当您运行 dapr init
时,Dapr 会创建一个名为 dapr.yaml
的默认多应用运行模板文件。运行 dapr run -f
会启动项目中的所有应用程序。在此示例中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../resources/
apps:
- appID: order-processor
appDirPath: ./order-processor/
command: ["npm", "run", "start"]
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。只有 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/csharp/sdk/order-processor
安装依赖项:
dotnet restore
dotnet build
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run -f .
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
var client = new DaprClientBuilder().Build();
// 将状态保存到状态存储中
await client.SaveStateAsync(DAPR_STORE_NAME, orderId.ToString(), order.ToString());
Console.WriteLine("Saving Order: " + order);
// 从状态存储中获取状态
var result = await client.GetStateAsync<string>(DAPR_STORE_NAME, orderId.ToString());
Console.WriteLine("Getting Order: " + result);
// 从状态存储中删除状态
await client.DeleteStateAsync(DAPR_STORE_NAME, orderId.ToString());
Console.WriteLine("Deleting Order: " + order);
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == Saving Order: Order { orderId = 1 }
== APP == Getting Order: Order { orderId = 1 }
== APP == Deleting Order: Order { orderId = 1 }
== APP == Saving Order: Order { orderId = 2 }
== APP == Getting Order: Order { orderId = 2 }
== APP == Deleting Order: Order { orderId = 2 }
== APP == Saving Order: Order { orderId = 3 }
== APP == Getting Order: Order { orderId = 3 }
== APP == Deleting Order: Order { orderId = 3 }
== APP == Saving Order: Order { orderId = 4 }
== APP == Getting Order: Order { orderId = 4 }
== APP == Deleting Order: Order { orderId = 4 }
== APP == Saving Order: Order { orderId = 5 }
== APP == Getting Order: Order { orderId = 5 }
== APP == Deleting Order: Order { orderId = 5 }
dapr.yaml
多应用运行模板文件当您运行 dapr init
时,Dapr 会创建一个名为 dapr.yaml
的默认多应用运行模板文件。运行 dapr run -f
会启动项目中的所有应用程序。在此示例中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../../resources/
apps:
- appID: order-processor
appDirPath: ./order-processor/
command: ["dotnet", "run"]
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/java/sdk/order-processor
安装依赖项:
mvn clean install
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run -f .
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 1; i <= 10; i++) {
int orderId = i;
Order order = new Order();
order.setOrderId(orderId);
// 将状态保存到状态存储中
client.saveState(DAPR_STATE_STORE, String.valueOf(orderId), order).block();
LOGGER.info("Saving Order: " + order.getOrderId());
// 从状态存储中获取状态
State<Order> response = client.getState(DAPR_STATE_STORE, String.valueOf(orderId), Order.class).block();
LOGGER.info("Getting Order: " + response.getValue().getOrderId());
// 从状态存储中删除状态
client.deleteState(DAPR_STATE_STORE, String.valueOf(orderId)).block();
LOGGER.info("Deleting Order: " + orderId);
TimeUnit.MILLISECONDS.sleep(1000);
}
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == INFO:root:Saving Order: {'orderId': '1'}
== APP == INFO:root:Result after get: b"{'orderId': '1'}"
== APP == INFO:root:Deleting Order: {'orderId': '1'}
== APP == INFO:root:Saving Order: {'orderId': '2'}
== APP == INFO:root:Result after get: b"{'orderId': '2'}"
== APP == INFO:root:Deleting Order: {'orderId': '2'}
== APP == INFO:root:Saving Order: {'orderId': '3'}
== APP == INFO:root:Result after get: b"{'orderId': '3'}"
== APP == INFO:root:Deleting Order: {'orderId': '3'}
== APP == INFO:root:Saving Order: {'orderId': '4'}
== APP == INFO:root:Result after get: b"{'orderId': '4'}"
== APP == INFO:root:Deleting Order: {'orderId': '4'}
dapr.yaml
多应用运行模板文件当您运行 dapr init
时,Dapr 会创建一个名为 dapr.yaml
的默认多应用运行模板文件。运行 dapr run -f
会启动项目中的所有应用程序。在此示例中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../resources/
apps:
- appID: order-processor
appDirPath: ./order-processor/
command: ["java", "-jar", "target/OrderProcessingService-0.0.1-SNAPSHOT.jar"]
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/go/sdk/order-processor
安装依赖项:
go build .
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run -f .
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
client, err := dapr.NewClient()
// 将状态保存到状态存储中
_ = client.SaveState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId), []byte(order))
log.Print("Saving Order: " + string(order))
// 从状态存储中获取状态
result, _ := client.GetState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId))
fmt.Println("Getting Order: " + string(result.Value))
// 从状态存储中删除状态
_ = client.DeleteState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId))
log.Print("Deleting Order: " + string(order))
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == dapr client initializing for: 127.0.0.1:53689
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":1}
== APP == Getting Order: {"orderId":1}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":1}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":2}
== APP == Getting Order: {"orderId":2}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":2}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":3}
== APP == Getting Order: {"orderId":3}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":3}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":4}
== APP == Getting Order: {"orderId":4}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":4}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":5}
== APP == Getting Order: {"orderId":5}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":5}
dapr.yaml
多应用运行模板文件当您运行 dapr init
时,Dapr 会创建一个名为 dapr.yaml
的默认多应用运行模板文件。运行 dapr run -f
会启动项目中的所有应用程序。在此示例中,dapr.yaml
文件包含以下内容:
version: 1
common:
resourcesPath: ../../resources/
apps:
- appID: order-processor
appDirPath: ./order-processor/
command: ["go", "run", "."]
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。在开始之前,请选择您偏好的编程语言对应的 Dapr SDK。
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/python/sdk/order-processor
安装依赖项:
pip3 install -r requirements.txt
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../resources/ -- python3 app.py
注意:在 Windows 系统中,由于未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
with DaprClient() as client:
# 将状态保存到状态存储中
client.save_state(DAPR_STORE_NAME, orderId, str(order))
logging.info('Saving Order: %s', order)
# 从状态存储中获取状态
result = client.get_state(DAPR_STORE_NAME, orderId)
logging.info('Result after get: ' + str(result.data))
# 从状态存储中删除状态
client.delete_state(store_name=DAPR_STORE_NAME, key=orderId)
logging.info('Deleting Order: %s', order)
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == INFO:root:Saving Order: {'orderId': '1'}
== APP == INFO:root:Result after get: b"{'orderId': '1'}"
== APP == INFO:root:Deleting Order: {'orderId': '1'}
== APP == INFO:root:Saving Order: {'orderId': '2'}
== APP == INFO:root:Result after get: b"{'orderId': '2'}"
== APP == INFO:root:Deleting Order: {'orderId': '2'}
== APP == INFO:root:Saving Order: {'orderId': '3'}
== APP == INFO:root:Result after get: b"{'orderId': '3'}"
== APP == INFO:root:Deleting Order: {'orderId': '3'}
== APP == INFO:root:Saving Order: {'orderId': '4'}
== APP == INFO:root:Result after get: b"{'orderId': '4'}"
== APP == INFO:root:Deleting Order: {'orderId': '4'}
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/javascript/sdk/order-processor
安装依赖项,其中将包括 JavaScript SDK 的 @dapr/dapr
包:
npm install
验证服务目录中包含以下文件:
package.json
package-lock.json
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../resources/ -- npm run start
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
const client = new DaprClient()
// 将状态保存到状态存储中
await client.state.save(DAPR_STATE_STORE_NAME, order)
console.log("Saving Order: ", order)
// 从状态存储中获取状态
const savedOrder = await client.state.get(DAPR_STATE_STORE_NAME, order.orderId)
console.log("Getting Order: ", savedOrder)
// 从状态存储中删除状态
await client.state.delete(DAPR_STATE_STORE_NAME, order.orderId)
console.log("Deleting Order: ", order)
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == > order-processor@1.0.0 start
== APP == > node index.js
== APP == Saving Order: { orderId: 1 }
== APP == Saving Order: { orderId: 2 }
== APP == Saving Order: { orderId: 3 }
== APP == Saving Order: { orderId: 4 }
== APP == Saving Order: { orderId: 5 }
== APP == Getting Order: { orderId: 1 }
== APP == Deleting Order: { orderId: 1 }
== APP == Getting Order: { orderId: 2 }
== APP == Deleting Order: { orderId: 2 }
== APP == Getting Order: { orderId: 3 }
== APP == Deleting Order: { orderId: 3 }
== APP == Getting Order: { orderId: 4 }
== APP == Deleting Order: { orderId: 4 }
== APP == Getting Order: { orderId: 5 }
== APP == Deleting Order: { orderId: 5 }
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/csharp/sdk/order-processor
回忆 NuGet 包:
dotnet restore
dotnet build
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../resources/ -- dotnet run
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
var client = new DaprClientBuilder().Build();
// 将状态保存到状态存储中
await client.SaveStateAsync(DAPR_STORE_NAME, orderId.ToString(), order.ToString());
Console.WriteLine("Saving Order: " + order);
// 从状态存储中获取状态
var result = await client.GetStateAsync<string>(DAPR_STORE_NAME, orderId.ToString());
Console.WriteLine("Getting Order: " + result);
// 从状态存储中删除状态
await client.DeleteStateAsync(DAPR_STORE_NAME, orderId.ToString());
Console.WriteLine("Deleting Order: " + order);
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == Saving Order: Order { orderId = 1 }
== APP == Getting Order: Order { orderId = 1 }
== APP == Deleting Order: Order { orderId = 1 }
== APP == Saving Order: Order { orderId = 2 }
== APP == Getting Order: Order { orderId = 2 }
== APP == Deleting Order: Order { orderId = 2 }
== APP == Saving Order: Order { orderId = 3 }
== APP == Getting Order: Order { orderId = 3 }
== APP == Deleting Order: Order { orderId = 3 }
== APP == Saving Order: Order { orderId = 4 }
== APP == Getting Order: Order { orderId = 4 }
== APP == Deleting Order: Order { orderId = 4 }
== APP == Saving Order: Order { orderId = 5 }
== APP == Getting Order: Order { orderId = 5 }
== APP == Deleting Order: Order { orderId = 5 }
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/java/sdk/order-processor
安装依赖项:
mvn clean install
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../resources -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
try (DaprClient client = new DaprClientBuilder().build()) {
for (int i = 1; i <= 10; i++) {
int orderId = i;
Order order = new Order();
order.setOrderId(orderId);
// 将状态保存到状态存储中
client.saveState(DAPR_STATE_STORE, String.valueOf(orderId), order).block();
LOGGER.info("Saving Order: " + order.getOrderId());
// 从状态存储中获取状态
State<Order> response = client.getState(DAPR_STATE_STORE, String.valueOf(orderId), Order.class).block();
LOGGER.info("Getting Order: " + response.getValue().getOrderId());
// 从状态存储中删除状态
client.deleteState(DAPR_STATE_STORE, String.valueOf(orderId)).block();
LOGGER.info("Deleting Order: " + orderId);
TimeUnit.MILLISECONDS.sleep(1000);
}
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == INFO:root:Saving Order: {'orderId': '1'}
== APP == INFO:root:Result after get: b"{'orderId': '1'}"
== APP == INFO:root:Deleting Order: {'orderId': '1'}
== APP == INFO:root:Saving Order: {'orderId': '2'}
== APP == INFO:root:Result after get: b"{'orderId': '2'}"
== APP == INFO:root:Deleting Order: {'orderId': '2'}
== APP == INFO:root:Saving Order: {'orderId': '3'}
== APP == INFO:root:Result after get: b"{'orderId': '3'}"
== APP == INFO:root:Deleting Order: {'orderId': '3'}
== APP == INFO:root:Saving Order: {'orderId': '4'}
== APP == INFO:root:Result after get: b"{'orderId': '4'}"
== APP == INFO:root:Deleting Order: {'orderId': '4'}
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端中,进入 order-processor
目录。
cd state_management/go/sdk/order-processor
安装依赖项并构建应用程序:
go build .
在 Dapr sidecar 旁边启动 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../resources -- go run .
order-processor
服务会将 orderId
键/值对写入、读取并删除到在 statestore.yaml
组件中定义的 statestore
实例中。服务启动后,会自动执行一个循环。
client, err := dapr.NewClient()
// 将状态保存到状态存储中
_ = client.SaveState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId), []byte(order))
log.Print("Saving Order: " + string(order))
// 从状态存储中获取状态
result, _ := client.GetState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId))
fmt.Println("Getting Order: " + string(result.Value))
// 从状态存储中删除状态
_ = client.DeleteState(ctx, STATE_STORE_NAME, strconv.Itoa(orderId))
log.Print("Deleting Order: " + string(order))
如上代码所示,应用程序会将状态保存在 Dapr 状态存储中,读取后再删除。
Order-processor 输出:
== APP == dapr client initializing for: 127.0.0.1:53689
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":1}
== APP == Getting Order: {"orderId":1}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":1}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":2}
== APP == Getting Order: {"orderId":2}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":2}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":3}
== APP == Getting Order: {"orderId":3}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":3}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":4}
== APP == Getting Order: {"orderId":4}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":4}
== APP == 2022/04/01 09:16:03 Saving Order: {"orderId":5}
== APP == Getting Order: {"orderId":5}
== APP == 2022/04/01 09:16:03 Deleting Order: {"orderId":5}
statestore.yaml
组件文件当您运行 dapr init
时,Dapr 会创建一个默认的 Redis statestore.yaml
并在本地机器上运行一个 Redis 容器,位置如下:
%UserProfile%\.dapr\components\statestore.yaml
~/.dapr/components/statestore.yaml
使用 statestore.yaml
组件,您可以轻松地替换状态存储而无需进行代码更改。
此快速入门中包含的 Redis 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: ""
- name: actorStateStore
value: "true"
在 YAML 文件中:
metadata/name
是您的应用程序与组件通信的方式(在代码示例中称为 DAPR_STORE_NAME
)。spec/metadata
定义了组件使用的 Redis 实例的连接。我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的discord 频道进行讨论。
我们来了解一下 Dapr 的绑定构建块。通过使用绑定,您可以:
在本快速入门中,您将使用输入 Cron 绑定每 10 秒调度一次批处理脚本。该脚本处理一个 JSON 文件,并使用 PostgreSQL Dapr 绑定将数据输出到 SQL 数据库。
在继续快速入门之前,请选择您偏好的 Dapr SDK 语言版本。
您需要准备以下环境:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在您的机器上通过 Docker 容器本地运行 PostgreSQL 实例。快速入门示例中包含一个 Docker Compose 文件,用于本地自定义、构建、运行和初始化带有默认 orders
表的 postgres
容器。
在终端窗口中,从快速入门克隆目录的根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令以设置容器:
docker compose up
验证容器是否在本地运行。
docker ps
输出应包括:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55305d1d378b postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp sql_db
在新的终端窗口中,导航到 SDK 目录。
cd bindings/python/sdk/batch
安装依赖项:
pip3 install -r requirements.txt
与 Dapr 边车一起运行 batch-sdk
服务。
dapr run --app-id batch-sdk --app-port 50051 --resources-path ../../../components -- python3 app.py
注意:在 Windows 中,由于未定义 Python3.exe,您可能需要使用
python app.py
而不是python3 app.py
。
process_batch
函数中的代码每 10 秒执行一次(在 components
目录中的 binding-cron.yaml
中定义)。绑定触发器通过 Dapr 边车在您的应用程序中查找通过 HTTP POST 调用的路由。
# 由 Dapr 输入绑定触发
@app.route('/' + cron_binding_name, methods=['POST'])
def process_batch():
batch-sdk
服务使用在 binding-postgresql.yaml
组件中定义的 PostgreSQL 输出绑定将 OrderId
、Customer
和 Price
记录插入到 orders
表中。
with DaprClient() as d:
sqlCmd = ('insert into orders (orderid, customer, price) values ' +
'(%s, \'%s\', %s)' % (order_line['orderid'],
order_line['customer'],
order_line['price']))
payload = {'sql': sqlCmd}
print(sqlCmd, flush=True)
try:
# 使用 Dapr 输出绑定通过 HTTP Post 插入订单
resp = d.invoke_binding(binding_name=sql_binding, operation='exec',
binding_metadata=payload, data='')
return resp
except Exception as e:
print(e, flush=True)
raise SystemExit(e)
请注意,如上所述,代码使用 OrderId
、Customer
和 Price
作为负载调用输出绑定。
您的输出绑定的 print
语句输出:
== APP == Processing batch..
== APP == insert into orders (orderid, customer, price) values (1, 'John Smith', 100.32)
== APP == insert into orders (orderid, customer, price) values (2, 'Jane Bond', 15.4)
== APP == insert into orders (orderid, customer, price) values (3, 'Tony James', 35.56)
== APP == Finished processing batch
在新的终端中,验证相同的数据已插入到数据库中。导航到 bindings/db
目录。
cd bindings/db
运行以下命令以启动交互式 psql CLI:
docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password
在 admin=#
提示符下,切换到 orders
表:
\c orders;
在 orders=#
提示符下,选择所有行:
select * from orders;
输出应如下所示:
orderid | customer | price
---------+------------+--------
1 | John Smith | 100.32
2 | Jane Bond | 15.4
3 | Tony James | 35.56
components\binding-cron.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
batch
)为此快速入门包含的 Cron binding-cron.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cron
namespace: quickstarts
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 10s" # 有效的 cron 调度
- name: direction
value: "input" # cron 绑定的方向
注意: binding-cron.yaml
的 metadata
部分包含一个 Cron 表达式,指定绑定被调用的频率。
component\binding-postgresql.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
binding-postgresql.yaml
文件中指定的设置连接到 PostgreSQL使用 binding-postgresql.yaml
组件,您可以轻松地更换后端数据库绑定,而无需进行代码更改。
为此快速入门包含的 PostgreSQL binding-postgresql.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgresql
version: v1
metadata:
- name: url # 必需
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
- name: direction
value: "output" # postgresql 绑定的方向
在 YAML 文件中:
spec/type
指定 PostgreSQL 用于此绑定。spec/metadata
定义组件使用的 PostgreSQL 实例的连接。您需要准备以下环境:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在您的机器上通过 Docker 容器本地运行 PostgreSQL 实例。快速入门示例中包含一个 Docker Compose 文件,用于本地自定义、构建、运行和初始化带有默认 orders
表的 postgres
容器。
在终端窗口中,从快速入门克隆目录的根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令以设置容器:
docker compose up
验证容器是否在本地运行。
docker ps
输出应包括:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55305d1d378b postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp sql_db
在新的终端窗口中,导航到 SDK 目录。
cd bindings/javascript/sdk/batch
安装依赖项:
npm install
与 Dapr 边车一起运行 batch-sdk
服务。
dapr run --app-id batch-sdk --app-port 5002 --dapr-http-port 3500 --resources-path ../../../components -- node index.js
process_batch
函数中的代码每 10 秒执行一次(在 components
目录中的 binding-cron.yaml
中定义)。绑定触发器通过 Dapr 边车在您的应用程序中查找通过 HTTP POST 调用的路由。
async function start() {
await server.binding.receive(cronBindingName,processBatch);
await server.start();
}
batch-sdk
服务使用在 binding-postgresql.yaml
组件中定义的 PostgreSQL 输出绑定将 OrderId
、Customer
和 Price
记录插入到 orders
表中。
async function processBatch(){
const loc = '../../orders.json';
fs.readFile(loc, 'utf8', (err, data) => {
const orders = JSON.parse(data).orders;
orders.forEach(order => {
let sqlCmd = `insert into orders (orderid, customer, price) values (${order.orderid}, '${order.customer}', ${order.price});`;
let payload = `{ "sql": "${sqlCmd}" } `;
console.log(payload);
client.binding.send(postgresBindingName, "exec", "", JSON.parse(payload));
});
console.log('Finished processing batch');
});
return 0;
}
请注意,如上所述,代码使用 OrderId
、Customer
和 Price
作为负载调用输出绑定。
您的输出绑定的 print
语句输出:
== APP == Processing batch..
== APP == insert into orders (orderid, customer, price) values(1, 'John Smith', 100.32)
== APP == insert into orders (orderid, customer, price) values(2, 'Jane Bond', 15.4)
== APP == insert into orders (orderid, customer, price) values(3, 'Tony James', 35.56)
在新的终端中,验证相同的数据已插入到数据库中。导航到 bindings/db
目录。
cd bindings/db
运行以下命令以启动交互式 Postgres CLI:
docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password
在 admin=#
提示符下,切换到 orders
表:
\c orders;
在 orders=#
提示符下,选择所有行:
select * from orders;
输出应如下所示:
orderid | customer | price
---------+------------+--------
1 | John Smith | 100.32
2 | Jane Bond | 15.4
3 | Tony James | 35.56
components\binding-cron.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
batch
)为此快速入门包含的 Cron binding-cron.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cron
namespace: quickstarts
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 10s" # 有效的 cron 调度
- name: direction
value: "input" # cron 绑定的方向
注意: binding-cron.yaml
的 metadata
部分包含一个 Cron 表达式,指定绑定被调用的频率。
component\binding-postgresql.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
binding-postgresql.yaml
文件中指定的设置连接到 PostgreSQL使用 binding-postgresql.yaml
组件,您可以轻松地更换后端数据库绑定,而无需进行代码更改。
为此快速入门包含的 PostgreSQL binding-postgresql.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgresql
version: v1
metadata:
- name: url # 必需
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
- name: direction
value: "output" # postgresql 绑定的方向
在 YAML 文件中:
spec/type
指定 PostgreSQL 用于此绑定。spec/metadata
定义组件使用的 PostgreSQL 实例的连接。您需要准备以下环境:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。仅 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在您的机器上通过 Docker 容器本地运行 PostgreSQL 实例。快速入门示例中包含一个 Docker Compose 文件,用于本地自定义、构建、运行和初始化带有默认 orders
表的 postgres
容器。
在终端窗口中,从快速入门克隆目录的根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令以设置容器:
docker compose up
验证容器是否在本地运行。
docker ps
输出应包括:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55305d1d378b postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp sql_db
在新的终端窗口中,导航到 SDK 目录。
cd bindings/csharp/sdk/batch
安装依赖项:
dotnet restore
dotnet build batch.csproj
与 Dapr 边车一起运行 batch-sdk
服务。
dapr run --app-id batch-sdk --app-port 7002 --resources-path ../../../components -- dotnet run
process_batch
函数中的代码每 10 秒执行一次(在 components
目录中的 binding-cron.yaml
中定义)。绑定触发器通过 Dapr 边车在您的应用程序中查找通过 HTTP POST 调用的路由。
app.MapPost("/" + cronBindingName, async () => {
// ...
});
batch-sdk
服务使用在 binding-postgresql.yaml
组件中定义的 PostgreSQL 输出绑定将 OrderId
、Customer
和 Price
记录插入到 orders
表中。
// ...
string jsonFile = File.ReadAllText("../../../orders.json");
var ordersArray = JsonSerializer.Deserialize<Orders>(jsonFile);
using var client = new DaprClientBuilder().Build();
foreach(Order ord in ordersArray?.orders ?? new Order[] {}){
var sqlText = $"insert into orders (orderid, customer, price) values ({ord.OrderId}, '{ord.Customer}', {ord.Price});";
var command = new Dictionary<string,string>(){
{"sql",
sqlText}
};
// ...
}
// 使用 Dapr 输出绑定通过 Dapr Client SDK 插入订单
await client.InvokeBindingAsync(bindingName: sqlBindingName, operation: "exec", data: "", metadata: command);
请注意,如上所述,代码使用 OrderId
、Customer
和 Price
作为负载调用输出绑定。
您的输出绑定的 print
语句输出:
== APP == Processing batch..
== APP == insert into orders (orderid, customer, price) values (1, 'John Smith', 100.32);
== APP == insert into orders (orderid, customer, price) values (2, 'Jane Bond', 15.4);
== APP == insert into orders (orderid, customer, price) values (3, 'Tony James', 35.56);
== APP == Finished processing batch
在新的终端中,验证相同的数据已插入到数据库中。导航到 bindings/db
目录。
cd bindings/db
运行以下命令以启动交互式 Postgres CLI:
docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password
在 admin=#
提示符下,切换到 orders
表:
\c orders;
在 orders=#
提示符下,选择所有行:
select * from orders;
输出应如下所示:
orderid | customer | price
---------+------------+--------
1 | John Smith | 100.32
2 | Jane Bond | 15.4
3 | Tony James | 35.56
components\binding-cron.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
batch
)为此快速入门包含的 Cron binding-cron.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cron
namespace: quickstarts
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 10s" # 有效的 cron 调度
- name: direction
value: "input" # cron 绑定的方向
注意: binding-cron.yaml
的 metadata
部分包含一个 Cron 表达式,指定绑定被调用的频率。
component\binding-postgresql.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
binding-postgresql.yaml
文件中指定的设置连接到 PostgreSQL使用 binding-postgresql.yaml
组件,您可以轻松地更换后端数据库绑定,而无需进行代码更改。
为此快速入门包含的 PostgreSQL binding-postgresql.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgresql
version: v1
metadata:
- name: url # 必需
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
- name: direction
value: "output" # postgresql 绑定的方向
在 YAML 文件中:
spec/type
指定 PostgreSQL 用于此绑定。spec/metadata
定义组件使用的 PostgreSQL 实例的连接。您需要准备以下环境:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在您的机器上通过 Docker 容器本地运行 PostgreSQL 实例。快速入门示例中包含一个 Docker Compose 文件,用于本地自定义、构建、运行和初始化带有默认 orders
表的 postgres
容器。
在终端窗口中,从快速入门克隆目录的根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令以设置容器:
docker compose up
验证容器是否在本地运行。
docker ps
输出应包括:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55305d1d378b postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp sql_db
在新的终端窗口中,导航到 SDK 目录。
cd bindings/java/sdk/batch
安装依赖项:
mvn clean install
与 Dapr 边车一起运行 batch-sdk
服务。
dapr run --app-id batch-sdk --app-port 8080 --resources-path ../../../components -- java -jar target/BatchProcessingService-0.0.1-SNAPSHOT.jar
process_batch
函数中的代码每 10 秒执行一次(在 components
目录中的 binding-cron.yaml
中定义)。绑定触发器通过 Dapr 边车在您的应用程序中查找通过 HTTP POST 调用的路由。
@PostMapping(path = cronBindingPath, consumes = MediaType.ALL_VALUE)
public ResponseEntity<String> processBatch() throws IOException, Exception
batch-sdk
服务使用在 binding-postgresql.yaml
组件中定义的 PostgreSQL 输出绑定将 OrderId
、Customer
和 Price
记录插入到 orders
表中。
try (DaprClient client = new DaprClientBuilder().build()) {
for (Order order : ordList.orders) {
String sqlText = String.format(
"insert into orders (orderid, customer, price) " +
"values (%s, '%s', %s);",
order.orderid, order.customer, order.price);
logger.info(sqlText);
Map<String, String> metadata = new HashMap<String, String>();
metadata.put("sql", sqlText);
// 使用 Dapr SDK 调用 sql 输出绑定
client.invokeBinding(sqlBindingName, "exec", null, metadata).block();
}
logger.info("Finished processing batch");
return ResponseEntity.ok("Finished processing batch");
}
请注意,如上所述,代码使用 OrderId
、Customer
和 Price
作为负载调用输出绑定。
您的输出绑定的 print
语句输出:
== APP == 2022-06-22 16:39:17.012 INFO 35772 --- [nio-8080-exec-4] c.s.c.BatchProcessingServiceController : Processing batch..
== APP == 2022-06-22 16:39:17.268 INFO 35772 --- [nio-8080-exec-4] c.s.c.BatchProcessingServiceController : insert into orders (orderid, customer, price) values (1, 'John Smith', 100.32);
== APP == 2022-06-22 16:39:17.838 INFO 35772 --- [nio-8080-exec-4] c.s.c.BatchProcessingServiceController : insert into orders (orderid, customer, price) values (2, 'Jane Bond', 15.4);
== APP == 2022-06-22 16:39:17.844 INFO 35772 --- [nio-8080-exec-4] c.s.c.BatchProcessingServiceController : insert into orders (orderid, customer, price) values (3, 'Tony James', 35.56);
== APP == 2022-06-22 16:39:17.848 INFO 35772 --- [nio-8080-exec-4] c.s.c.BatchProcessingServiceController : Finished processing batch
在新的终端中,验证相同的数据已插入到数据库中。导航到 bindings/db
目录。
cd bindings/db
运行以下命令以启动交互式 Postgres CLI:
docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password
在 admin=#
提示符下,切换到 orders
表:
\c orders;
在 orders=#
提示符下,选择所有行:
select * from orders;
输出应如下所示:
orderid | customer | price
---------+------------+--------
1 | John Smith | 100.32
2 | Jane Bond | 15.4
3 | Tony James | 35.56
components\binding-cron.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
batch
)为此快速入门包含的 Cron binding-cron.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cron
namespace: quickstarts
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 10s" # 有效的 cron 调度
- name: direction
value: "input" # cron 绑定的方向
注意: binding-cron.yaml
的 metadata
部分包含一个 Cron 表达式,指定绑定被调用的频率。
component\binding-postgresql.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
binding-postgresql.yaml
文件中指定的设置连接到 PostgreSQL使用 binding-postgresql.yaml
组件,您可以轻松地更换后端数据库绑定,而无需进行代码更改。
为此快速入门包含的 PostgreSQL binding-postgresql.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgresql
version: v1
metadata:
- name: url # 必需
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
- name: direction
value: "output" # postgresql 绑定的方向
在 YAML 文件中:
spec/type
指定 PostgreSQL 用于此绑定。spec/metadata
定义组件使用的 PostgreSQL 实例的连接。您需要准备以下环境:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在您的机器上通过 Docker 容器本地运行 PostgreSQL 实例。快速入门示例中包含一个 Docker Compose 文件,用于本地自定义、构建、运行和初始化带有默认 orders
表的 postgres
容器。
在终端窗口中,从快速入门克隆目录的根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令以设置容器:
docker compose up
验证容器是否在本地运行。
docker ps
输出应包括:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
55305d1d378b postgres "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp sql_db
在新的终端窗口中,导航到 SDK 目录。
cd bindings/go/sdk/batch
安装依赖项:
go build .
与 Dapr 边车一起运行 batch-sdk
服务。
dapr run --app-id batch-sdk --app-port 6002 --dapr-http-port 3502 --dapr-grpc-port 60002 --resources-path ../../../components -- go run .
process_batch
函数中的代码每 10 秒执行一次(在 components
目录中的 binding-cron.yaml
中定义)。绑定触发器通过 Dapr 边车在您的应用程序中查找通过 HTTP POST 调用的路由。
// 由 Dapr 输入绑定触发
r.HandleFunc("/"+cronBindingName, processBatch).Methods("POST")
batch-sdk
服务使用在 binding-postgresql.yaml
组件中定义的 PostgreSQL 输出绑定将 OrderId
、Customer
和 Price
记录插入到 orders
表中。
func sqlOutput(order Order) (err error) {
client, err := dapr.NewClient()
if err != nil {
return err
}
ctx := context.Background()
sqlCmd := fmt.Sprintf("insert into orders (orderid, customer, price) values (%d, '%s', %s);", order.OrderId, order.Customer, strconv.FormatFloat(order.Price, 'f', 2, 64))
fmt.Println(sqlCmd)
// 使用 Dapr 输出绑定通过 Dapr SDK 插入订单
in := &dapr.InvokeBindingRequest{
Name: sqlBindingName,
Operation: "exec",
Data: []byte(""),
Metadata: map[string]string{"sql": sqlCmd},
}
err = client.InvokeOutputBinding(ctx, in)
if err != nil {
return err
}
return nil
}
请注意,如上所述,代码使用 OrderId
、Customer
和 Price
作为负载调用输出绑定。
您的输出绑定的 print
语句输出:
== APP == Processing batch..
== APP == insert into orders (orderid, customer, price) values(1, 'John Smith', 100.32)
== APP == insert into orders (orderid, customer, price) values(2, 'Jane Bond', 15.4)
== APP == insert into orders (orderid, customer, price) values(3, 'Tony James', 35.56)
在新的终端中,验证相同的数据已插入到数据库中。导航到 bindings/db
目录。
cd bindings/db
运行以下命令以启动交互式 Postgres CLI:
docker exec -i -t postgres psql --username postgres -p 5432 -h localhost --no-password
在 admin=#
提示符下,切换到 orders
表:
\c orders;
在 orders=#
提示符下,选择所有行:
select * from orders;
输出应如下所示:
orderid | customer | price
---------+------------+--------
1 | John Smith | 100.32
2 | Jane Bond | 15.4
3 | Tony James | 35.56
components\binding-cron.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
batch
)为此快速入门包含的 Cron binding-cron.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cron
namespace: quickstarts
spec:
type: bindings.cron
version: v1
metadata:
- name: schedule
value: "@every 10s" # 有效的 cron 调度
- name: direction
value: "input" # cron 绑定的方向
注意: binding-cron.yaml
的 metadata
部分包含一个 Cron 表达式,指定绑定被调用的频率。
component\binding-postgresql.yaml
组件文件当您执行 dapr run
命令并指定组件路径时,Dapr 边车:
binding-postgresql.yaml
文件中指定的设置连接到 PostgreSQL使用 binding-postgresql.yaml
组件,您可以轻松地更换后端数据库绑定,而无需进行代码更改。
为此快速入门包含的 PostgreSQL binding-postgresql.yaml
文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgresql
version: v1
metadata:
- name: url # 必需
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
- name: direction
value: "output" # postgresql 绑定的方向
在 YAML 文件中:
spec/type
指定 PostgreSQL 用于此绑定。spec/metadata
定义组件使用的 PostgreSQL 实例的连接。我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的 discord 频道进行讨论。
我们来了解一下 Dapr 的 Actors 构建模块。在这个快速入门中,您将运行一个智能设备微服务和一个简单的控制台客户端,以演示 Dapr Actors 中的有状态对象模式。
目前,您可以通过 .NET SDK 体验这个 actors 快速入门。
以下是 .NET actors 快速入门的简要概述:
SmartDevice.Service
微服务,您将托管:SmokeDetectorActor
烟雾报警对象ControllerActor
对象,用于指挥和控制智能设备SmartDevice.Client
控制台应用程序,客户端应用程序与每个 actor 或控制器交互,以执行聚合操作。SmartDevice.Interfaces
包含服务和客户端应用程序使用的共享接口和数据类型。对于这个示例,您将需要:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。只有 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
git clone https://github.com/dapr/quickstarts.git
在一个新的终端窗口中,导航到 actors/csharp/sdk/service
目录并恢复依赖项:
cd actors/csharp/sdk/service
dotnet build
运行 SmartDevice.Service
,这将启动服务本身和 Dapr 边车:
dapr run --app-id actorservice --app-port 5001 --dapr-http-port 3500 --resources-path ../../../resources -- dotnet run --urls=http://localhost:5001/
预期输出:
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
== APP == Request starting HTTP/1.1 GET http://127.0.0.1:5001/healthz - -
== APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
== APP == Executing endpoint 'Dapr Actors Health Check'
== APP == info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
== APP == Executed endpoint 'Dapr Actors Health Check'
== APP == info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
== APP == Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - - - 200 - text/plain 5.2599ms
在一个新的终端实例中,导航到 actors/csharp/sdk/client
目录并安装依赖项:
cd ./actors/csharp/sdk/client
dotnet build
运行 SmartDevice.Client
应用程序:
dapr run --app-id actorclient -- dotnet run
预期输出:
== APP == Startup up...
== APP == Calling SetDataAsync on SmokeDetectorActor:1...
== APP == Got response: Success
== APP == Calling GetDataAsync on SmokeDetectorActor:1...
== APP == Device 1 state: Location: First Floor, Status: Ready
== APP == Calling SetDataAsync on SmokeDetectorActor:2...
== APP == Got response: Success
== APP == Calling GetDataAsync on SmokeDetectorActor:2...
== APP == Device 2 state: Location: Second Floor, Status: Ready
== APP == Registering the IDs of both Devices...
== APP == Registered devices: 1, 2
== APP == Detecting smoke on Device 1...
== APP == Device 1 state: Location: First Floor, Status: Alarm
== APP == Device 2 state: Location: Second Floor, Status: Alarm
== APP == Sleeping for 16 seconds before checking status again to see reminders fire and clear alarms
== APP == Device 1 state: Location: First Floor, Status: Ready
== APP == Device 2 state: Location: Second Floor, Status: Ready
如果您在本地机器上为 Dapr 配置了 Zipkin,您可以在 Zipkin Web UI 中查看 actor 与客户端的交互(通常在 http://localhost:9411/zipkin/
)。
当您运行客户端应用程序时,发生了一些事情:
两个 SmokeDetectorActor
actors 在 客户端应用程序中创建 并使用对象状态初始化:
ActorProxy.Create<ISmartDevice>(actorId, actorType)
proxySmartDevice.SetDataAsync(data)
这些对象是可重入的并持有状态,如 proxySmartDevice.GetDataAsync()
所示。
// Actor Ids 和类型
var deviceId1 = "1";
var deviceId2 = "2";
var smokeDetectorActorType = "SmokeDetectorActor";
var controllerActorType = "ControllerActor";
Console.WriteLine("Startup up...");
// ActorId 唯一标识第一个设备的第一个 actor 实例
var deviceActorId1 = new ActorId(deviceId1);
// 创建将存储在第一个 actor 中的数据类的新实例
var deviceData1 = new SmartDeviceData(){
Location = "First Floor",
Status = "Ready",
};
// 使用服务实现的相同接口创建本地代理。
var proxySmartDevice1 = ActorProxy.Create<ISmartDevice>(deviceActorId1, smokeDetectorActorType);
// 现在您可以使用 actor 接口调用 actor 的方法。
Console.WriteLine($"Calling SetDataAsync on {smokeDetectorActorType}:{deviceActorId1}...");
var setDataResponse1 = await proxySmartDevice1.SetDataAsync(deviceData1);
Console.WriteLine($"Got response: {setDataResponse1}");
Console.WriteLine($"Calling GetDataAsync on {smokeDetectorActorType}:{deviceActorId1}...");
var storedDeviceData1 = await proxySmartDevice1.GetDataAsync();
Console.WriteLine($"Device 1 state: {storedDeviceData1}");
// 为第二个设备创建第二个 actor
var deviceActorId2 = new ActorId(deviceId2);
// 创建将存储在第一个 actor 中的数据类的新实例
var deviceData2 = new SmartDeviceData(){
Location = "Second Floor",
Status = "Ready",
};
// 使用服务实现的相同接口创建本地代理。
var proxySmartDevice2 = ActorProxy.Create<ISmartDevice>(deviceActorId2, smokeDetectorActorType);
// 现在您可以使用 actor 接口调用第二个 actor 的方法。
Console.WriteLine($"Calling SetDataAsync on {smokeDetectorActorType}:{deviceActorId2}...");
var setDataResponse2 = await proxySmartDevice2.SetDataAsync(deviceData2);
Console.WriteLine($"Got response: {setDataResponse2}");
Console.WriteLine($"Calling GetDataAsync on {smokeDetectorActorType}:{deviceActorId2}...");
var storedDeviceData2 = await proxySmartDevice2.GetDataAsync();
Console.WriteLine($"Device 2 state: {storedDeviceData2}");
SmokeDetectorActor 1
的 DetectSmokeAsync
方法被调用。
public async Task DetectSmokeAsync()
{
var controllerActorId = new ActorId("controller");
var controllerActorType = "ControllerActor";
var controllerProxy = ProxyFactory.CreateActorProxy<IController>(controllerActorId, controllerActorType);
await controllerProxy.TriggerAlarmForAllDetectors();
}
ControllerActor
的 TriggerAlarmForAllDetectors
方法被调用。当检测到烟雾时,ControllerActor
内部触发所有警报
public async Task TriggerAlarmForAllDetectors()
{
var deviceIds = await ListRegisteredDeviceIdsAsync();
foreach (var deviceId in deviceIds)
{
var actorId = new ActorId(deviceId);
var proxySmartDevice = ProxyFactory.CreateActorProxy<ISmartDevice>(actorId, "SmokeDetectorActor");
await proxySmartDevice.SoundAlarm();
}
// 注册一个提醒,每 15 秒刷新并清除警报状态
await this.RegisterReminderAsync("AlarmRefreshReminder", null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
}
控制台 打印一条消息,指示检测到烟雾。
// 在设备 1 上检测到烟雾,触发所有设备的警报。
Console.WriteLine($"Detecting smoke on Device 1...");
proxySmartDevice1 = ActorProxy.Create<ISmartDevice>(deviceActorId1, smokeDetectorActorType);
await proxySmartDevice1.DetectSmokeAsync();
SmokeDetectorActor 1
和 2
的 SoundAlarm
方法 被调用。
storedDeviceData1 = await proxySmartDevice1.GetDataAsync();
Console.WriteLine($"Device 1 state: {storedDeviceData1}");
storedDeviceData2 = await proxySmartDevice2.GetDataAsync();
Console.WriteLine($"Device 2 state: {storedDeviceData2}");
ControllerActor
还使用 RegisterReminderAsync
创建一个持久提醒,在 15 秒后调用 ClearAlarm
。
// 注册一个提醒,每 15 秒刷新并清除警报状态
await this.RegisterReminderAsync("AlarmRefreshReminder", null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15));
要了解示例的完整上下文,请查看以下代码:
SmokeDetectorActor.cs
:实现智能设备 actorsControllerActor.cs
:实现管理所有设备的控制器 actorISmartDevice
:每个 SmokeDetectorActor
的方法定义和共享数据类型IController
:ControllerActor
的方法定义和共享数据类型我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进的建议吗?
加入我们的 discord 频道 讨论。
了解更多关于 Actor 构建模块 的信息
探索 Dapr 教程 >>Dapr提供了一个专用的机密API,允许开发者从机密存储中检索机密。在本快速入门中,您将:
在继续快速入门之前,请选择您偏好的编程语言对应的Dapr SDK。
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd secrets_management/python/sdk/order-processor
安装依赖项:
pip3 install -r requirements.txt
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../components/ -- python3 app.py
注意:在Windows中,可能需要使用
python app.py
而不是python3 app.py
。
order-processor
服务
请注意,order-processor
服务配置如下:
local-secret-store.yaml
组件中定义的DAPR_SECRET_STORE
。secrets.json
中定义的机密。# app.py
DAPR_SECRET_STORE = 'localsecretstore'
SECRET_NAME = 'secret'
with DaprClient() as client:
secret = client.get_secret(store_name=DAPR_SECRET_STORE, key=SECRET_NAME)
logging.info('Fetched Secret: %s', secret.secret)
local-secret-store.yaml
组件
DAPR_SECRET_STORE
在local-secret-store.yaml
组件文件中定义,位于secrets_management/components:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
在YAML文件中:
metadata/name
是应用程序引用组件的名称(在代码示例中称为DAPR_SECRET_STORE
)。spec/metadata
定义了组件使用的机密的连接信息。secrets.json
文件
SECRET_NAME
在secrets.json
文件中定义,位于secrets_management/python/sdk/order-processor:
{
"secret": "YourPasskeyHere"
}
如上面的应用程序代码中所示,order-processor
服务通过Dapr机密存储检索机密并在控制台中显示。
Order-processor输出:
== APP == INFO:root:Fetched Secret: {'secret': 'YourPasskeyHere'}
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd secrets_management/javascript/sdk/order-processor
安装依赖项:
npm install
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../components/ -- npm start
order-processor
服务
请注意,order-processor
服务配置如下:
local-secret-store.yaml
组件中定义的DAPR_SECRET_STORE
。secrets.json
中定义的机密。// index.js
const DAPR_SECRET_STORE = "localsecretstore";
const SECRET_NAME = "secret";
async function main() {
// ...
const secret = await client.secret.get(DAPR_SECRET_STORE, SECRET_NAME);
console.log("Fetched Secret: " + JSON.stringify(secret));
}
local-secret-store.yaml
组件
DAPR_SECRET_STORE
在local-secret-store.yaml
组件文件中定义,位于secrets_management/components:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
在YAML文件中:
metadata/name
是应用程序引用组件的名称(在代码示例中称为DAPR_SECRET_STORE
)。spec/metadata
定义了组件使用的机密的连接信息。secrets.json
文件
SECRET_NAME
在secrets.json
文件中定义,位于secrets_management/javascript/sdk/order-processor:
{
"secret": "YourPasskeyHere"
}
如上面的应用程序代码中所示,order-processor
服务通过Dapr机密存储检索机密并在控制台中显示。
Order-processor输出:
== APP ==
== APP == > order-processor@1.0.0 start
== APP == > node index.js
== APP ==
== APP == Fetched Secret: {"secret":"YourPasskeyHere"}
您需要准备以下环境:
注意: .NET 6是此版本中Dapr .NET SDK包的最低支持版本。只有.NET 8和.NET 9将在Dapr v1.16及以后版本中得到支持。
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd secrets_management/csharp/sdk/order-processor
安装依赖项:
dotnet restore
dotnet build
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../components/ -- dotnet run
order-processor
服务
请注意,order-processor
服务配置如下:
local-secret-store.yaml
组件中定义的DAPR_SECRET_STORE
。secrets.json
中定义的机密。// Program.cs
const string DAPR_SECRET_STORE = "localsecretstore";
const string SECRET_NAME = "secret";
var client = new DaprClientBuilder().Build();
var secret = await client.GetSecretAsync(DAPR_SECRET_STORE, SECRET_NAME);
var secretValue = string.Join(", ", secret);
Console.WriteLine($"Fetched Secret: {secretValue}");
local-secret-store.yaml
组件
DAPR_SECRET_STORE
在local-secret-store.yaml
组件文件中定义,位于secrets_management/components:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
在YAML文件中:
metadata/name
是应用程序引用组件的名称(在代码示例中称为DAPR_SECRET_STORE
)。spec/metadata
定义了组件使用的机密的连接信息。secrets.json
文件
SECRET_NAME
在secrets.json
文件中定义,位于secrets_management/csharp/sdk/order-processor:
{
"secret": "YourPasskeyHere"
}
如上面的应用程序代码中所示,order-processor
服务通过Dapr机密存储检索机密并在控制台中显示。
Order-processor输出:
== APP == Fetched Secret: [secret, YourPasskeyHere]
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd secrets_management/java/sdk/order-processor
安装依赖项:
mvn clean install
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../components/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
order-processor
服务
请注意,order-processor
服务配置如下:
local-secret-store.yaml
组件中定义的DAPR_SECRET_STORE
。secrets.json
中定义的机密。// OrderProcessingServiceApplication.java
private static final String SECRET_STORE_NAME = "localsecretstore";
// ...
Map<String, String> secret = client.getSecret(SECRET_STORE_NAME, "secret").block();
System.out.println("Fetched Secret: " + secret);
local-secret-store.yaml
组件
DAPR_SECRET_STORE
在local-secret-store.yaml
组件文件中定义,位于secrets_management/components:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
在YAML文件中:
metadata/name
是应用程序引用组件的名称(在代码示例中称为DAPR_SECRET_STORE
)。spec/metadata
定义了组件使用的机密的连接信息。secrets.json
文件
SECRET_NAME
在secrets.json
文件中定义,位于secrets_management/java/sdk/order-processor:
{
"secret": "YourPasskeyHere"
}
如上面的应用程序代码中所示,order-processor
服务通过Dapr机密存储检索机密并在控制台中显示。
Order-processor输出:
== APP == Fetched Secret: {secret=YourPasskeyHere}
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd secrets_management/go/sdk/order-processor
安装依赖项:
go build .
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../components/ -- go run .
order-processor
服务
请注意,order-processor
服务配置如下:
local-secret-store.yaml
组件中定义的DAPR_SECRET_STORE
。secrets.json
中定义的机密。const DAPR_SECRET_STORE = "localsecretstore"
const SECRET_NAME = "secret"
// ...
secret, err := client.GetSecret(ctx, DAPR_SECRET_STORE, SECRET_NAME, nil)
if secret != nil {
fmt.Println("Fetched Secret: ", secret[SECRET_NAME])
}
local-secret-store.yaml
组件
DAPR_SECRET_STORE
在local-secret-store.yaml
组件文件中定义,位于secrets_management/components:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localsecretstore
namespace: default
spec:
type: secretstores.local.file
version: v1
metadata:
- name: secretsFile
value: secrets.json
- name: nestedSeparator
value: ":"
在YAML文件中:
metadata/name
是应用程序引用组件的名称(在代码示例中称为DAPR_SECRET_STORE
)。spec/metadata
定义了组件使用的机密的连接信息。secrets.json
文件
SECRET_NAME
在secrets.json
文件中定义,位于secrets_management/go/sdk/order-processor:
{
"secret": "YourPasskeyHere"
}
如上面的应用程序代码中所示,order-processor
服务通过Dapr机密存储检索机密并在控制台中显示。
Order-processor输出:
== APP == Fetched Secret: YourPasskeyHere
我们正在不断改进我们的快速入门示例,重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的discord频道讨论。
接下来,我们将介绍 Dapr 的配置模块。配置项通常具有动态特性,并且与应用程序的需求紧密相关。配置项是包含配置信息的键/值对,例如:
在本快速入门中,您将运行一个使用配置 API 的 order-processor
微服务。该服务将:
在继续快速入门之前,请选择您偏好的 Dapr SDK 语言版本。
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
克隆后,打开一个新终端并运行以下命令,为配置项 orderId1
和 orderId2
设置值。
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
order-processor
服务从快速入门克隆目录的根目录,导航到 order-processor
目录。
cd configuration/python/sdk/order-processor
安装依赖项:
pip3 install -r requirements.txt
在 Dapr 边车环境中运行 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../components/ --app-port 6001 -- python3 app.py
注意:在 Windows 中,您可能需要使用
python app.py
而不是python3 app.py
。
预期输出:
== APP == Configuration for orderId1 : value: "101"
== APP ==
== APP == Configuration for orderId2 : value: "102"
== APP ==
== APP == App unsubscribed from config changes
应用程序取消订阅后,尝试更新配置项值。使用以下命令更改 orderId1
和 orderId2
的值:
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
再次运行 order-processor
服务:
dapr run --app-id order-processor --resources-path ../../../components/ --app-port 6001 -- python3 app.py
注意:在 Windows 中,您可能需要使用
python app.py
而不是python3 app.py
。
应用程序将显示更新后的配置值:
== APP == Configuration for orderId1 : value: "103"
== APP ==
== APP == Configuration for orderId2 : value: "104"
== APP ==
order-processor
服务order-processor
服务包括以下代码:
获取配置项:
# 从配置存储中获取配置项
for config_item in CONFIGURATION_ITEMS:
config = client.get_configuration(store_name=DAPR_CONFIGURATION_STORE, keys=[config_item], config_metadata={})
print(f"Configuration for {config_item} : {config.items[config_item]}", flush=True)
订阅配置更新:
# 订阅配置更改
configuration = await client.subscribe_configuration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS)
取消订阅配置更新并退出应用程序:
# 取消订阅配置更新
unsubscribed = True
for config_item in CONFIGURATION_ITEMS:
unsub_item = client.unsubscribe_configuration(DAPR_CONFIGURATION_STORE, config_item)
#...
if unsubscribed == True:
print("App unsubscribed from config changes", flush=True)
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
克隆后,打开一个新终端并运行以下命令,为配置项 orderId1
和 orderId2
设置值。
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
order-processor
服务从快速入门克隆目录的根目录,导航到 order-processor
目录。
cd configuration/javascript/sdk/order-processor
安装依赖项:
npm install
在 Dapr 边车环境中运行 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../components/ --app-protocol grpc --dapr-grpc-port 3500 -- node index.js
预期输出:
== APP == Configuration for orderId1: {"key":"orderId1","value":"101","version":"","metadata":{}}
== APP == Configuration for orderId2: {"key":"orderId2","value":"102","version":"","metadata":{}}
== APP == App unsubscribed to config changes
应用程序取消订阅后,尝试更新配置项值。使用以下命令更改 orderId1
和 orderId2
的值:
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
再次运行 order-processor
服务:
dapr run --app-id order-processor --resources-path ../../../components/ --app-protocol grpc --dapr-grpc-port 3500 -- node index.js
应用程序将显示更新后的配置值:
== APP == Configuration for orderId1: {"key":"orderId1","value":"103","version":"","metadata":{}}
== APP == Configuration for orderId2: {"key":"orderId2","value":"104","version":"","metadata":{}}
order-processor
服务order-processor
服务包括以下代码:
获取配置项:
// 从配置存储中获取配置项
//...
const config = await client.configuration.get(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
Object.keys(config.items).forEach((key) => {
console.log("Configuration for " + key + ":", JSON.stringify(config.items[key]));
});
订阅配置更新:
// 订阅配置更新
try {
const stream = await client.configuration.subscribeWithKeys(
DAPR_CONFIGURATION_STORE,
CONFIGURATION_ITEMS,
(config) => {
console.log("Configuration update", JSON.stringify(config.items));
}
);
取消订阅配置更新并退出应用程序:
// 取消订阅配置更新并在 20 秒后退出应用程序
setTimeout(() => {
stream.stop();
console.log("App unsubscribed to config changes");
process.exit(0);
},
您需要准备以下环境:
注意: .NET 6 是此版本中 Dapr .NET SDK 包的最低支持版本。仅 .NET 8 和 .NET 9 将在 Dapr v1.16 及更高版本中得到支持。
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
克隆后,打开一个新终端并运行以下命令,为配置项 orderId1
和 orderId2
设置值。
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
order-processor
服务从快速入门克隆目录的根目录,导航到 order-processor
目录。
cd configuration/csharp/sdk/order-processor
恢复 NuGet 包:
dotnet restore
dotnet build
在 Dapr 边车环境中运行 order-processor
服务。
dapr run --app-id order-processor-http --resources-path ../../../components/ --app-port 7001 -- dotnet run --project .
预期输出:
== APP == Configuration for orderId1: {"Value":"101","Version":"","Metadata":{}}
== APP == Configuration for orderId2: {"Value":"102","Version":"","Metadata":{}}
== APP == App unsubscribed from config changes
应用程序取消订阅后,尝试更新配置项值。使用以下命令更改 orderId1
和 orderId2
的值:
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
再次运行 order-processor
服务:
dapr run --app-id order-processor-http --resources-path ../../../components/ --app-port 7001 -- dotnet run --project .
应用程序将显示更新后的配置值:
== APP == Configuration for orderId1: {"Value":"103","Version":"","Metadata":{}}
== APP == Configuration for orderId2: {"Value":"104","Version":"","Metadata":{}}
order-processor
服务order-processor
服务包括以下代码:
获取配置项:
// 从配置存储中获取配置
GetConfigurationResponse config = await client.GetConfiguration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
foreach (var item in config.Items)
{
var cfg = System.Text.Json.JsonSerializer.Serialize(item.Value);
Console.WriteLine("Configuration for " + item.Key + ": " + cfg);
}
订阅配置更新:
// 订阅配置更新
SubscribeConfigurationResponse subscribe = await client.SubscribeConfiguration(DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS);
取消订阅配置更新并退出应用程序:
// 取消订阅配置更新并退出应用程序
try
{
client.UnsubscribeConfiguration(DAPR_CONFIGURATION_STORE, subscriptionId);
Console.WriteLine("App unsubscribed from config changes");
Environment.Exit(0);
}
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
克隆后,打开一个新终端并运行以下命令,为配置项 orderId1
和 orderId2
设置值。
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
order-processor
服务从快速入门克隆目录的根目录,导航到 order-processor
目录。
cd configuration/java/sdk/order-processor
安装依赖项:
mvn clean install
在 Dapr 边车环境中运行 order-processor
服务。
dapr run --app-id order-processor --resources-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
预期输出:
== APP == Configuration for orderId1: {'value':'101'}
== APP == Configuration for orderId2: {'value':'102'}
== APP == App unsubscribed to config changes
应用程序取消订阅后,尝试更新配置项值。使用以下命令更改 orderId1
和 orderId2
的值:
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
再次运行 order-processor
服务:
dapr run --app-id order-processor --resources-path ../../../components -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
应用程序将显示更新后的配置值:
== APP == Configuration for orderId1: {'value':'103'}
== APP == Configuration for orderId2: {'value':'104'}
order-processor
服务order-processor
服务包括以下代码:
获取配置项:
// 从配置存储中获取配置项
try (DaprPreviewClient client = (new DaprClientBuilder()).buildPreviewClient()) {
for (String configurationItem : CONFIGURATION_ITEMS) {
ConfigurationItem item = client.getConfiguration(DAPR_CONFIGURATON_STORE, configurationItem).block();
System.out.println("Configuration for " + configurationItem + ": {'value':'" + item.getValue() + "'}");
}
订阅配置更新:
// 订阅配置更改
Flux<SubscribeConfigurationResponse> subscription = client.subscribeConfiguration(DAPR_CONFIGURATON_STORE,
CONFIGURATION_ITEMS.toArray(String[]::new));
取消订阅配置更新并退出应用程序:
// 取消订阅配置更改
UnsubscribeConfigurationResponse unsubscribe = client
.unsubscribeConfiguration(subscriptionId, DAPR_CONFIGURATON_STORE).block();
if (unsubscribe.getIsUnsubscribed()) {
System.out.println("App unsubscribed to config changes");
}
您需要准备以下环境:
克隆快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
克隆后,打开一个新终端并运行以下命令,为配置项 orderId1
和 orderId2
设置值。
docker exec dapr_redis redis-cli MSET orderId1 "101" orderId2 "102"
order-processor
服务从快速入门克隆目录的根目录,导航到 order-processor
目录。
cd configuration/go/sdk/order-processor
在 Dapr 边车环境中运行 order-processor
服务。
dapr run --app-id order-processor --app-port 6001 --resources-path ../../../components -- go run .
预期输出:
== APP == Configuration for orderId1: {"Value":"101","Version":"","Metadata":null}
== APP == Configuration for orderId2: {"Value":"102","Version":"","Metadata":null}
== APP == dapr configuration subscribe finished.
== APP == App unsubscribed to config changes
应用程序取消订阅后,尝试更新配置项值。使用以下命令更改 orderId1
和 orderId2
的值:
docker exec dapr_redis redis-cli MSET orderId1 "103" orderId2 "104"
再次运行 order-processor
服务:
dapr run --app-id order-processor --app-port 6001 --resources-path ../../../components -- go run .
应用程序将显示更新后的配置值:
== APP == Configuration for orderId1: {"Value":"103","Version":"","Metadata":null}
== APP == Configuration for orderId2: {"Value":"104","Version":"","Metadata":null}
order-processor
服务order-processor
服务包括以下代码:
获取配置项:
// 从配置存储中获取配置项
for _, item := range CONFIGURATION_ITEMS {
config, err := client.GetConfigurationItem(ctx, DAPR_CONFIGURATION_STORE, item)
//...
c, _ := json.Marshal(config)
fmt.Println("Configuration for " + item + ": " + string(c))
}
订阅配置更新:
// 订阅配置更改
err = client.SubscribeConfigurationItems(ctx, DAPR_CONFIGURATION_STORE, CONFIGURATION_ITEMS, func(id string, config map[string]*dapr.ConfigurationItem) {
// 应用程序首次订阅配置更改时仅返回订阅 ID
if len(config) == 0 {
fmt.Println("App subscribed to config changes with subscription id: " + id)
subscriptionId = id
return
}
})
取消订阅配置更新并退出应用程序:
// 取消订阅配置更新并在 20 秒后退出应用程序
select {
case <-ctx.Done():
err = client.UnsubscribeConfigurationItems(context.Background(), DAPR_CONFIGURATION_STORE, subscriptionId)
//...
{
fmt.Println("App unsubscribed to config changes")
}
观看此视频演示配置 API 快速入门:
我们正在不断改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的Discord 频道讨论。
我们来了解一下 Dapr 的加密构建块。在这个快速入门中,您将创建一个应用程序,使用 Dapr 加密 API 来加密和解密数据。您将:
目前,您可以使用 Go SDK 体验加密 API。
此快速入门包括一个名为
crypto-quickstart
的 JavaScript 应用程序。
对于此示例,您将需要:
克隆 快速入门仓库中提供的示例
git clone https://github.com/dapr/quickstarts.git
在终端中,从根目录导航到加密示例。
cd cryptography/javascript/sdk
导航到包含源代码的文件夹:
cd ./crypto-quickstart
安装依赖项:
npm install
应用程序代码定义了两个必需的密钥:
使用 OpenSSL 生成两个密钥:一个 RSA 密钥和一个 AES 密钥,并将它们写入两个文件:
mkdir -p keys
# 生成一个私有 RSA 密钥,4096 位密钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
# 为 AES 生成一个 256 位密钥
openssl rand -out keys/symmetric-key-256 32
使用 Dapr 运行 Go 服务应用程序:
dapr run --app-id crypto-quickstart --resources-path ../../../components/ -- npm start
预期输出
== APP == 2023-10-25T14:30:50.435Z INFO [GRPCClient, GRPCClient] Opening connection to 127.0.0.1:58173
== APP == == Encrypting message using buffers
== APP == Encrypted the message, got 856 bytes
== APP == == Decrypting message using buffers
== APP == Decrypted the message, got 24 bytes
== APP == The secret is "passw0rd"
== APP == == Encrypting message using streams
== APP == Encrypting federico-di-dio-photography-Q4g0Q-eVVEg-unsplash.jpg to encrypted.out
== APP == Encrypted the message to encrypted.out
== APP == == Decrypting message using streams
== APP == Decrypting encrypted.out to decrypted.out.jpg
== APP == Decrypted the message to decrypted.out.jpg
local-storage.yaml
之前,您在 crypto-quickstarts
中创建了一个名为 keys
的目录。在 local-storage
组件 YAML 中,path
元数据映射到新创建的 keys
目录。
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localstorage
spec:
type: crypto.dapr.localstorage
version: v1
metadata:
- name: path
# 路径相对于示例所在的文件夹
value: ./keys
index.mjs
应用程序文件 使用您生成的 RSA 和 AES 密钥加密和解密消息和文件。应用程序创建了一个新的 Dapr SDK 客户端:
async function start() {
const client = new DaprClient({
daprHost,
daprPort,
communicationProtocol: CommunicationProtocolEnum.GRPC,
});
// 从缓冲区加密和解密消息
await encryptDecryptBuffer(client);
// 使用流加密和解密消息
await encryptDecryptStream(client);
}
一旦创建了客户端,应用程序就会加密一条消息:
async function encryptDecryptBuffer(client) {
// 要加密的消息
const plaintext = `The secret is "passw0rd"`
// 首先,加密消息
console.log("== Encrypting message using buffers");
const encrypted = await client.crypto.encrypt(plaintext, {
componentName: "localstorage",
keyName: "rsa-private-key.pem",
keyWrapAlgorithm: "RSA",
});
console.log("Encrypted the message, got", encrypted.length, "bytes");
然后应用程序解密消息:
// 解密消息
console.log("== Decrypting message using buffers");
const decrypted = await client.crypto.decrypt(encrypted, {
componentName: "localstorage",
});
console.log("Decrypted the message, got", decrypted.length, "bytes");
console.log(decrypted.toString("utf8"));
// ...
}
接下来,应用程序加密一个大图像文件:
async function encryptDecryptStream(client) {
// 首先,加密消息
console.log("== Encrypting message using streams");
console.log("Encrypting", testFileName, "to encrypted.out");
await pipeline(
createReadStream(testFileName),
await client.crypto.encrypt({
componentName: "localstorage",
keyName: "symmetric-key-256",
keyWrapAlgorithm: "A256KW",
}),
createWriteStream("encrypted.out"),
);
console.log("Encrypted the message to encrypted.out");
然后应用程序解密大图像文件:
// 解密消息
console.log("== Decrypting message using streams");
console.log("Decrypting encrypted.out to decrypted.out.jpg");
await pipeline(
createReadStream("encrypted.out"),
await client.crypto.decrypt({
componentName: "localstorage",
}),
createWriteStream("decrypted.out.jpg"),
);
console.log("Decrypted the message to decrypted.out.jpg");
}
此快速入门包括一个名为
crypto-quickstart
的 Go 应用程序。
对于此示例,您将需要:
克隆 快速入门仓库中提供的示例
git clone https://github.com/dapr/quickstarts.git
在终端中,从根目录导航到加密示例。
cd cryptography/go/sdk
导航到包含源代码的文件夹:
cd ./crypto-quickstart
应用程序代码定义了两个必需的密钥:
使用 OpenSSL 生成两个密钥:一个 RSA 密钥和一个 AES 密钥,并将它们写入两个文件:
mkdir -p keys
# 生成一个私有 RSA 密钥,4096 位密钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
# 为 AES 生成一个 256 位密钥
openssl rand -out keys/symmetric-key-256 32
使用 Dapr 运行 Go 服务应用程序:
dapr run --app-id crypto-quickstart --resources-path ../../../components/ -- go run .
预期输出
== APP == dapr client initializing for: 127.0.0.1:52407
== APP == Encrypted the message, got 856 bytes
== APP == Decrypted the message, got 24 bytes
== APP == The secret is "passw0rd"
== APP == Wrote decrypted data to encrypted.out
== APP == Wrote decrypted data to decrypted.out.jpg
local-storage.yaml
之前,您在 crypto-quickstarts
中创建了一个名为 keys
的目录。在 local-storage
组件 YAML 中,path
元数据映射到新创建的 keys
目录。
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: localstorage
spec:
type: crypto.dapr.localstorage
version: v1
metadata:
- name: path
# 路径相对于示例所在的文件夹
value: ./keys
app.go
应用程序文件 使用您生成的 RSA 和 AES 密钥加密和解密消息和文件。应用程序创建了一个新的 Dapr SDK 客户端:
func main() {
// 创建一个新的 Dapr SDK 客户端
client, err := dapr.NewClient()
//...
// 步骤 1:使用 RSA 密钥加密字符串,然后解密并在终端中显示输出
encryptDecryptString(client)
// 步骤 2:加密大文件,然后使用 AES 密钥解密
encryptDecryptFile(client)
}
一旦创建了客户端,应用程序就会加密一条消息:
func encryptDecryptString(client dapr.Client) {
// ...
// 加密消息
encStream, err := client.Encrypt(context.Background(),
strings.NewReader(message),
dapr.EncryptOptions{
ComponentName: CryptoComponentName,
// 要使用的密钥名称
// 由于这是一个 RSA 密钥,我们将其指定为密钥包装算法
KeyName: RSAKeyName,
KeyWrapAlgorithm: "RSA",
},
)
// ...
// 该方法返回一个可读流,我们在内存中完整读取
encBytes, err := io.ReadAll(encStream)
// ...
fmt.Printf("Encrypted the message, got %d bytes\n", len(encBytes))
然后应用程序解密消息:
// 现在,解密加密的数据
decStream, err := client.Decrypt(context.Background(),
bytes.NewReader(encBytes),
dapr.DecryptOptions{
// 我们只需要传递组件的名称
ComponentName: CryptoComponentName,
// 传递密钥名称是可选的
KeyName: RSAKeyName,
},
)
// ...
// 该方法返回一个可读流,我们在内存中完整读取
decBytes, err := io.ReadAll(decStream)
// ...
// 在控制台上打印消息
fmt.Printf("Decrypted the message, got %d bytes\n", len(decBytes))
fmt.Println(string(decBytes))
}
接下来,应用程序加密一个大图像文件:
func encryptDecryptFile(client dapr.Client) {
const fileName = "liuguangxi-66ouBTTs_x0-unsplash.jpg"
// 获取输入文件的可读流
plaintextF, err := os.Open(fileName)
// ...
defer plaintextF.Close()
// 加密文件
encStream, err := client.Encrypt(context.Background(),
plaintextF,
dapr.EncryptOptions{
ComponentName: CryptoComponentName,
// 要使用的密钥名称
// 由于这是一个对称密钥,我们将 AES 指定为密钥包装算法
KeyName: SymmetricKeyName,
KeyWrapAlgorithm: "AES",
},
)
// ...
// 将加密数据写入文件 "encrypted.out"
encryptedF, err := os.Create("encrypted.out")
// ...
encryptedF.Close()
fmt.Println("Wrote decrypted data to encrypted.out")
然后应用程序解密大图像文件:
// 现在,解密加密的数据
// 首先,再次打开文件 "encrypted.out",这次用于读取
encryptedF, err = os.Open("encrypted.out")
// ...
defer encryptedF.Close()
// 现在,解密加密的数据
decStream, err := client.Decrypt(context.Background(),
encryptedF,
dapr.DecryptOptions{
// 我们只需要传递组件的名称
ComponentName: CryptoComponentName,
// 传递密钥名称是可选的
KeyName: SymmetricKeyName,
},
)
// ...
// 将解密数据写入文件 "decrypted.out.jpg"
decryptedF, err := os.Create("decrypted.out.jpg")
// ...
decryptedF.Close()
fmt.Println("Wrote decrypted data to decrypted.out.jpg")
}
观看此 Dapr 社区电话 #83 中的加密 API 演示视频:
我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的 discord 频道进行讨论。
Dapr 作业构建块 允许您在特定时间或间隔调度和运行作业。在本快速入门中,您将学习如何使用 Dapr 的作业 API 来调度、获取和删除作业。
您可以通过以下两种方式来体验此作业快速入门:
在开始之前,请选择您偏好的 Dapr SDK 语言。目前,您可以使用 Go SDK 来试验作业 API。
本快速入门包含两个应用程序:
job-scheduler.go
: 负责调度、检索和删除作业。job-service.go
: 负责处理已调度的作业。您需要以下工具:
克隆 快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
从仓库根目录导航到作业目录:
cd jobs/go/sdk
运行应用程序并调度作业:
dapr run -f .
预期输出
== APP - job-service == dapr 客户端初始化中:127.0.0.1:6281
== APP - job-service == 已注册作业处理程序:R2-D2
== APP - job-service == 已注册作业处理程序:C-3PO
== APP - job-service == 已注册作业处理程序:BB-8
== APP - job-service == 在端口启动服务器:6200
== APP - job-service == 作业已调度:R2-D2
== APP - job-service == 作业已调度:C-3PO
== APP - job-service == 2024/07/17 18:09:59 作业:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}}
== APP - job-scheduler == 获取作业响应:{"droid":"C-3PO","Task":"Memory Wipe"}
== APP - job-service == 作业已调度:BB-8
== APP - job-service == 2024/07/17 18:09:59 作业:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}}
== APP - job-scheduler == 获取作业响应:{"droid":"BB-8","Task":"Internal Gyroscope Check"}
== APP - job-scheduler == 已删除作业:BB-8
5 秒后,终端输出应显示 R2-D2
作业正在处理:
== APP - job-service == 启动机器人:R2-D2
== APP - job-service == 执行维护作业:Oil Change
10 秒后,终端输出应显示 C3-PO
作业正在处理:
== APP - job-service == 启动机器人:C-3PO
== APP - job-service == 执行维护作业:Memory Wipe
完成后,您可以使用以下命令停止并清理应用程序进程。
dapr stop -f .
在 Dapr 安装期间运行 dapr init
时:
dapr_scheduler
控制平面与其他 Dapr 服务一起启动。.dapr/components
目录中生成了 dapr.yaml
多应用运行模板文件。在此快速入门中运行 dapr run -f .
启动了 job-scheduler
和 job-service
。在终端输出中,您可以看到以下作业正在调度、检索和删除。
R2-D2
作业正在调度。C-3PO
作业正在调度。C-3PO
作业正在检索。BB-8
作业正在调度。BB-8
作业正在检索。BB-8
作业正在删除。R2-D2
作业在 5 秒后执行。R2-D2
作业在 10 秒后执行。dapr.yaml
多应用运行模板文件使用 dapr run -f .
运行 多应用运行模板文件 启动项目中的所有应用程序。在此快速入门中,dapr.yaml
文件包含以下内容:
version: 1
apps:
- appDirPath: ./job-service/
appID: job-service
appPort: 6200
daprGRPCPort: 6281
appProtocol: grpc
command: ["go", "run", "."]
- appDirPath: ./job-scheduler/
appID: job-scheduler
appPort: 6300
command: ["go", "run", "."]
job-service
应用job-service
应用程序创建服务调用处理程序以管理作业的生命周期(scheduleJob
、getJob
和 deleteJob
)。
if err := server.AddServiceInvocationHandler("scheduleJob", scheduleJob); err != nil {
log.Fatalf("error adding invocation handler: %v", err)
}
if err := server.AddServiceInvocationHandler("getJob", getJob); err != nil {
log.Fatalf("error adding invocation handler: %v", err)
}
if err := server.AddServiceInvocationHandler("deleteJob", deleteJob); err != nil {
log.Fatalf("error adding invocation handler: %v", err)
}
接下来,为所有机器人注册作业事件处理程序:
for _, jobName := range jobNames {
if err := server.AddJobEventHandler(jobName, handleJob); err != nil {
log.Fatalf("failed to register job event handler: %v", err)
}
fmt.Println("Registered job handler for: ", jobName)
}
fmt.Println("Starting server on port: " + appPort)
if err = server.Start(); err != nil {
log.Fatalf("failed to start server: %v", err)
}
然后,job-service
调用处理调度、获取、删除和处理作业事件的函数。
// 处理调度 DroidJob 的处理程序
func scheduleJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) {
if in == nil {
err = errors.New("no invocation parameter")
return
}
droidJob := DroidJob{}
err = json.Unmarshal(in.Data, &droidJob)
if err != nil {
fmt.Println("failed to unmarshal job: ", err)
return nil, err
}
jobData := JobData{
Droid: droidJob.Name,
Task: droidJob.Job,
}
content, err := json.Marshal(jobData)
if err != nil {
fmt.Printf("Error marshalling job content")
return nil, err
}
// 调度作业
job := daprc.Job{
Name: droidJob.Name,
DueTime: droidJob.DueTime,
Data: &anypb.Any{
Value: content,
},
}
err = app.daprClient.ScheduleJobAlpha1(ctx, &job)
if err != nil {
fmt.Println("failed to schedule job. err: ", err)
return nil, err
}
fmt.Println("Job scheduled: ", droidJob.Name)
out = &common.Content{
Data: in.Data,
ContentType: in.ContentType,
DataTypeURL: in.DataTypeURL,
}
return out, err
}
// 处理按名称获取作业的处理程序
func getJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) {
if in == nil {
err = errors.New("no invocation parameter")
return nil, err
}
job, err := app.daprClient.GetJobAlpha1(ctx, string(in.Data))
if err != nil {
fmt.Println("failed to get job. err: ", err)
}
out = &common.Content{
Data: job.Data.Value,
ContentType: in.ContentType,
DataTypeURL: in.DataTypeURL,
}
return out, err
}
// 处理按名称删除作业的处理程序
func deleteJob(ctx context.Context, in *common.InvocationEvent) (out *common.Content, err error) {
if in == nil {
err = errors.New("no invocation parameter")
return nil, err
}
err = app.daprClient.DeleteJobAlpha1(ctx, string(in.Data))
if err != nil {
fmt.Println("failed to delete job. err: ", err)
}
out = &common.Content{
Data: in.Data,
ContentType: in.ContentType,
DataTypeURL: in.DataTypeURL,
}
return out, err
}
// 处理作业事件的处理程序
func handleJob(ctx context.Context, job *common.JobEvent) error {
var jobData common.Job
if err := json.Unmarshal(job.Data, &jobData); err != nil {
return fmt.Errorf("failed to unmarshal job: %v", err)
}
var jobPayload JobData
if err := json.Unmarshal(job.Data, &jobPayload); err != nil {
return fmt.Errorf("failed to unmarshal payload: %v", err)
}
fmt.Println("Starting droid:", jobPayload.Droid)
fmt.Println("Executing maintenance job:", jobPayload.Task)
return nil
}
job-scheduler
应用在 job-scheduler
应用程序中,首先将 R2D2、C3PO 和 BB8 作业定义为 []DroidJob
:
droidJobs := []DroidJob{
{Name: "R2-D2", Job: "Oil Change", DueTime: "5s"},
{Name: "C-3PO", Job: "Memory Wipe", DueTime: "15s"},
{Name: "BB-8", Job: "Internal Gyroscope Check", DueTime: "30s"},
}
然后使用作业 API 调度、检索和删除作业。正如您从终端输出中看到的,首先调度 R2D2 作业:
// 调度 R2D2 作业
err = schedule(droidJobs[0])
if err != nil {
log.Fatalln("Error scheduling job: ", err)
}
然后调度 C3PO 作业,并返回作业数据:
// 调度 C-3PO 作业
err = schedule(droidJobs[1])
if err != nil {
log.Fatalln("Error scheduling job: ", err)
}
// 获取 C-3PO 作业
resp, err := get(droidJobs[1])
if err != nil {
log.Fatalln("Error retrieving job: ", err)
}
fmt.Println("Get job response: ", resp)
然后调度、检索和删除 BB8 作业:
// 调度 BB-8 作业
err = schedule(droidJobs[2])
if err != nil {
log.Fatalln("Error scheduling job: ", err)
}
// 获取 BB-8 作业
resp, err = get(droidJobs[2])
if err != nil {
log.Fatalln("Error retrieving job: ", err)
}
fmt.Println("Get job response: ", resp)
// 删除 BB-8 作业
err = delete(droidJobs[2])
if err != nil {
log.Fatalln("Error deleting job: ", err)
}
fmt.Println("Job deleted: ", droidJobs[2].Name)
job-scheduler.go
还定义了 schedule
、get
和 delete
函数,从 job-service.go
调用。
// 通过从 job-service 调用 grpc 服务并传递 DroidJob 作为参数来调度作业
func schedule(droidJob DroidJob) error {
jobData, err := json.Marshal(droidJob)
if err != nil {
fmt.Println("Error marshalling job content")
return err
}
content := &daprc.DataContent{
ContentType: "application/json",
Data: []byte(jobData),
}
// 调度作业
_, err = app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "scheduleJob", "POST", content)
if err != nil {
fmt.Println("Error invoking method: ", err)
return err
}
return nil
}
// 通过从 job-service 调用 grpc 服务并传递作业名称作为参数来获取作业
func get(droidJob DroidJob) (string, error) {
content := &daprc.DataContent{
ContentType: "text/plain",
Data: []byte(droidJob.Name),
}
// 获取作业
resp, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "getJob", "GET", content)
if err != nil {
fmt.Println("Error invoking method: ", err)
return "", err
}
return string(resp), nil
}
// 通过从 job-service 调用 grpc 服务并传递作业名称作为参数来删除作业
func delete(droidJob DroidJob) error {
content := &daprc.DataContent{
ContentType: "text/plain",
Data: []byte(droidJob.Name),
}
_, err := app.daprClient.InvokeMethodWithContent(context.Background(), "job-service", "deleteJob", "DELETE", content)
if err != nil {
fmt.Println("Error invoking method: ", err)
return err
}
return nil
}
本快速入门包含两个应用程序:
job-scheduler.go
: 负责调度、检索和删除作业。job-service.go
: 负责处理已调度的作业。您需要以下工具:
克隆 快速入门仓库中的示例。
git clone https://github.com/dapr/quickstarts.git
从仓库根目录导航到作业目录:
cd jobs/go/sdk
在终端中运行 job-service
应用:
dapr run --app-id job-service --app-port 6200 --dapr-grpc-port 6281 --app-protocol grpc -- go run .
预期输出
== APP == dapr 客户端初始化中:127.0.0.1:6281
== APP == 已注册作业处理程序:R2-D2
== APP == 已注册作业处理程序:C-3PO
== APP == 已注册作业处理程序:BB-8
== APP == 在端口启动服务器:6200
在新终端窗口中运行 job-scheduler
应用:
dapr run --app-id job-scheduler --app-port 6300 -- go run .
预期输出
== APP == dapr 客户端初始化中:
== APP == 获取作业响应:{"droid":"C-3PO","Task":"Memory Wipe"}
== APP == 获取作业响应:{"droid":"BB-8","Task":"Internal Gyroscope Check"}
== APP == 作业已删除:BB-8
返回到 job-service
应用的终端窗口。输出应为:
== APP == 作业已调度:R2-D2
== APP == 作业已调度:C-3PO
== APP == 2024/07/17 18:25:36 作业:{name:"C-3PO" due_time:"10s" data:{value:"{\"droid\":\"C-3PO\",\"Task\":\"Memory Wipe\"}"}}
== APP == 作业已调度:BB-8
== APP == 2024/07/17 18:25:36 作业:{name:"BB-8" due_time:"15s" data:{value:"{\"droid\":\"BB-8\",\"Task\":\"Internal Gyroscope Check\"}"}}
== APP == 启动机器人:R2-D2
== APP == 执行维护作业:Oil Change
== APP == 启动机器人:C-3PO
== APP == 执行维护作业:Memory Wipe
解读当您运行 dapr run
时 job-service
和 job-scheduler
应用程序中发生的事情。
观看使用 Go HTTP 示例的作业 API 演示,录制于 Dapr 社区电话 #107。
我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的 discord 频道 参与讨论。
通过模拟系统故障来了解Dapr的弹性功能。在本快速入门中,您将:
在继续快速入门之前,请选择您偏好的Dapr SDK语言。
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd ../state_management/python/sdk/order-processor
安装依赖项
pip3 install -r requirements.txt
在Dapr边车的支持下运行order-processor
服务。然后,Dapr边车会加载位于资源目录中的弹性规范:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- order-processor
spec:
policies:
retries:
retryForever:
policy: constant
duration: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
dapr run --app-id order-processor --resources-path ../../../resources/ -- python3
应用程序启动后,order-processor
服务会将orderId
键值对写入和读取到statestore
的Redis实例中在statestore.yaml
组件中定义。
== APP == Saving Order: { orderId: '1' }
== APP == Getting Order: { orderId: '1' }
== APP == Saving Order: { orderId: '2' }
== APP == Getting Order: { orderId: '2' }
== APP == Saving Order: { orderId: '3' }
== APP == Getting Order: { orderId: '3' }
== APP == Saving Order: { orderId: '4' }
== APP == Getting Order: { orderId: '4' }
通过停止在开发机器上执行dapr init
时初始化的Redis容器实例来模拟故障。一旦实例停止,来自order-processor
服务的写入和读取操作将开始失败。
由于resiliency.yaml
规范将statestore
定义为组件目标,所有失败的请求将自动应用重试和断路器策略:
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
在新的终端窗口中,运行以下命令以停止Redis:
docker stop dapr_redis
一旦Redis停止,请求开始失败,并应用名为retryForever
的重试策略。以下输出显示了来自order-processor
服务的日志:
INFO[0006] Error processing operation component[statestore] output. Retrying...
根据retryForever
策略,重试将以5秒间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦5次连续重试失败,断路器策略simpleCB
被触发,断路器打开,停止所有请求:
INFO[0026] Circuit breaker "simpleCB-statestore" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from half-open to open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from half-open to closed
只要Redis容器停止,这种半开/打开行为将继续。
当您在机器上重新启动Redis容器后,应用程序将无缝恢复并继续之前的操作。
docker start dapr_redis
INFO[0036] Recovered processing operation component[statestore] output.
== APP == Saving Order: { orderId: '5' }
== APP == Getting Order: { orderId: '5' }
== APP == Saving Order: { orderId: '6' }
== APP == Getting Order: { orderId: '6' }
== APP == Saving Order: { orderId: '7' }
== APP == Getting Order: { orderId: '7' }
== APP == Saving Order: { orderId: '8' }
== APP == Getting Order: { orderId: '8' }
== APP == Saving Order: { orderId: '9' }
== APP == Getting Order: { orderId: '9' }
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd ../state_management/javascript/sdk/order-processor
安装依赖项
npm install
在Dapr边车的支持下运行order-processor
服务。然后,Dapr边车会加载位于资源目录中的弹性规范:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
dapr run --app-id order-processor --resources-path ../../../resources/ -- npm start
应用程序启动后,order-processor
服务会将orderId
键值对写入和读取到statestore
的Redis实例中在statestore.yaml
组件中定义。
== APP == Saving Order: { orderId: '1' }
== APP == Getting Order: { orderId: '1' }
== APP == Saving Order: { orderId: '2' }
== APP == Getting Order: { orderId: '2' }
== APP == Saving Order: { orderId: '3' }
== APP == Getting Order: { orderId: '3' }
== APP == Saving Order: { orderId: '4' }
== APP == Getting Order: { orderId: '4' }
通过停止在开发机器上执行dapr init
时初始化的Redis容器实例来模拟故障。一旦实例停止,来自order-processor
服务的写入和读取操作将开始失败。
由于resiliency.yaml
规范将statestore
定义为组件目标,所有失败的请求将自动应用重试和断路器策略:
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
在新的终端窗口中,运行以下命令以停止Redis:
docker stop dapr_redis
一旦Redis停止,请求开始失败,并应用名为retryForever
的重试策略。以下输出显示了来自order-processor
服务的日志:
INFO[0006] Error processing operation component[statestore] output. Retrying...
根据retryForever
策略,重试将以5秒间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦5次连续重试失败,断路器策略simpleCB
被触发,断路器打开,停止所有请求:
INFO[0026] Circuit breaker "simpleCB-statestore" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from half-open to open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from half-open to closed
只要Redis容器停止,这种半开/打开行为将继续。
当您在机器上重新启动Redis容器后,应用程序将无缝恢复并继续之前的操作。
docker start dapr_redis
INFO[0036] Recovered processing operation component[statestore] output.
== APP == Saving Order: { orderId: '5' }
== APP == Getting Order: { orderId: '5' }
== APP == Saving Order: { orderId: '6' }
== APP == Getting Order: { orderId: '6' }
== APP == Saving Order: { orderId: '7' }
== APP == Getting Order: { orderId: '7' }
== APP == Saving Order: { orderId: '8' }
== APP == Getting Order: { orderId: '8' }
== APP == Saving Order: { orderId: '9' }
== APP == Getting Order: { orderId: '9' }
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd ../state_management/csharp/sdk/order-processor
安装依赖项
dotnet restore
dotnet build
在Dapr边车的支持下运行order-processor
服务。然后,Dapr边车会加载位于资源目录中的弹性规范:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
dapr run --app-id order-processor --resources-path ../../../resources/ -- dotnet run
应用程序启动后,order-processor
服务会将orderId
键值对写入和读取到statestore
的Redis实例中在statestore.yaml
组件中定义。
== APP == Saving Order: { orderId: '1' }
== APP == Getting Order: { orderId: '1' }
== APP == Saving Order: { orderId: '2' }
== APP == Getting Order: { orderId: '2' }
== APP == Saving Order: { orderId: '3' }
== APP == Getting Order: { orderId: '3' }
== APP == Saving Order: { orderId: '4' }
== APP == Getting Order: { orderId: '4' }
通过停止在开发机器上执行dapr init
时初始化的Redis容器实例来模拟故障。一旦实例停止,来自order-processor
服务的写入和读取操作将开始失败。
由于resiliency.yaml
规范将statestore
定义为组件目标,所有失败的请求将自动应用重试和断路器策略:
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
在新的终端窗口中,运行以下命令以停止Redis:
docker stop dapr_redis
一旦Redis停止,请求开始失败,并应用名为retryForever
的重试策略。以下输出显示了来自order-processor
服务的日志:
INFO[0006] Error processing operation component[statestore] output. Retrying...
根据retryForever
策略,重试将以5秒间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦5次连续重试失败,断路器策略simpleCB
被触发,断路器打开,停止所有请求:
INFO[0026] Circuit breaker "simpleCB-statestore" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from half-open to open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from half-open to closed
只要Redis容器停止,这种半开/打开行为将继续。
当您在机器上重新启动Redis容器后,应用程序将无缝恢复并继续之前的操作。
docker start dapr_redis
INFO[0036] Recovered processing operation component[statestore] output.
== APP == Saving Order: { orderId: '5' }
== APP == Getting Order: { orderId: '5' }
== APP == Saving Order: { orderId: '6' }
== APP == Getting Order: { orderId: '6' }
== APP == Saving Order: { orderId: '7' }
== APP == Getting Order: { orderId: '7' }
== APP == Saving Order: { orderId: '8' }
== APP == Getting Order: { orderId: '8' }
== APP == Saving Order: { orderId: '9' }
== APP == Getting Order: { orderId: '9' }
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd ../state_management/java/sdk/order-processor
安装依赖项
mvn clean install
在Dapr边车的支持下运行order-processor
服务。然后,Dapr边车会加载位于资源目录中的弹性规范:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
dapr run --app-id order-processor --resources-path ../../../resources/ -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
应用程序启动后,order-processor
服务会将orderId
键值对写入和读取到statestore
的Redis实例中在statestore.yaml
组件中定义。
== APP == Saving Order: { orderId: '1' }
== APP == Getting Order: { orderId: '1' }
== APP == Saving Order: { orderId: '2' }
== APP == Getting Order: { orderId: '2' }
== APP == Saving Order: { orderId: '3' }
== APP == Getting Order: { orderId: '3' }
== APP == Saving Order: { orderId: '4' }
== APP == Getting Order: { orderId: '4' }
通过停止在开发机器上执行dapr init
时初始化的Redis容器实例来模拟故障。一旦实例停止,来自order-processor
服务的写入和读取操作将开始失败。
由于resiliency.yaml
规范将statestore
定义为组件目标,所有失败的请求将自动应用重试和断路器策略:
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
在新的终端窗口中,运行以下命令以停止Redis:
docker stop dapr_redis
一旦Redis停止,请求开始失败,并应用名为retryForever
的重试策略。以下输出显示了来自order-processor
服务的日志:
INFO[0006] Error processing operation component[statestore] output. Retrying...
根据retryForever
策略,重试将以5秒间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦5次连续重试失败,断路器策略simpleCB
被触发,断路器打开,停止所有请求:
INFO[0026] Circuit breaker "simpleCB-statestore" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from half-open to open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from half-open to closed
只要Redis容器停止,这种半开/打开行为将继续。
当您在机器上重新启动Redis容器后,应用程序将无缝恢复并继续之前的操作。
docker start dapr_redis
INFO[0036] Recovered processing operation component[statestore] output.
== APP == Saving Order: { orderId: '5' }
== APP == Getting Order: { orderId: '5' }
== APP == Saving Order: { orderId: '6' }
== APP == Getting Order: { orderId: '6' }
== APP == Saving Order: { orderId: '7' }
== APP == Getting Order: { orderId: '7' }
== APP == Saving Order: { orderId: '8' }
== APP == Getting Order: { orderId: '8' }
== APP == Saving Order: { orderId: '9' }
== APP == Getting Order: { orderId: '9' }
对于此示例,您将需要:
克隆快速入门仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
在终端窗口中,导航到order-processor
目录。
cd ../state_management/go/sdk/order-processor
安装依赖项
go build .
在Dapr边车的支持下运行order-processor
服务。然后,Dapr边车会加载位于资源目录中的弹性规范:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
dapr run --app-id order-processor --resources-path ../../../resources -- go run .
应用程序启动后,order-processor
服务会将orderId
键值对写入和读取到statestore
的Redis实例中在statestore.yaml
组件中定义。
== APP == Saving Order: { orderId: '1' }
== APP == Getting Order: { orderId: '1' }
== APP == Saving Order: { orderId: '2' }
== APP == Getting Order: { orderId: '2' }
== APP == Saving Order: { orderId: '3' }
== APP == Getting Order: { orderId: '3' }
== APP == Saving Order: { orderId: '4' }
== APP == Getting Order: { orderId: '4' }
通过停止在开发机器上执行dapr init
时初始化的Redis容器实例来模拟故障。一旦实例停止,来自order-processor
服务的写入和读取操作将开始失败。
由于resiliency.yaml
规范将statestore
定义为组件目标,所有失败的请求将自动应用重试和断路器策略:
targets:
components:
statestore:
outbound:
retry: retryForever
circuitBreaker: simpleCB
在新的终端窗口中,运行以下命令以停止Redis:
docker stop dapr_redis
一旦Redis停止,请求开始失败,并应用名为retryForever
的重试策略。以下输出显示了来自order-processor
服务的日志:
INFO[0006] Error processing operation component[statestore] output. Retrying...
根据retryForever
策略,重试将以5秒间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦5次连续重试失败,断路器策略simpleCB
被触发,断路器打开,停止所有请求:
INFO[0026] Circuit breaker "simpleCB-statestore" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0031] Circuit breaker "simpleCB-statestore" changed state from half-open to open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from open to half-open
INFO[0036] Circuit breaker "simpleCB-statestore" changed state from half-open to closed
只要Redis容器停止,这种半开/打开行为将继续。
当您在机器上重新启动Redis容器后,应用程序将无缝恢复并继续之前的操作。
docker start dapr_redis
INFO[0036] Recovered processing operation component[statestore] output.
== APP == Saving Order: { orderId: '5' }
== APP == Getting Order: { orderId: '5' }
== APP == Saving Order: { orderId: '6' }
== APP == Getting Order: { orderId: '6' }
== APP == Saving Order: { orderId: '7' }
== APP == Getting Order: { orderId: '7' }
== APP == Saving Order: { orderId: '8' }
== APP == Getting Order: { orderId: '8' }
== APP == Saving Order: { orderId: '9' }
== APP == Getting Order: { orderId: '9' }
我们正在不断努力改进我们的快速入门示例,并重视您的反馈。您觉得这个快速入门有帮助吗?您有改进建议吗?
加入我们的discord频道讨论。
了解更多关于弹性功能及其如何与Dapr的构建块API协作。
探索Dapr教程 >>通过模拟系统故障来测试Dapr的弹性功能。在本入门指南中,您将会:
checkout
和order-processor
。checkout
将持续向order-processor
发起Dapr服务调用请求。在继续入门指南之前,选择您偏好的Dapr SDK语言版本。
对于此示例,您将需要:
克隆入门指南仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从入门指南目录的根目录导航到order-processor
目录。
cd service_invocation/python/http/order-processor
安装依赖项:
pip3 install -r requirements.txt
运行order-processor
服务及其Dapr sidecar。
dapr run --app-port 8001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- python3 app.py
checkout
服务应用程序在新的终端窗口中,从入门指南目录的根目录导航到checkout
目录。
cd service_invocation/python/http/checkout
安装依赖项:
pip3 install -r requirements.txt
运行checkout
服务及其Dapr sidecar。
dapr run --app-id checkout --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3500 -- python3 app.py
Dapr sidecar随后加载位于资源目录中的弹性配置:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
当两个服务和sidecar都在运行时,注意订单如何通过Dapr服务调用从checkout
服务传递到order-processor
服务。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
通过停止order-processor
服务来模拟故障。一旦实例停止,来自checkout
服务的服务调用操作将开始失败。
由于resiliency.yaml
配置将order-processor
服务定义为弹性目标,所有失败的请求将应用重试和断路器策略:
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
在order-processor
窗口中,停止服务:
CTRL + C
一旦第一个请求失败,名为retryForever
的重试策略将被应用:
INFO[0005] Error processing operation endpoint[order-processor, order-processor:orders]. Retrying...
重试将以5秒的间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦连续5次重试失败,断路器策略simpleCB
将被触发,断路器打开,停止所有请求:
INFO[0025] Circuit breaker "order-processor:orders" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
这种半开/打开行为将持续到order-processor
服务停止为止。
一旦您重新启动order-processor
服务,应用程序将无缝恢复,继续接受订单请求。
在order-processor
服务终端中,重新启动应用程序:
dapr run --app-port 8001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- python3 app.py
checkout
服务输出:
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
对于此示例,您将需要:
克隆入门指南仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从入门指南目录的根目录导航到order-processor
目录。
cd service_invocation/javascript/http/order-processor
安装依赖项:
npm install
运行order-processor
服务及其Dapr sidecar。
dapr run --app-port 5001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- npm start
checkout
服务应用程序在新的终端窗口中,从入门指南目录的根目录导航到checkout
目录。
cd service_invocation/javascript/http/checkout
安装依赖项:
npm install
运行checkout
服务及其Dapr sidecar。
dapr run --app-id checkout --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3500 -- npm start
Dapr sidecar随后加载位于资源目录中的弹性配置:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
当两个服务和sidecar都在运行时,注意订单如何通过Dapr服务调用从checkout
服务传递到order-processor
服务。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
通过停止order-processor
服务来模拟故障。一旦实例停止,来自checkout
服务的服务调用操作将开始失败。
由于resiliency.yaml
配置将order-processor
服务定义为弹性目标,所有失败的请求将应用重试和断路器策略:
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
在order-processor
窗口中,停止服务:
CMD + C
CTRL + C
一旦第一个请求失败,名为retryForever
的重试策略将被应用:
INFO[0005] Error processing operation endpoint[order-processor, order-processor:orders]. Retrying...
重试将以5秒的间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦连续5次重试失败,断路器策略simpleCB
将被触发,断路器打开,停止所有请求:
INFO[0025] Circuit breaker "order-processor:orders" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
这种半开/打开行为将持续到order-processor
服务停止为止。
一旦您重新启动order-processor
服务,应用程序将无缝恢复,继续接受订单请求。
在order-processor
服务终端中,重新启动应用程序:
dapr run --app-port 5001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- npm start
checkout
服务输出:
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
对于此示例,您将需要:
克隆入门指南仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从入门指南目录的根目录导航到order-processor
目录。
cd service_invocation/csharp/http/order-processor
安装依赖项:
dotnet restore
dotnet build
运行order-processor
服务及其Dapr sidecar。
dapr run --app-port 7001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- dotnet run
checkout
服务应用程序在新的终端窗口中,从入门指南目录的根目录导航到checkout
目录。
cd service_invocation/csharp/http/checkout
安装依赖项:
dotnet restore
dotnet build
运行checkout
服务及其Dapr sidecar。
dapr run --app-id checkout --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3500 -- dotnet run
Dapr sidecar随后加载位于资源目录中的弹性配置:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
当两个服务和sidecar都在运行时,注意订单如何通过Dapr服务调用从checkout
服务传递到order-processor
服务。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
通过停止order-processor
服务来模拟故障。一旦实例停止,来自checkout
服务的服务调用操作将开始失败。
由于resiliency.yaml
配置将order-processor
服务定义为弹性目标,所有失败的请求将应用重试和断路器策略:
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
在order-processor
窗口中,停止服务:
CMD + C
CTRL + C
一旦第一个请求失败,名为retryForever
的重试策略将被应用:
INFO[0005] Error processing operation endpoint[order-processor, order-processor:orders]. Retrying...
重试将以5秒的间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦连续5次重试失败,断路器策略simpleCB
将被触发,断路器打开,停止所有请求:
INFO[0025] Circuit breaker "order-processor:orders" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
这种半开/打开行为将持续到order-processor
服务停止为止。
一旦您重新启动order-processor
服务,应用程序将无缝恢复,继续接受订单请求。
在order-processor
服务终端中,重新启动应用程序:
dapr run --app-port 7001 --app-id order-processor --app-protocol http --dapr-http-port 3501 -- dotnet run
checkout
服务输出:
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
对于此示例,您将需要:
克隆入门指南仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从入门指南目录的根目录导航到order-processor
目录。
cd service_invocation/java/http/order-processor
安装依赖项:
mvn clean install
运行order-processor
服务及其Dapr sidecar。
dapr run --app-id order-processor --resources-path ../../../resources/ --app-port 9001 --app-protocol http --dapr-http-port 3501 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
checkout
服务应用程序在新的终端窗口中,从入门指南目录的根目录导航到checkout
目录。
cd service_invocation/java/http/checkout
安装依赖项:
mvn clean install
运行checkout
服务及其Dapr sidecar。
dapr run --app-id checkout --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3500 -- java -jar target/CheckoutService-0.0.1-SNAPSHOT.jar
Dapr sidecar随后加载位于资源目录中的弹性配置:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
当两个服务和sidecar都在运行时,注意订单如何通过Dapr服务调用从checkout
服务传递到order-processor
服务。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
通过停止order-processor
服务来模拟故障。一旦实例停止,来自checkout
服务的服务调用操作将开始失败。
由于resiliency.yaml
配置将order-processor
服务定义为弹性目标,所有失败的请求将应用重试和断路器策略:
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
在order-processor
窗口中,停止服务:
CMD + C
CTRL + C
一旦第一个请求失败,名为retryForever
的重试策略将被应用:
INFO[0005] Error processing operation endpoint[order-processor, order-processor:orders]. Retrying...
重试将以5秒的间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦连续5次重试失败,断路器策略simpleCB
将被触发,断路器打开,停止所有请求:
INFO[0025] Circuit breaker "order-processor:orders" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
这种半开/打开行为将持续到order-processor
服务停止为止。
一旦您重新启动order-processor
服务,应用程序将无缝恢复,继续接受订单请求。
在order-processor
服务终端中,重新启动应用程序:
dapr run --app-id order-processor --resources-path ../../../resources/ --app-port 9001 --app-protocol http --dapr-http-port 3501 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar
checkout
服务输出:
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
对于此示例,您将需要:
克隆入门指南仓库中提供的示例。
git clone https://github.com/dapr/quickstarts.git
order-processor
服务在终端窗口中,从入门指南目录的根目录导航到order-processor
目录。
cd service_invocation/go/http/order-processor
安装依赖项:
go build .
运行order-processor
服务及其Dapr sidecar。
dapr run --app-port 6001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- go run .
checkout
服务应用程序在新的终端窗口中,从入门指南目录的根目录导航到checkout
目录。
cd service_invocation/go/http/checkout
安装依赖项:
go build .
运行checkout
服务及其Dapr sidecar。
dapr run --app-id checkout --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3500 -- go run .
Dapr sidecar随后加载位于资源目录中的弹性配置:
apiVersion: dapr.io/v1alpha1
kind: Resiliency
metadata:
name: myresiliency
scopes:
- checkout
spec:
policies:
retries:
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
当两个服务和sidecar都在运行时,注意订单如何通过Dapr服务调用从checkout
服务传递到order-processor
服务。
checkout
服务输出:
== APP == Order passed: {"orderId": 1}
== APP == Order passed: {"orderId": 2}
== APP == Order passed: {"orderId": 3}
== APP == Order passed: {"orderId": 4}
order-processor
服务输出:
== APP == Order received: {"orderId": 1}
== APP == Order received: {"orderId": 2}
== APP == Order received: {"orderId": 3}
== APP == Order received: {"orderId": 4}
通过停止order-processor
服务来模拟故障。一旦实例停止,来自checkout
服务的服务调用操作将开始失败。
由于resiliency.yaml
配置将order-processor
服务定义为弹性目标,所有失败的请求将应用重试和断路器策略:
targets:
apps:
order-processor:
retry: retryForever
circuitBreaker: simpleCB
在order-processor
窗口中,停止服务:
CMD + C
CTRL + C
一旦第一个请求失败,名为retryForever
的重试策略将被应用:
INFO[0005] Error processing operation endpoint[order-processor, order-processor:orders]. Retrying...
重试将以5秒的间隔无限期地继续每个失败的请求。
retryForever:
policy: constant
maxInterval: 5s
maxRetries: -1
一旦连续5次重试失败,断路器策略simpleCB
将被触发,断路器打开,停止所有请求:
INFO[0025] Circuit breaker "order-processor:orders" changed state from closed to open
circuitBreakers:
simpleCB:
maxRequests: 1
timeout: 5s
trip: consecutiveFailures >= 5
经过5秒后,断路器将切换到半开状态,允许一个请求通过以验证故障是否已解决。如果请求继续失败,断路器将再次触发回到打开状态。
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
INFO[0030] Circuit breaker "order-processor:orders" changed state from open to half-open
INFO[0030] Circuit breaker "order-processor:orders" changed state from half-open to open
这种半开/打开行为将持续到order-processor
服务停止为止。
一旦您重新启动order-processor
服务,应用程序将无缝恢复,继续接受订单请求。
在order-processor
服务终端中,重新启动应用程序:
dapr run --app-port 6001 --app-id order-processor --resources-path ../../../resources/ --app-protocol http --dapr-http-port 3501 -- go run .
checkout
服务输出:
== APP == Order passed: {"orderId": 5}
== APP == Order passed: {"orderId": 6}
== APP == Order passed: {"orderId": 7}
== APP == Order passed: {"orderId": 8}
== APP == Order passed: {"orderId": 9}
== APP == Order passed: {"orderId": 10}
order-processor
服务输出:
== APP == Order received: {"orderId": 5}
== APP == Order received: {"orderId": 6}
== APP == Order received: {"orderId": 7}
== APP == Order received: {"orderId": 8}
== APP == Order received: {"orderId": 9}
== APP == Order received: {"orderId": 10}
我们正在不断努力改进我们的入门指南示例,重视您的反馈。您觉得这个入门指南有帮助吗?您有改进建议吗?
加入我们的discord频道讨论。
访问此链接以获取有关Dapr弹性的更多信息。
探索Dapr教程 >>