为什么要用 Dapr?
Dapr:面向云端与边缘的微服务构建单元
目标
使开发人员能够使用任何语言或框架编写分布式应用程序
通过提供最佳实践构建块,解决开发人员构建微服务应用程序所面临的难题
成为社区驱动、开放和供应商中立者,寻找新的贡献者
通过开放式 API 提供一致性和可移植性
跨云和边缘,与平台无关
拥抱可扩展性,提供可插拔组件,无需供应商锁定
通过高性能、轻量功能实现 IoT 和边缘方案
从现有代码中增量采用,没有运行时依赖项
工作原理
Dapr 将 side-car 式的(Service Mesh中代指无侵入地扩展能力)容器/进程注入每个计算单元。通过标准 HTTP 或gRPC 协议,side-car 式地与事件触发器交互并与计算单元通信。这使 Dapr 能够支持所有现有甚至未来的编程语言,而无需您导入框架和库。
Dapr 通过标准HTTP 谓词或 gRPC 接口提供内置的状态管理、可靠的消息传递(至少一次传递)、触发器和绑定。这允许您按照相同的编程范例编写无状态、有状态和类似参与者的服务。您可以自由选择一致性模型、线程模型和消息传递模式。
Dapr 在Kubernetes 上原生地运行,作为独立的二进制文件在您的机器上、 IoT 设备上、或作为容器运行,而不是将二进制文件注入到任何系统、云中或本地。
Dapr 使用可插拔状态存储和消息总线(如 Redis 和 gRPC)来提供广泛的通信方法,包括使用 gRPC 直接dapr 对 dapr 方式和具有保证传递和至少一次语义的异步发布订阅(Pub-Sub)方式。
特征
事件驱动的 Pub-Sub 系统,利用可插拔的组件和“至少一次语”义实现
具有可插拔组件的输入和输出绑定
具有可插拔数据存储的状态管理
一致的服务到服务(service-to-servic)的发现和调用
可选择的有状态模型:强/最终一致性,或者首次写入/最后写入获胜方式
跨平台虚拟Actor(类似Orleans)
具有流量限制功能
内置了使用开放式遥测(Open Telemetry)进行分布式跟踪
利用专用的 Operator 和 CRD 在 Kubernetes 上原生运行
通过 HTTP 和 gRPC 支持所有编程语言
支持混合云,适配来自 Azure、AWS、GCP 的各种开放组件(绑定、pub-sub、状态)
可在任何地方运行——以进程或容器化的方式
轻量(58MB 二进制文件,4MB 物理内存)
作为Side Car运行——无需特殊的 SDK 或库
专用 CLI——易于调试,拥有开发人员友好的体验
拥有.NET、Java、Dotnet、Go、Java脚本和Python的客户端
拥有可移植性与可扩展性的标准API
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const port = 3000;
app.get( '/dajgr/subscribe', (_req, res) => { res.json([ 'A', 'B' ]);
});
app.post('/A', (req, res) => { console.log("A:", req.body); res.sendStatus(200);
});
app.post('/B', (req, res) => { console.log("B:", req.body); res.sendStatus(200);
});
app.listen(port, () => console.log('Node App listening on port ${port}!'))
作为比较,以下为使用UseStartup()处理程序从ASP.NET Core CreateWebHostBuilder() 处调用的C#代码。
using System.Collections.Generic;
using System.Text.Json;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Dependencylnjection;
using Microsoft.Extensions.Hosting;
using System.IO;
namespace DaprPubSub
{ public class Startup
{ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { // Route called by Dapr runtime to get topics this app subscribes to. endpoints.MapGet("dagr/subscribe", async context => { // Returns list of topics to subscribe to as json in response. var topicsToSubscribe = new List<string>() { "TopicA", "TopicB" >; await JsonSerializer.SerializeAsync(context.Response.Body, topicsToSubscribe); }); // Route to handle events published to TopicA endpoints.MapPost("A", async context => { // Read the event form request body. using (var streamReader = new StreamReader(context.Request.Body)) { var json = await streamReader.ReadToEndAsync(); Console.WriteLine("Received event for TopicA."); Console.WriteLine($"Event Data: {json}'); } }); // Route to handle events published to TopicB endpoints.MapPost("B", async context => { // Read the event form request body. using (var streamReader = new StreamReader(context.Request.Body)) { var json = await streamReader.ReadToEndAsync(); Console.WriteLine("Received event for TopicB."); Console.WriteLine($"Event Data: {json}"); } }); }); } }
}
经由各个主题向已订阅服务发布事件,就像使用主题名称及载荷调用Dapr本地http发布API一样简单。以下节点代码示例即为如何利用Dapr发布API(本地端口3500)实现发布,当然大家也可以使用curl命令达成同样的效果:
curl -X POST http://localhost:3500/vl.0/publish/A \ -H "Content-Type: application/json" \ -d '{"status": "completed"}'
const express = require('express'’);
const path = require('path');
const request = require('request');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const port = 8080;
const daprllrl = 'http://localhost:${process.env.DAPR_HTTP_PORT || 3500}/vl.0';
app.post('/publish', (req, res) => { console.log("Publishing: ", req.body); const publishUrl = '${daprUrl}/publish/${req.body.messageType}'; request( { uri: publishUrl, method: ’POST', json: req.body } ); res.sendStatus(200);
});
app.listen(process.env.PORT || port, () => console.log('Listening on port ${port}!' 根据以上示例,在服务中使用Dapr并不涉及编译时间依赖性,只需要直接利用消息正文构建URL即可。
Sidecar架构与受支持基础设施
在Kubernetes等容器托管环境内,Dapr以side-car容器的形式运行在应用程序容器所在的Pod当中。
dapr init
(for local deployment)dapr init --kubernetes
(for Kubernetes deployment)
开发者语言SDK与框架
为了使Dapr对不同语言更自然,它还包括适用于Go、Java、JavaScript、.NET和Python的语言特定的SDK。这些 SDK 通过类型化的语言 API 公开 Dapr 构建基块中的功能,例如保存状态、发布事件或创建 Actor,而不是调用 http/gRPC API。这使开发人员能够用他们选择的语言编写无状态和有状态函数与 Actor 的组合。由于这些 SDK 共享 Dapr 运行时,您甚至可以获得跨语言执行组件和函数支持!
此外,Dapr 还可以与任何开发框架集成。例如,在 Dapr .NET SDK 中,您会发现 ASP.NET Core 的集成,它带来了有状态的路由控制器,可以响应来自其他服务的pub/sub事件,使ASP.NET Core 成为更好的构建微服务 Web 应用程序的框架。