Dapr Outbox 是1.12中的功能。

本文只介绍Dapr Outbox 执行流程,Dapr Outbox基本用法请阅读官方文档 。本文中appID=order-processor,topic=orders

本文前提知识:熟悉Dapr状态管理Dapr发布订阅Outbox 模式

Outbox 模式的核心是在同一个数据库事务中保存业务数据和待发布的事件消息,再由某个“定时任务”读取待发布的事件消息并发布事件(并删除数据库中事件消息)

相关文章:

.NET中实现Outbox模式的框架CAP,作者Savorboard

使用 dotnetcore/CAP 的本地消息表模式,圣杰

先在内部发布一个主题(topic)

要使用Dapr Outbox,在.NET中就是调用DaprClientExecuteStateTransactionAsync(...)方法(得先完成Outbox相关的配置!),调用此方法会完成事务操作(保存业务数据和待发布的事件消息)并发布事件消息。

string DAPR_STORE_NAME = "statestoresql";
var client = new DaprClientBuilder().Build();
var orderId = 1;
var order = new Order(orderId); var bytes = JsonSerializer.SerializeToUtf8Bytes(order);
var upsert = new List<StateTransactionRequest>()
{
new StateTransactionRequest(orderId.ToString(), bytes, StateOperationType.Upsert)
}; // 保存状态,并发布事件消息
await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, upsert); public record Order([property: JsonPropertyName("orderId")] int orderId);
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: orderpubsub # 发布订阅组件
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestoresql # 状态组件
spec:
type: state.mysql
version: v1
metadata:
- name: connectionString
value: "root:mysecret@tcp(localhost:3306)/?allowNativePasswords=true"
- name: outboxPublishPubsub
value: orderpubsub
- name: outboxPublishTopic
value: orders

调用ExecuteStateTransactionAsync(...)方法时,此方法把请求转发给sidecar,sidecar会发布一个内部主题。所谓内部,就是供Dapr使用,用户不用操作;所谓主题(Topic)就是一个事件;此主题格式为:namespace + appID + topic + "outbox" ,假设appID=order-processor,topic=orders,则内部主题(Topic)名就是order-processorordersoutbox(namespace 是与k8s有关),此主题用于判断事务是否执行成功。

注:该内部主题(topic)默认和事件消息使用同一个Dapr发布/订阅组件,可以通过配置状态组件的元数据(metadata配置)字段outboxPubsub单独指定内部主题所使用的发布/订阅组件。相关配置请看官方文档

主题内容CloudEvent格式,发布的事件数据如下(真正的待发布事件消息就是json中的data字段,后面就是读取的此值):

{
"data":"{\"orderId\":1}",
"datacontenttype":"text/plain",
"id":"outbox-a53e45f3-d646-4e4e-bcbf-0692ec7b9dd0",
"pubsubname":"orderpubsub",
"source":"order-processor",
"specversion":"1.0",
"time":"2024-01-25T17:12:31+08:00",
"topic":"",
"traceid":"",
"traceparent":"",
"tracestate":"",
"type":"com.dapr.event.sent"
}

有了事件的发布者,那事件的订阅者是谁呢?appID=order-processor的Dapr sidecar实例。可以是执行保存状态的sidecar程序,或者是appID=order-processor的其他sidecar。

在同一事务中保存状态和事件消息

  • 在内部主题(Topic)发布成功,会在同一事务中保存状态和事件消息,也就是将方法client.ExecuteStateTransactionAsync(...)中的数据保存到数据库。id为outbox-a53e45f3-d646-4e4e-bcbf-0692ec7b9dd0的表示需待发布事件消息,id为order-processor||1表示状态数据。事件消息和状态数据保存在同一张表state中,在mysql中其表结构和数据如下所示。

  • 如果此内部主题(Topic)发布失败,调用方直接抛异常,不会执行事务操作!state表不会有下面两条数据。

  • "eyJvcmRlcklkIjoxfQ=="既是状态数据又是待发布的事件数据;经过Base64解码,得到该值为json格式,即:{"orderId":1}

CREATE TABLE `state`  (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`value` json NOT NULL,
`isbinary` tinyint(1) NOT NULL,
`insertDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`eTag` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`expiredate` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `expiredate_idx`(`expiredate` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
id value isbinary insertDate updateDate eTag expiredate
outbox-a53e45f3-d646-4e4e-bcbf-0692ec7b9dd0 "0" 0 2024-01-25 09:22:14 2024-01-25 09:22:14 07884eed-eb5d-4887-8399-051c71206ed5
order-processor||1 "eyJvcmRlcklkIjoxfQ==" 1 2024-01-25 09:12:31 2024-01-25 09:22:14 3d1e368f-f6d8-4ccd-946d-c10090c7cc42

内部主题(Topic)的订阅者发布事件消息

数据库事务执行成功后,什么时候把事件消息发布出去呢?

事件消息发布出去是在内部主题(Topic)的订阅者中实现的,具体如下:

步骤XappIDorder-processor的sidecar接收到内部主题(Topic)发送的事件,然后通过查询判断id为outbox-a53e45f3-d646-4e4e-bcbf-0692ec7b9dd0的数据是否存在?

  • 如果存在,表示状态数据和事件消息都已保存在mysql中,则发布事件消息(事件数据就前面提到的data字段)。事件发布成功后,则删除id为outbox-a53e45f3-d646-4e4e-bcbf-0692ec7b9dd0的记录。
  • 如果不存在就直接退出,停止后续操作;事件的订阅者会多次收到订阅消息,即重复步骤X过程。

这里会有一个问题:接收到内部主题(Topic)后,状态和事件消息可能没有持久化到mysql(前面提到过,Dapr sidecar是先发布一个内部主题,再在同一事务中保存状态和事件消息)。所以获取状态执行以下重试策略。删除状态时也是此重试策略。

bo := &backoff.ExponentialBackOff{
InitialInterval: time.Millisecond * 500,// 初始间隔
MaxInterval: time.Second * 3, // 最大间隔。重试时间超过此值时,以此值为准
MaxElapsedTime: time.Second * 10, // 累计重试时间
Multiplier: 3, // 递增倍数
Clock: backoff.SystemClock,
RandomizationFactor: 0.1, // 随机因子
}

总结

Dapr Outbox 执行流程简单说就是:先发布一个内部事件,再执行保存业务数据和事件消息,内部事件的订阅者再发布真正的事件消息。Dapr轮询数据库中待发布事件消息是通过订阅一个内部主题(Topic)实现的。

因为状态保存和事件发布是在sidecar中执行,所以业务代码和事件消息不在同一个事务中!!!Dapr Outbox是把业务的状态数据和事件消息在同一个事务中保存,也就是代码client.ExecuteStateTransactionAsync(...);并且状态数据和事件消息是保存到同一张表state中。

参考:

代码

Enable the transactional outbox pattern

outbox.go

Outbox issues

Dapr Outbox 执行流程的更多相关文章

  1. 步步深入:MySQL架构总览->查询执行流程->SQL解析顺序

    前言: 一直是想知道一条SQL语句是怎么被执行的,它执行的顺序是怎样的,然后查看总结各方资料,就有了下面这一篇博文了. 本文将从MySQL总体架构--->查询执行流程--->语句执行顺序来 ...

  2. 第二天 ci执行流程

    第二天 ci执行流程 welcome 页面 this this->load 单入口框架index.php 两个文件夹 system application定义 定义常亮路径 载入 codeign ...

  3. 轻量级前端MVVM框架avalon - 执行流程2

    接上一章 执行流程1 在这一大堆扫描绑定方法中应该会哪些实现? 首先我们看avalon能帮你做什么? 数据填充,比如表单的一些初始值,切换卡的各个面板的内容({{xxx}},{{xxx|html}}, ...

  4. [Java编程思想-学习笔记]第4章 控制执行流程

    4.1  return 关键字return有两方面的用途:一方面指定一个方法结束时返回一个值:一方面强行在return位置结束整个方法,如下所示: char test(int score) { if ...

  5. ThinkPHP2.2框架执行流程图,ThinkPHP控制器的执行流程

    ThinkPHP2.2框架执行原理.流程图在线手册 ThinkPHP控制器的执行流程 对用户的第一次URL访问 http://<serverIp>/My/index.php/Index/s ...

  6. 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

    最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, ...

  7. Servlet执行流程和生命周期【慕课网搬】

    Servlet执行流程(GET方式为例) 首先用户客户端浏览器发出Get方式(点击超链接方式)向浏览器发出请求. 服务器接收到客户端点击超链接,接收到GET请求之后,服务器到WEB.xml中<s ...

  8. javascript事件执行流程分析

    我一直想搞清楚事件在DOM中的传播方式,今天经高人指点终于明白一二.首先扒了一张图: 事件捕获过程:当我们点击TEXT时,首先是window->document->body->div ...

  9. asp.net MVC4的执行流程

    MVC在底层和传统的asp.net是一致的,在底层之上,相关流程如下: 1)Global.asax里,MvcApplication对象的Application_Start()事件中,调用 RouteC ...

  10. 03Mybatis_mybatis框架原理——执行流程

    mybatis的框架的原理(执行流程).

随机推荐

  1. 新博客 VuejsDev.com 用于梳理知识点

    新博客 VuejsDev.com 用于梳理知识点 https://www.vuejsdev.com/ 后期没有精力发布了,迁移到博客园了,防止服务器到期~ [VueJsDev] 目录列表 https: ...

  2. c语言中静态链接库的创建和使用

    静态链接库的创建 静态链接库其实就相当于压缩包,其内部可以包含多个源文件.但需要注意的是,并非任何一个源文件都可以被加工成静态链接库,其至少需要满足以下 2 个条件: 源文件中只提供可以重复使用的代码 ...

  3. matlab在mac下无写权限解决方法和思路

    问题出在哪儿?   做音频算法很多年,使用matlab的历史也是十年多了,可是在mac下使用matlab倒是第一次,基本熟悉了之后,顺杆丝滑啊.要比在windows下好用多了,不过,中间遇到了一些问题 ...

  4. Kubernetes集群部署Node Feature Discovery组件用于检测集群节点特性

    1.概述 Node Feature Discovery(NFD)是由Intel创建的项目,能够帮助Kubernetes集群更智能地管理节点资源.它通过检测每个节点的特性能力(例如CPU型号.GPU型号 ...

  5. 关于api的表优化及代码优化小结

    提示:近期有空整理下mysql设计注意点吧 文章目录 一.表设计方面 二.代码设计方面 总结 一.表设计方面 建表要求三范式 5个必须字段is_del,create_time(CURRENT_TIME ...

  6. 对TCP/IP协议的理解

    话说两台电脑要通讯就必须遵守共同的规则,就好比两个人要沟通就必须使用共同的语言一样.一个只懂英语的人,和一个只懂中文的人由于没有共同的语言(规则)就没办法沟通.两台电脑之间进行通讯所共同遵守的规则,就 ...

  7. django(路由层)

    一.简介 # 路由匹配 url(r'test',views.test), url(r'test_add',views.test_add) # r'test'与请求头的数据进行正则匹配 ''' url方 ...

  8. Sealos 云开发:Laf 出嫁了,与 Sealos 正式结合!

    千呼万唤始出来,Laf 云开发最近已正式与 Sealos 融合,入住 Sealos!大家可以登录 Sealos 公有云 体验和使用,现在正式介绍一下 Sealos 云开发. Sealos 云开发是什么 ...

  9. JDBC反序列化

    JDBC反序列化攻击 介绍 JDBC(Java DataBase Connectivity)是一种用于执行Sql语句的Java Api,即Java数据库连接,是Java语言中用来规范客户端程序如何来访 ...

  10. 记录--啊?Vue是有三种路由模式的?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 众所周知,vue路由模式常见的有 history 和 hash 模式,但其实还有一种方式-abstract模式(了解一哈~) 别急,本文我 ...