Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。Dapr 确保开发人员专注于编写业务逻辑,不必分神解决分布式系统难题,从而显著提高了生产力。Dapr 降低了构建微服务架构类现代云原生应用的门槛。
本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用
MacOS & Dapr 1.8:
sudo curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash
Linux/Windows 安装方式:
Dapr 初始化包括:
dapr init
dapr -v
CLI version: 1.8.0
Runtime version: 1.8.0
如前所述,dapr init
命令会启动几个容器,这些容器将帮助您开始使用 Dapr
。验证您有运行 daprio/dapr
、openzipkin/zipkin
和 redis
映像的容器实例:
在 dapr init
上,CLI
还会创建一个默认组件文件夹,其中包含几个 YAML
文件,其中包含状态存储、Pub/sub
和 Zipkin
的定义。Dapr sidecar
将读取这些组件并使用:
通过打开您的组件目录进行验证:
%UserProfile%\.dapr
下~/.dapr
下ls $HOME/.dapr
bin components config.yaml
运行 Dapr sidecar 并试用 state API
dapr run
命令启动一个应用程序,以及一个 sidecar。
启动一个 Dapr sidecar,它将在端口 3500 上侦听名为 myapp
的空白应用程序:
dapr run --app-id myapp --dapr-http-port 3500
由于没有使用上述命令定义自定义组件文件夹,因此 Dapr 使用在 dapr init
流程期间创建的默认组件定义。
使用对象更新状态。新状态将如下所示:
[
{
"key": "name",
"value": "Bruce Wayne"
}
]
请注意,包含在状态中的每个对象都有一个分配有值为 name
的 key
。您将在下一步中使用该 key
。
使用以下命令保存新的状态对象:
curl -X POST -H "Content-Type: application/json" -d '[{ "key": "name", "value": "Bruce Wayne"}]' http://localhost:3500/v1.0/state/statestore
使用带有 key 为 name
的状态检索您刚刚存储在 state 中的对象。在同一终端窗口中,运行以下命令:
curl http://localhost:3500/v1.0/state/statestore/name
docker exec -it dapr_redis redis-cli
列出 Redis 键以查看 Dapr 如何使用您提供给 dapr run 的 app-id 作为 key
的前缀创建键值对:
keys *
"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
所有官方示例笔者均在 MacOS/NodeJs v16.16.0
下实战完成。
使用 Dapr 的服务调用构建块,您的应用程序可以与其他应用程序可靠且安全地通信。
git clone https://github.com/dapr/quickstarts.git
从 quickstarts 的根目录导航到 order-processor
目录。
cd service_invocation/javascript/http/order-processor
安装依赖项:
npm install
与 Dapr sidecar 一起运行 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);
});
在新的终端窗口中,从 quickstarts 根目录导航到 checkout
目录。
cd service_invocation/javascript/http/checkout
安装依赖项:
npm install
与 Dapr sidecar 一起运行 checkout
服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 -- npm start
在 checkout
服务中,您会注意到无需重写您的应用程序代码即可使用 Dapr 的服务调用。您可以通过简单地添加 dapr-app-id
header 来启用服务调用,该 header 指定目标服务的 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 在任何 Dapr 实例上调用应用程序。在代码中,sidecar 编程模型鼓励每个应用程序与其自己的 Dapr 实例通信。Dapr 实例随后发现并相互通信。
checkout
& order-processor
服务输出:
让我们看一下 Dapr 的状态管理构建块。您将使用 Redis 进行状态存储,来保存、获取和删除你的状态,您也可以将其换成任何一种受 Dapr 支持的状态存储。
在终端窗口中,导航到 order-processor
目录。
cd state_management/javascript/sdk/order-processor
安装依赖项,其中将包括 JavaScript SDK 中的 dapr-client
包:
npm install
验证您在服务目录中包含以下文件:
package.json
package-lock.json
与 Dapr sidecar 一起运行 order-processor
服务。
dapr run --app-id order-processor --components-path ../../../components/ -- npm run start
order-processor
服务将 orderId key/value
写入、读取和删除到 statestore.yaml
组件中定义的 statestore
实例。一旦服务启动,它就会执行一个循环。
const client = new DaprClient(DAPR_HOST, DAPR_HTTP_PORT);
// 将 state 保存到 state store 中
client.state.save(STATE_STORE_NAME, [
{
key: orderId.toString(),
value: order
}
]);
console.log("Saving Order: ", order);
// 从 state store 中获取 state
var result = client.state.get(STATE_STORE_NAME, orderId.toString());
result.then(function(val) {
console.log("Getting Order: ", val);
});
// 从 state store 中删除 state
client.state.delete(STATE_STORE_NAME, orderId.toString());
result.then(function(val) {
console.log("Deleting Order: ", val);
});
请注意,正如上面代码中所指定的,代码将应用程序状态保存
在 Dapr 状态存储中,读取
它,然后将其删除
。
Order-processor 输出:
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 的发布和订阅构建块
让我们看一下 Dapr 的发布和订阅 (Pub/sub) 构建块。您将运行发布者微服务和订阅者微服务,以演示 Dapr 如何启用发布/订阅模式。
在终端窗口中,从 quickstarts 根目录导航到 order-processor 目录。
cd pub_sub/javascript/sdk/order-processor
安装依赖项,其中将包括 JavaScript SDK 中的 dapr-client 包:
npm install
验证您在服务目录中包含以下文件:
package.json
package-lock.json
与 Dapr sidecar 一起运行 order-processor subscriber 服务。
dapr run --app-port 5001 --app-id order-processing --app-protocol http --dapr-http-port 3501 --components-path ../../../components -- npm run start
在 order-processor 订阅者中,我们订阅名为 order_pub_sub 的 Redis 实例(如 pubsub.yaml 组件中所定义)和 topic orders。这使您的应用程序代码能够通过 Dapr sidecar 与 Redis 组件实例通信。
server.pubsub.subscribe("order_pub_sub", "orders", (data) => console.log("Subscriber received: " + JSON.stringify(data)));
在新的终端窗口中,从 Quickstarts 克隆目录的根目录导航到 checkout 目录。
cd pub_sub/javascript/sdk/checkout
安装依赖项,其中将包括 JavaScript SDK 中的 dapr-client 包:
npm install
验证您在服务目录中包含以下文件:
package.json
package-lock.json
与 Dapr sidecar 一起运行 checkout 发布者服务。
dapr run --app-id checkout --app-protocol http --dapr-http-port 3500 --components-path ../../../components -- npm run start
在 checkout
发布者服务中,我们将 orderId 消息发布到名为 order_pub_sub
的 Redis 实例(在 pubsub.yaml 组件中定义)和 topic orders
。服务一启动,就会循环发布:
const client = new DaprClient(DAPR_HOST, DAPR_HTTP_PORT);
await client.pubsub.publish(PUBSUB_NAME, PUBSUB_TOPIC, order);
console.log("Published data: " + JSON.stringify(order));
请注意,正如上面代码中所指定的,发布者将一个随机数推送到 Dapr sidecar,而订阅者接收它。
发布者 & 订阅者输出:
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: order_pub_sub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
在 YAML 文件中:
metadata/name
是您的应用程序与组件对话的方式。spec/metadata
定义与组件实例的连接。scopes
指定哪个应用程序可以使用该组件。开始使用 Dapr 的 Binding 构建块
让我们看一下 Dapr 的 Bindings 构建块。使用绑定,您可以:
接下来您将使用输入 Cron binding 安排批处理脚本每 10 秒运行一次。该脚本使用 PostgreSQL Dapr binding 处理 JSON 文件并将数据输出到 SQL 数据库。
在您机器上的 Docker 容器中本地运行 PostgreSQL 实例。示例包含一个 Docker Compose 文件,用于在本地自定义、构建、运行和初始化带有默认 orders
表的 postgres 容器。
在终端窗口中,从 quickstarts 根目录导航到 bindings/db
目录。
cd bindings/db
运行以下命令来设置容器:
docker compose up
在新的终端窗口中,导航到 SDK 目录。
cd bindings/javascript/sdk/batch
安装依赖项:
npm install
与 Dapr sidecar 一起运行 batch-sdk 服务。
dapr run --app-id batch-sdk --app-port 5002 --dapr-http-port 3500 --components-path ../../../components -- node index.js
process_batch 函数内的代码每 10 秒执行一次(在 components 目录的 binding-cron.yaml 中定义)。绑定触发器在 Dapr sidecar 的 Flask 应用程序中查找通过 HTTP POST 调用的路由。
async function start() {
await server.binding.receive(cronBindingName,processBatch);
await server.start();
}
batch-sdk 服务使用 binding-postgres.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 作为 payload 调用输出绑定。
你的输出绑定的 print 语句输出:
在新终端中,验证是否已将相同的数据插入到数据库中。
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;
输出应如下所示:
components\binding-cron.yaml 组件文件
当您执行 dapr run 命令并指定组件路径时,Dapr sidecar:
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"
注意:binding-cron.yaml 的元数据部分包含一个 Cron 表达式,用于指定调用绑定的频率。
component\binding-postgres.yaml 组件文件
当您执行 dapr run 命令并指定组件路径时,Dapr sidecar:
使用 binding-postgres.yaml 组件,您可以轻松换出后端数据库绑定,而无需更改代码。
本快速入门包含的 PostgreSQL binding-postgres.yaml 文件包含以下内容:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: sqldb
namespace: quickstarts
spec:
type: bindings.postgres
version: v1
metadata:
- name: url
value: "user=postgres password=docker host=localhost port=5432 dbname=orders pool_min_conns=1 pool_max_conns=10"
在 YAML 文件中:
开始使用 Dapr 的 Secrets Management 构建块
Dapr 提供了一个专用的 secrets API,允许开发人员从 secrets store 中检索 secrets。接下来:
在终端窗口中,导航到 order-processor
目录。
cd secrets_management/javascript/sdk/order-processor
安装依赖项:
npm install
与 Dapr sidecar 一起运行 order-processor 服务。
dapr run --app-id order-processor --components-path ../../../components/ -- npm start
order-processor 服务
请注意下面的 order-processor 服务如何指向:
// 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
定义与组件使用的 secret 的连接。secrets.json 文件
SECRET_NAME
在位于 secrets_management/javascript/sdk/order-processor
的 secrets.json
文件中定义:
{
"secret": "YourPasskeyHere"
}
正如上面的应用程序代码中所指定的,order-processor 服务通过 Dapr secret 存储检索 secret 并将其显示在控制台中。