Dapr是一套开源可移植事件驱动型运行时,能够帮助开发人员轻松构建起能够运行在云端及边缘位置的高弹性、微服务、无状态/有状态应用程序。Dapr当中包含多种编程语言与开发者框架,同时显著简化了应用程序(例如示例中提到的电子商务应用)的构建流程。
为什么要使用Dapr:
解决了以下问题:
Distributed App Challenges
- Hard to incrementally migrate from existing code to a microservices architecture.(难以从已有的代码逐步迁移到微服务架构)
- Many programming model runtimes have narrow language support and tightly controlled feature sets.(很多运行时模型支持的较少的开发语言及对功能特性有较多的强制限制)
- Event-driven architectures and state handling are complicated making them difficult to scale.(事件驱动架构的状态处理导致他们很难水平扩展)
- Many runtimes only target specific infrastructure platforms with limited code portability across clouds and edge.(很多运行时指定集成架的构平台,限制了代码在云及边缘计算的可移植性。)
使用Dapr :
Streamlined Microservices(效率更高的微服务)
-
Dapr enables developers using any language or framework to easily write microservices, providing industry best practices to solve distributed systems problems.(dapr 允许开发者使用任何语言与框架与开发微服务,提供工业级最好的实践去解决分布式系统的问题)
-
Dapr provides consistency and portability through open APIs and extensible components that are community-driven.(dapr 通过开放的API与社区提供的扩展组件提供一致性与扩展性)
-
Dapr handles state, resource bindings and pub/sub messaging, which enable event-driven, resilient architectures that scale.(dapr 可以处理有状态,资源绑定,订阅/发送 消息,从而支持可伸缩的事件驱动,弹性架构)
-
Dapr is platform agnostic and runs on any infrastructure, including public clouds and edge devices with its open APIs.(Dapr与平台无关,可以在任何基础设施上运行,包括公共云和具有开放api的边缘设备)
实践一下,从Hello World 开始
- 环境准备
- hello world
环境准备
参考官方:https://github.com/dapr/docs/blob/master/getting-started/environment-setup.md
这里采用helm 安装。
>helm repo add dapr https://dapr.github.io/helm-charts/
"dapr" has been added to your repositories
>helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "dapr" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈
>helm install dapr dapr/dapr --namespace dapr-system
NAME: dapr
LAST DEPLOYED: Mon Oct 5 09:00:38 2020
NAMESPACE: dapr-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing Dapr: High-performance, lightweight serverless runtime for cloud and edge
Your release is named dapr.
To get started with Dapr, we recommend using our quickstarts:
https://github.com/dapr/quickstarts
For more information on running Dapr, visit:
https://dapr.io
hello world 实践
官方文档在这里: https://github.com/dapr/quickstarts/tree/master/hello-world
- 下载代码
>git clone -b release-0.11 https://github.com/dapr/quickstarts.git 正克隆到 'quickstarts'... remote: Enumerating objects: 19, done. remote: Counting objects: 100% (19/19), done. remote: Compressing objects: 100% (19/19), done. remote: Total 1963 (delta 5), reused 1 (delta 0), pack-reused 1944 接收对象中: 100% (1963/1963), 9.61 MiB | 27.00 KiB/s, 完成. 处理 delta 中: 100% (1147/1147), 完成.
2. 分析下nodejs服务方代码
- nodejs 暴露3000端口,dapr暴露端口为3500.
- nodejs 服务暴露3个endpoint:
/order 获取order的endpoint
/neworder 创建order的endpoint
/order/:id 删除order的endpoint
这三个endpoint通过dapr state管理持久化到statestore.
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// ------------------------------------------------------------
const express = require('express');
const bodyParser = require('body-parser');
require('isomorphic-fetch');
const app = express();
app.use(bodyParser.json());
const daprPort = process.env.DAPR_HTTP_PORT || 3500;
const stateStoreName = `statestore`;
const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`;
const port = 3000;
app.get('/order', (_req, res) => {
fetch(`${stateUrl}/order`)
.then((response) => {
if (!response.ok) {
throw "Could not get state.";
}
return response.text();
}).then((orders) => {
res.send(orders);
}).catch((error) => {
console.log(error);
res.status(500).send({message: error});
});
});
app.post('/neworder', (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
}];
fetch(stateUrl, {
method: "POST",
body: JSON.stringify(state),
headers: {
"Content-Type": "application/json"
}
}).then((response) => {
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});
});
});
app.delete('/order/:id', (req, res) => {
const key = req.params.id;
console.log('Invoke Delete for ID ' + key);
const deleteUrl = stateUrl + '/' + key;
fetch(deleteUrl, {
method: "DELETE",
headers: {
"Content-Type": "application/json"
}
}).then((response) => {
if (!response.ok) {
throw "Failed to delete state.";
}
console.log("Successfully deleted state.");
res.status(200).send();
}).catch((error) => {
console.log(error);
res.status(500).send({message: error});
});
});
app.listen(port, () => console.log(`Node App listening on port ${port}!`));
3. 启动服务
>dapr run --app-id nodeapp --app-port 3000 --dapr-http-port 3500 node app.js
ℹ️ Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 59682
dapr 启动app-id为nodeapp 的服务。
4. 调用验证
- 通过dapr调用
- 通过post调用
- 通过python应用来调用
python 作为客户端服务调用程序如下:
# ------------------------------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------------------------------
import os
import requests
import time
dapr_port = os.getenv("DAPR_HTTP_PORT", 3500)
dapr_url = "http://localhost:{}/v1.0/invoke/nodeapp/method/neworder".format(dapr_port)
n = 0
while True:
n += 1
message = {"data": {"orderId": n}}
try:
response = requests.post(dapr_url, json=message, timeout=5)
if not response.ok:
print("HTTP %d => %s" % (response.status_code,
response.content.decode("utf-8")), flush=True)
else:
print("%s call %s success!"%(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),dapr_url))
except Exception as e:
print(e, flush=True)
time.sleep(1)
程序很简单,每隔1秒调用下服务端的/neworder 接口,打印调用结果
使用Dapr 启动python 服务,如下:
⚙ /opt/tech/git/dapr/quickstarts/hello-world release-0.11 ● dapr run --app-id pythonapp python3.8 app.py
ℹ️ Starting Dapr with id pythonapp. HTTP Port: 50499. gRPC Port: 50500
ℹ️ Checking if Dapr sidecar is listening on HTTP port 50499
== DAPR == time="2020-10-08T11:00:18.265869+08:00" level=info msg="starting Dapr Runtime -- version 0.11.0 -- commit 71f9fb5" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.265925+08:00" level=info msg="log level set to: info" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.266216+08:00" level=info msg="metrics server started on :50501/" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.metrics type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.267382+08:00" level=info msg="standalone mode configured" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.267412+08:00" level=info msg="app id: pythonapp" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.267437+08:00" level=info msg="mTLS is disabled. Skipping certificate request and tls validation" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.268035+08:00" level=info msg="local service entry announced: pythonapp -> 192.168.0.155:50505" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.contrib type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.268127+08:00" level=info msg="Initialized name resolution to standalone" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.283064+08:00" level=info msg="component loaded. name: pubsub, type: pubsub.redis" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.320585+08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.320708+08:00" level=info msg="waiting for all outstanding components to be processed" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.320868+08:00" level=info msg="component loaded. name: zipkin, type: exporters.zipkin" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.320897+08:00" level=info msg="all outstanding components processed" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321177+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. actor scan interval: 30s" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321369+08:00" level=info msg="enabled gRPC tracing middleware" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.grpc.api type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321362+08:00" level=info msg="starting connection attempt to placement service: localhost:50005" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321432+08:00" level=info msg="API gRPC server is running on port 50500" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.3215+08:00" level=info msg="enabled gRPC tracing middleware" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.grpc.internal type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321529+08:00" level=info msg="internal gRPC server is running on port 50505" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321771+08:00" level=info msg="enabled cors http middleware" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.http type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321791+08:00" level=info msg="enabled tracing http middleware" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.http type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321831+08:00" level=info msg="http server is running on port 50499" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.321858+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 54.486ms" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.327063+08:00" level=info msg="established connection to placement service at localhost:50005" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.331767+08:00" level=info msg="placement order received: lock" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.33185+08:00" level=info msg="placement order received: update" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.331871+08:00" level=info msg="placement tables updated, version: 0" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
== DAPR == time="2020-10-08T11:00:18.331901+08:00" level=info msg="placement order received: unlock" app_id=pythonapp instance=MacBook-Pro.local scope=dapr.runtime.actor type=log ver=0.11.0
ℹ️ Checking if Dapr sidecar is listening on GRPC port 50500
ℹ️ Dapr sidecar is up and running.
ℹ️ Updating metadata for app command: python3.8 app.py
✅ You're up and running! Both Dapr and your app logs will appear here.
此时服务端日志情况:(省略部分)
...
== APP == Got a new order! Order ID: 96
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 97
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 98
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 99
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 100
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 101
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 102
== APP == Successfully persisted state.
== APP == Got a new order! Order ID: 103
可以看到成功发起的调用,已经成功的保存了状态。
问题:
细心的会在想,为什么暴露的服务是/v1.0/invoke/nodeapp/method/neworder ?
下篇分析。