sidecar构架之dapr服务调用

服务调用示意

dapr调用示意

ZidVEq

sidecar 之间的通信都是 gRPC (3、6)
application与sidecar之间的通信是http/grpc (1、7、4、5)

  1. 服务 A 对服务 B 发起HTTP/gRPC的调用。
  2. Dapr使用服务注册发现平台的名称解析组件发现服务B的位置。(例如:mDNS、consul等)
  3. Dapr 将消息转发至服务 B的 Dapr 边车
    注: Dapr 边车之间的所有调用考虑到性能都优先使用 gRPC。 仅服务与 Dapr 边车之间的调用可以是 HTTP 或 gRPC
  4. 服务 B的 Dapr 边车将请求转发至服务 B 上的特定端点 (或方法) 。 服务 B 随后运行其业务逻辑代码。
  5. 服务 B 发送响应给服务 A。 响应将转至服务 B 的边车。
  6. Dapr 将消息转发至服务 A 的 Dapr 边车。
  7. 服务 A 接收响应。

python和node服务示例

ipdCVT

  1. Node.js应用程序有一个app IDnodeapp的Dapr应用程序。 当python应用程序通过 POST http://localhost:3500/v1.0/invoke/nodeapp/method/neworder 调用 Node.js 应用程序的 neworder方法时, 首先会到达python app的本地dapr sidecar。
  2. Dapr 使用本地机器运行的名称解析组件(在这种情况下自动运行的 mDNS),发现 Node.js 应用的位置。
  3. Dapr 使用刚刚收到的位置将请求转发到 Node.js 应用的 sidecar。
  4. Node.js 应用的 sidecar 将请求转发到 Node.js 应用程序。 Node.js 应用执行其业务逻辑,记录收到的消息,然后将订单 ID 存储到 Redis (未在图表中显示)中
  5. Node.js应 用程序通过 Node.js sidecar 向 Python 应用程序发送一个响应。
  6. Dapr 转发响应到 Python 的 Dapr sidecar
  7. Python 应用程序收到响应。

API 和端口

Dapr runtime 对外提供两个 API,分别是 Dapr HTTP API 和 Dapr gRPC API。另外两个 dapr runtime 之间的通讯 (Dapr internal API) 固定用 gRPC 协议。

两个 Dapr API 对外暴露的端口

  • 3500: HTTP 端口,可以通过命令行参数 dapr-http-port 设置
  • 50001: gRPC 端口,可以通过命令行参数 dapr-grpc-port 设置
1
dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 --dapr-grpc-port 50001 node app.js

Dapr internal API 是内部端口,比较特殊,没有固定的默认值,而是取任意随机可用端口。也可以通过命令行参数 dapr-internal-grpc-port 设置。

为了向服务器端的应用发送请求,dapr 需要获知应用在哪个端口监听并处理请求,这个信息通过命令行参数 app-port 设置。Dapr 的示例中一般喜欢用 3000 端口。

实践

我们使用官方的教程来实验一下

启动服务

我们先开启node的订单服务

1
2
3
cd ./hello-world/node
npm install
dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js
1
ℹ️  Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 54626

服务解析

启动后,我们先看看订单服务有什么操作
创建订单:/neworder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
app.post('/neworder', async (req, res) => {
const data = req.body.data;
const orderId = data.orderId;
console.log("Got a new order! Order ID: " + orderId);

const state = [{
key: "order",
value: data
}];

try {
const response = await fetch(stateUrl, {
method: "POST",
body: JSON.stringify(state),
headers: {
"Content-Type": "application/json"
}
})
if (!response.ok) {
throw "Failed to persist state.";
}
console.log("Successfully persisted state.");
res.status(200).send();
} catch (error) {
console.log(error);
res.status(500).send({message: error});
}
});

获取订单:/order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/order', async (_req, res) => {
try {
const response = await fetch(`${stateUrl}/order`)
if (!response.ok) {
throw "Could not get state.";
}
const orders = await response.text();
res.send(orders);
}
catch (error) {
console.log(error);
res.status(500).send({message: error});
}
});

服务调用

http调用方式

yMot68

使用 Dapr cli 调用
1
2
3
dapr invoke --app-id nodeapp --method neworder --data-file sample.json
// sample.json
{"data":{"orderId":"42"}}
1
2
3
4
// 创建订单
dapr invoke --app-id nodeapp --method neworder --data '{"data": { "orderId": "42" } }'
// 获取订单
dapr invoke --app-id nodeapp --method order --verb GET
使用curl调用

通过dapr的Endpoint

1
2
3
4
// 创建订单
curl -XPOST -d @sample.json -H Content-Type:application/json http://localhost:3500/v1.0/invoke/nodeapp/method/neworder
// 获取订单
curl http://localhost:3500/v1.0/invoke/nodeapp/method/order

通过Node程序自己的Endpoint,这样不通过Dapr Sidecar。

1
curl -XPOST -d @sample.json -H "Content-Type:application/json" http://localhost:3000/neworder
使用Postman调用

6iLdya
0cTGFL

使用SDK

默认HTTP

1
2
3
4
5
6
7
8
9
10
11
12
13
import { DaprClient } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort);

const serviceAppId = "nodeapp";
const serviceMethod = "neworder";

// POST Request
const response = await client.invoker.invoke(serviceAppId , serviceMethod , HttpMethod.POST, { data: {"orderId":"42"} });

const serviceMethod2 = "order";
// GET Request
const response = await client.invoker.invoke(serviceAppId , serviceMethod2 , HttpMethod.GET);

另一个程序语言的服务调用(python)

接下来部署Python的程序。Python也得先装运行环境:

1
2
3
cd ./hello-world/python
sudo apt install python3-pip
dapr run --app-id pythonapp --dapr-http-port 3501 python3 app.py

启动成功了。因为Python自己不提供服务,所以–app-port不用指定。–dapr-http-port是3501,这是自己的Sidecar用的端口,不能跟别人的重了。再看看刚才Node的窗口,不停的有新的Request过来,就是Python程序来的每隔一秒的Request。
最后看一下dapr list的结果:

1
2
3
APP ID     HTTP PORT  GRPC PORT  APP PORT  COMMAND         AGE  CREATED              PID
nodeapp 3500 35485 3000 node app.js 41m 2020-12-27 00:54.54 18395
pythonapp 40175 33349 0 python3 app.py 1m 2020-12-27 01:36.27 31185

GRPC调用方式

ryUFBq

GRPC调用方式,在application得起一个GRPC服务,然后通过dapr调用application的GRPC服务

使用SDK
1
2
3
4
5
6
7
import { DaprClient, CommunicationProtocol } from "@dapr/dapr";
const client = new DaprClient(daprHost, daprPort, CommunicationProtocol.GRPC);

// Create a Proxy that allows us to use our gRPC code

const clientProxy = await client.proxy.create<GreeterClient>(GreeterClient);