Dapr 绑定(Bindings)介绍
基于云的 Serverless 产品/服务(如 Microsoft Azure Function 和 Amazon AWS Lambda)已在分布式体系结构领域获得了广泛的应用。 它们有一个优势是 使微服务能够处理来自外部系统的事件或在外部系统中调用事件 ,从而 消除了底层复杂性和管道问题 。
Serverless 基本概念入门参考 =》https://www.infoq.cn/article/s101GtcCV05_2AgKo8GD
外部资源有很多:它们包括跨不同平台和供应商的数据存储、消息系统和 Web 资源。 Dapr 绑定(Bindings)构建基块将这些相同的资源绑定功能引入 Dapr 应用程序的门阶。
Dapr 绑定(Bindings)具备的优点
- 排除连接到消息传递系统 ( 如队列和消息总线 ) 并进行轮询的复杂性;
- 聚焦于业务逻辑,而不是如何与系统交互的实现细节;
- 使代码不受 SDK 或库的跟踪;
- 处理重试和故障恢复;
- 在运行时在绑定之间切换;
- 构建具有特定于环境的绑定的可移植应用程序,不需要进行代码更改;
Dapr 绑定的用途
绑定提供了一种常用方法,用于使用来自外部系统的事件触发应用程序,或使用可选的数据负载调用外部系统。绑定非常适合事件驱动的按需计算,并有助于减少样板代码。
通过 Dapr 资源绑定,你的服务可以在直接应用程序之外的外部资源中整合业务操作。
- 外部系统触发(输入绑定,Input Binding):来自外部系统的事件可以触发你的服务中的操作,传递上下文信息。
- 内部系统触发(输出绑定,Output Binding):你的服务可以扩展操作,触发另一个外部系统中的事件,传递上下文有效负载信息。
你的 服务在没有耦合或感知外部资源的情况下进行通信。 管道封装在预定义的 Dapr 组件中。 要使用的 Dapr 组件可在运行时轻松地进行交换,无需更改代码。
例如,用户通过推文发送关键字信息时事件触发了 Twitter 帐户(触发 Dapr 的 Sidecar API),Your service 公开用于接收和处理推文的事件处理程序。 完成后,Your service 将触发( Dapr 的 Sidecar API)调用外部 Twilio 服务的事件。驱动 Twilio 发送一条包含该推文的短信。
输入绑定(Input Bindings)
输入绑定用于在发生来自外部资源的事件时触发应用程序。 可选的有效负载和元数据可以与请求一起发送。
为了接收来自输入绑定的事件:
- 定义描述绑定类型及其元数据 ( 连接信息等) 的组件 YAML;
- 监听传入事件的 HTTP 终结点(Endpoint),或使用 gRPC 原型库获取传入事件;
执行步骤如下:
- Dapr sidecar 读取绑定配置文件并订阅为外部资源指定的事件。 在示例中,事件源是 Twitter 帐户。
- 在 Twitter 上发布匹配的推文时,在 Dapr sidecar 中运行的绑定组件会选取它并触发事件。
- Dapr sidecar 调用终结点 (即为绑定) 事件处理程序。 在示例中,服务侦听端口6000 上的终结点(/tweet)上的 HTTP POST。 由于它是 HTTP POST 操作,因此事件的 JSON 有效负载在请求正文中传递。
- 处理事件后,服务将返回 HTTP 状态代码 200 OK。
如果操作应出错,将返回相应的 400 或 500 级别 HTTP 状态代码。 对于具有 至少 一次语义 传递保证的绑定,Dapr sidecar 将重试触发器。
输出绑定(Output Binding)
输出绑定允许用户调用外部资源。 可选的有效负载和元数据可与调用请求一起发送。
为了调用输出绑定:
- 定义描述绑定类型及其元数据 ( 连接信息等) 的组件 YAML;
- 使用 HTTP 终结点(Endpoint)或 gRPC 方法调用具有可选有效负载的绑定;
执行步骤如下:
- Dapr sidecar 读取绑定 Yaml 配置文件,并提供有关如何连接到外部资源的信息。 在示例中,外部资源是 Twilio SMS 帐户。
- 应用程序调用【/v1.0/bindings/sms】Dapr sidecar 上的终结点(Endpoint)。 在这种情况下,它使用 HTTP POST 调用 API,也可使用 gRPC。
- Dapr sidecar 中运行的绑定组件调用外部消息传送系统来发送消息。 该消息将包含 POST 请求中传递的有效负载。
资源绑定行为 & 发布/订阅模式的类比
两者有相似之处,但也有区别。
- 发布/订阅(Pub/Sub) 侧重于 Dapr 服务之间的 异步通信。
- 资源绑定(Input/Output Bindings) 涉及的范围更广。 它侧重于软件平台之间的 系统互操作性。
资源绑定(Input/Output Bindings)在微服务应用程序之外的不同应用程序、数据存储和服务之间交换信息。
Dapr 资源绑定的 API
由于 dapr 的运行时 daprd 是采用 go 语言编写的,所以下面的 API 均由 go 提供。
- 输入绑定
type InputBinding interface {
Init(metadata Metadata) error
Read(handler func(*ReadResponse) ([]byte, error)) error
}
- 输出绑定
type OutputBinding interface {
Init(metadata Metadata) error
Invoke(req *InvokeRequest) (*InvokeResponse, error)
Operations() []OperationKind
}
输出绑定可用于调用外部系统,也可以从中返回数据。每个输出绑定都可以决定它支持哪些操作。此信息通过该方法传达给调用方。Operations()
创建输出绑定时,需要返回项列表。例如,如果运行的组件接受 SQL 查询并返回结果集,则可以是 OperationKind,OperationKind,query
虽然组件不限于受支持的操作列表,但如果操作类型属于该操作定义,则最好使用常见组件。
更多 Dapr 绑定资源组件支持情况,请查看 =》https://docs.dapr.io/reference/components-reference/supported-bindings/
在 .NET 中使用 Dapr Bindings
以下代码继续以 BackEnd 项目环境为例,新增如下代码信息。
添加输入/输出绑定代码
- 新增 DaprBindingsController 的 API,添加如下代码:
using Dapr.Client;
using Microsoft.AspNetCore.Mvc;
using System.Text;
namespace FrontEnd.Controllers;
[Route("api/[controller]")]
[ApiController]
public class DaprBindingsController : ControllerBase
{
private readonly ILogger<DaprBindingsController> _logger;
public DaprBindingsController(ILogger<DaprBindingsController> logger)
{
_logger = logger;
}
/// <summary>
/// 输入绑定
/// </summary>
/// <returns></returns>
[HttpPost("Input")]
public async Task<ActionResult> InputAsync()
{
byte[] buffer = new byte[Request.ContentLength.Value];
using Stream stream = Request.Body;
stream.Position = 0L;
await stream.ReadAsync(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer);
_logger.LogInformation($"{DateTime.Now} ,binding(InputAsync) ===> { content }");
return Ok();
}
/// <summary>
/// 输出绑定
/// operation 操作类型:create,get,delete,list
/// </summary>
/// <param name="daprClient"></param>
/// <returns></returns>
[HttpGet("Output")]
public async Task<ActionResult> OutputAsync([FromServices] DaprClient daprClient,string data)
{
_logger.LogInformation($"{DateTime.Now} ,binding(OutputAsync) ===> {data}");
await daprClient.InvokeBindingAsync("api/DaprBindings/Output", "create", $"{DateTime.Now} ===> {data}");
return Ok();
}
}
添加 yaml 组件配置
组件配置的 yaml 文件,在 self-hosted 模式中,通常存放于如下位置
C:\Users\<username>\.dapr\components
- 添加 rabbitmq-input-binding.yaml 配置
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: api/DaprBindings/Input # 务必和 api 接口路由一致;
spec:
type: bindings.rabbitmq
version: v1
metadata:
- name: queueName # 队列名称
value: queue-for-input-binding # 输入绑定的队列名称
- name: host # mq 主机
value: amqp://mqtest:test123@192.168.30.71:5672 # rabbitmq 连接地址(带密码)
- 添加 rabbitmq-output-binding.yaml 配置
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: api/DaprBindings/Output
spec:
type: bindings.rabbitmq
version: v1
metadata:
- name: queueName
value: queue-for-output-binding
- name: host
value: amqp://mqtest:test123@192.168.30.71:5672
绑定服务测试
- 启动 BackEnd 服务
dapr run --dapr-grpc-port 50010 --dapr-http-port 3510 --app-port 5001 --app-id backend dotnet run
- 浏览器查看 swagger 页面
http://localhost:5001/swagger/index.html
- 浏览器查看 RabbitMQ 管理界面
输入绑定队列名称:queue-for-input-binding
输出绑定队列名称:queue-for-output-binding
- 测试输入绑定,rabbitmq 管理页面 publish 一条消息
- 测试输出绑定,在 swagger 页面接口写入消息操作,显示如下信息:
curl 命令执行:
curl -X 'GET' \
'http://localhost:5001/api/DaprBindings/Output?data=dapr-uotput-6661' \
-H 'accept: */*'
- 查看 rabbitmq 中的输出绑定队列,已经有消息产生
总结
使用 Dapr 的 Bindings 构建块,整个代码的可以移植性明显增强,大大减少了传统 SDK 模式的代码量,同时还抽象了使用外部资源组件的能力,同等能力的组件在运行时无缝切换,并且整个过程无需维护第三方资源组件的连接通信代码,只需要配置对应的 yaml 文件即可,让开发人员有更多的精力专注于业务本体的代码编写;