共计 8935 个字符,预计需要花费 23 分钟才能阅读完成。
作者:罗静
在刚刚过来的 RocketMQ Summit 2022 寰球开发者峰会上,咱们对外正式开源了咱们的新产品 RocketMQ-Eventbridge 事件驱动引擎。
RocketMQ 给人最大的印象始终是一个音讯引擎。那什么是事件驱动引擎?为什么咱们这次要推出事件驱动引擎这个产品?他有哪些利用场景,以及对应的技术计划是什么?
明天咱们就一起来看下,整篇文章蕴含三局部:
第一局部,咱们一起看下什么是事件。
第二局部,和大家一起看看,事件有哪些不一样的“超能力”,应用这些“超能力”呢,咱们又无能些什么?
第三局部,咱们讲一下 RocketMQ 给出的对于事件的解决方案,也是咱们这次开源的我的项目:RocketMQ-EventBridge。
什么是事件
大家本人能够先在脑袋里想一下,什么是事件?咱们给事件下的一个定义是:
过来曾经产生的事,尤其是比拟重要的事。
A thing that happens, especially one of importance.
这个很好了解。比如说,昨天下午我做了一次核酸检测;今天上午又吃了一个冰激淋。这些都是过来曾经产生的事件。然而,如果我再问:事件跟音讯有什么区别?这个时候,大家是不是感觉事件这个定义,如同又不那么清晰?
方才说的那些事件,是不是也能够了解为音讯啊?如果,老张给我发送了一条短信,这个算是事件,还是音讯啊?平时开发过程中,“什么时候应用音讯,什么时候应用事件?”
不过,在答复这个问题之前,咱们一起来看一个典型的微服务。
一个微服务零碎和内部零碎的交互,能够简略分为两局部:一是接管内部申请(就是图中下面黄色的局部);二是是调用内部服务(就是图中上面绿色的局部)。
接管内部申请,咱们有两种形式:一种是提供 API,接管内部发过来的 Query 申请和 Commond 申请;另外一种是被动订阅内部 Command 音讯。这两类操作,进入零碎外部之后呢,咱们经常还会,调用其余为微服务零碎,一起协同解决,来实现一个具体的操作。当这些操作,使得零碎状态产生扭转时,就会产生事件。
这里呢,咱们把从内部接管到的 Command 音讯,和零碎外部产生的事件,都称之为音讯。
咱们总结一下,音讯和事件的关系是这样的:音讯蕴含两局部,Command 音讯和 Event 音讯
1、看图中左半局部,Command 是内部零碎发送给本零碎的一条操作命令;
2、再看图中右半局部,Event 则是本零碎收到 Command 操作申请,零碎外部产生扭转之后,随之而产生了事件;
所以,事件和音讯是不同的,事件能够了解为是一种非凡的音讯。其非凡的点,次要在 4 个中央:
已产生、且不可变
事件,肯定是“已发的”。“已产生”的代表什么呢?不可变的。咱们不可能扭转过来。这个个性十分重要,在咱们处理事件、剖析事件的时候,这就意味着,咱们相对能够置信这些事件,只有是收到的事件,肯定是零碎实在产生过的行为。而且是不可批改。
比照 Command 和 Query。Command 的中文是什么?命令。很显然,它是还没有产生的,只是表白了一种冀望。咱们晓得“冀望的”,不肯定会胜利产生。
比方:把厨房的灯关上、去按下门铃、转给 A 账户 10w……
这些都是 Commond,都是冀望产生的行为。然而,最终有没有产生?并不知道。
Event 则是明确曾经产生的事件。比方:厨房灯被关上了、有人按了门铃、A 账户收到了 10w……
再比照 Query,它则是查问零碎以后状态的一种申请,比方:厨房的灯是关上着的、门铃正在响、查下账户显示余额 11w……
无冀望的
这个怎么了解?事件是主观的形容一个事物的状态或属性值的变动,但对于如何处理事件自身并没有做任何冀望。
相比之下,Commond 和 Query 则都是有冀望的,他们心愿零碎做出扭转或则返回后果,然而 Event 呢,它只是主观形容零碎的一个变动。
咱们看一个例子:交通信号灯,从绿灯变成红灯,事件自身并没有要求行人或汽车禁止通行,而是交通法规须要红绿灯,并赋予了其规定。
所以,零碎个别不会定向的、独自向另外一个零碎发送事件,而是对立的通知“事件核心”,“事件核心”呢,那外面有各个系统上报上来的,各式各样的事件。零碎会向事件核心阐明:本人这个零碎,会产生哪些事件呀,这些事件的格局是怎么样的呀。
别的零碎如果感兴趣呢,就能够来被动订阅这些事件。真正赋予事件价值的,是事件消费者。事件消费者想看看, 某个零碎产生了什么变动呀?OK,那他就去订阅这些事件,所以事件是消费者驱动的。
这跟音讯有什么区别呢?Commond 音讯的发送和订阅,是单方约定好的,外人不晓得,往往是以文档或代码的模式,大家按约定好的协定,发送和订阅生产,所以音讯是生产者驱动的。
咱们打个比喻,事件就像市场经济,商品被生产进去,具体有什么价值,有多大价值,很大水平上看其消费者。咱们能看到零碎中各种各样的事件,就像橱窗里摆放了各种各样的商品。而 Commond 音讯呢,有点像计划经济,一出世就带着很强的目的性,我就是要“调配”给谁生产。
人造有序
事件的第三个个性是:“人造有序”。含意:同一个实体,不能同时产生 A 又产生 B,必有先后关系;如果是,则这两个事件必属于不同的事件类型。
比方:针对同一个交通信号灯,不能既变成绿灯,又变成红灯,同一时刻,只能变成一种状态。
大家可能发现了一点,这里其实暗藏了事件的一个额定属性:因为人造有序,跟时间轴上的某一时刻强绑定,且不能同时产生,所以它肯定是惟一的。
如果咱们看到了两个内容一样的事件,那么肯定是产生了两次,而且一次在前,一次在后。(这对于咱们解决数据最终一致性、以及零碎行为剖析都很有价值:咱们看到的,不光光是零碎的一个最终后果,而是看到变成这个后果之前的,一系列两头过程)
具像化
事件的第四个个性是:“具象化”的。
事件会尽可能的把“案发现场”残缺的记录下来,因为它也不晓得消费者会如何应用它,所以它会做到尽量的详尽,比方:
●是由谁产生的事件?Subject
●是什么类型的事件?Type
●是谁发送的事件?Source
●事件的唯一性标记是什么?Id
●什么时候产生?Time
●事件的内容是什么?Data
●事件的内容有哪些信息?Dataschema
咱们还是以交通信号灯举例子:
比照咱们常见的音讯,因为上下游个别是确定的,经常为了性能和传输效率,则会做到尽可能的精简,只有满足“计划经济”指定安顿的消费者需要即可。
总结一下,事件下面的 4 个个性,是对事件微小的一个属性加成,让事件领有了跟一般音讯不一样的“超能力”。使事件,经常被用到 4 个典型场景:事件告诉、事件溯源、零碎间集成和 CQRS。
上面让咱们一个个开展,具体看看这些利用场景。
事件的典型利用场景
事件告诉
事件告诉是咱们零碎中很常见的一个场景。比方:用户下单事件告诉给领取零碎;用户付款事件告诉给交易系统。
这里,让咱们回到一开始信号灯那个例子。当交通信号灯,从红灯变成绿灯时,可能存在很多零碎都须要这个信息。
形式 1: 发送方被动调用,适配接管方
一种最简略的形式是,咱们顺次 call 每个零碎,并把信息传递进来。比方:信号灯零碎,被动调用地图导航的 API 服务、调用交警中控的 API 服务,调用城市大脑的 API 服务,把红绿灯变动信号发送进来。
但咱们都晓得,这个设计十分蹩脚。尤其当零碎越来越多时,这无疑是劫难的,不仅开发成本高,而且其中一个零碎呈现问题,可能会 hang 住整个服务,则导致调用其余零碎都会受到影响。
形式 2:接管方被动订阅,适配发送方
一个很天然的解决方案是,咱们将这些信息发送到两头音讯服务 Broker,其余零碎如果有须要,则被动去订阅这些音讯即可。
这个时候,信号灯零碎与其余零碎并没有间接的调用依赖,交警中控服务、地图导航服务、城市大脑服务,只有依照约定的协定,去订阅信号灯的音讯,并解析这些信息即可。
然而,这里同样存在一个问题:这个架构中,是以“信号灯”为核心。消费者须要了解发送者的业务畛域,并被动增加适配层,(就是图中红色盘旋镖局部),将音讯转化为本人业务畛域内的语言。但对于每一个微服务来说,他都心愿都是高内聚低耦合的。
如果交警中控须要全国的信号灯数据,然而每个地区的音讯格局又不一样,这就意味着,交警中控须要适配每一个地区的协定,做一层转换。而且万一前面变动了怎么办?想想就晓得这个运维老本有多可怕。
那是否交警中控系统,能够要求全国所有红绿灯零碎,都按同一种数据协定给到本人呢?不好意思,这些信号灯数据地图服务也在用,城市大脑也在用,不能更改。
形式 3:引入事件,Borker 依据接管方协定,进行灵便适配
但如果应用事件,就不一样了。因为事件是“无冀望的”,“具像化的”,人造的保留了案发现场尽可能多的信息,且更加标准规范,对于消费者(也就是交警中空)来说,能够轻易将不同省份,收集上来的事件,轻易组装成,合乎本人业务要求的格局。
而且,这一组装,是在中间层 Broker 产生的。对于交警中控来说,它只须要,依照本人业务畛域的设计,提供一个接管事件的 API,而后其余事件,通过 Broker,被动投递到这个 API 上即可。从头到尾,对交警中控系统,没有一行适配内部业务的代码。
所以,这种形式有 3 个显著的劣势:
1、只关注本人业务畛域自身,不须要做适配内部的代码;
2、所有对系统的变更,收敛到 API,为惟一入口;同一个 API,可能既是用来接管事件的,也可能同时用于控制台操作;
3、因为事件是推送过去的,所以,也不须要像之前一样,引入一个 SDK,和 Broker 产生连贯,获取音讯,升高了零碎的复杂度。
这样,咱们一开始的图,就会变成这个样子:交通信号灯产生事件,投递到事件核心,其余须要这些事件的消费者,在事件核心订阅,再由事件核心,依照他们冀望的事件格局,被动投递过来。
让咱们再来回顾下整个过程:
第 1 幅图:一开始,咱们通过强依赖的形式,让信号灯零碎,被动将信息发送给各个系统。那这张图里,咱们是以各个上游服务为核心,信号灯零碎去适配各个上游服务。
第 2 幅图:起初,咱们采纳传统音讯的形式,对调用链路进行理解耦,两边零碎不再间接依赖了,然而依旧会存在业务上的依赖。消费者须要去了解生产者的音讯格局,并在本人零碎外部,进行转换适配。所以,这里其实是以生产者为核心。
第 3 幅图:最初,咱们引入了事件告诉的形式,对于这种形式,生产者和消费者,他们都只须要关注本人零碎自身就能够了。生产者,生产什么样的事件,消费者,生产什么样的数据格式,都各自以本人的业务为核心,不须要为对方做适配。真正做到咱们说的高内聚低耦合,实现彻底的齐全解耦。
当初,回到咱们一开始提到的典型微服务模型,对于有些场景,咱们就能够变为上面这种形式:对微服务的变更操作,对立收敛到 API 操作入口,去掉 Commond 音讯入口。收敛入口,对于咱们保护微服务,保障系统稳定性,经常十分有益处的。
事件溯源
事件溯源是什么?事件溯源简略了解就是让零碎回到过来任意时刻。那怎么样,能力让零碎能够回到过来呢?很简略,首先零碎所有产生的变动,都得以事件的形式记录下来;而后,咱们就能够通过回放事件的形式,回到过来任何一个时刻。
那为什么只有事件能力做这个事,其余一般音讯不行呢?这个还是要回到咱们方才说的几个事件个性:已产生不可变的、人造有序且惟一的、而且是十分具体具体的,残缺的记录了事件的案发现场。所以,对于事件溯源这个场景,事件能够说是零碎的一等一的公民。
举个例子:比如说,如果咱们可能残缺地收集路上的各种事件信息,包含信号灯、车量、天气、拥挤路况等等,那么,咱们就能够“穿梭工夫”,回到交通现场,从新做一次决策。比方,在智慧交通场景,当咱们想去验证一个调度算法的时候,咱们就能够回放过后产生的所有事件,来重现现场。
大家可能感觉这个很神奇,然而,其实咱们平时始终有接触,大家晓得是什么吗?就是咱们罕用的代码版本 - 管理系统,比方:github。
这里有大家可能会问,如果一个零碎积赞了很多事件,想重放是不是得很久?比方在一些交易场景,每天都会产生大量的事件,那应该怎么解决呢?这里呢,零碎个别每天晚上都会打一份快照。如果零碎意外宕机,想回到某一个时刻,就能够把前一天的快照取出,而后再从新跑下当天的事件,即可复原。而白天呢,所有的事件都是在内存中进行解决,不会跟数据库交互,所以零碎性能十分快,只有事件会落盘。
当然,事件溯源也不是适宜所有场景,它有长处也有毛病,具体看上图。
零碎间集成
方才讲的第 1 个场景:事件告诉,个别波及到两个上下游团队的合作开发;讲的第 2 个场景:事件溯源,则个别是 1 个团队内的开发;但零碎间集成,则往往面对的是三个业务团队的合作开发。这个怎么了解呢?
其实这个也很常见:比方公司里购买了 ERP 零碎,同时也购买了内部考勤零碎、内部营销零碎服务等等。这些零碎都有一个共同点,是什么?都不是咱们本人开发的,是而买来的。
如果咱们想把 ERP 零碎的人员信息,实时且主动同步到考勤零碎中去怎么办?其实这个是有点麻烦的,因为这些都不是咱们本人开发的。
1、咱们不能批改 ERP 零碎的代码,被动去调用考勤零碎,把人员变更信息发送过来;
2、也不能批改考情零碎的代码,被动去调用内部 ERP 零碎的 API;
然而咱们能够通过事件总线,借助 webhook 或则规范 API 等等形式,收集上游的 ERP 零碎产生的人员变更事件,而后进行过滤和转换,推送到上游考勤零碎中去,当然,这里也能够是外部自研服务。
所以,当初的研发模式变成了:事件核心治理了所有 SaaS 服务,包含外部自研零碎产生的所有事件。而后呢,咱们只须要在事件核心,寻找咱们须要的事件,进行订阅,对 SaaS 服务和外部自研零碎,进行简略服务编排,即可实现开发。
CQRS
CQRS 中的 C 代表 Command,Command 什么意思?就是明令,个别蕴含:Create/Update/Delete,Q 代表 Query,是指查问。所以 CQRS 实质是读写拆散:所有的写操作,在图中右边的零碎中实现,而后将零碎因为 Command 产生变动的事件, 同步到左边的查问零碎。
这里同学可能有疑难,这跟数据库的读写拆散有什么区别?数据库读写拆散也是提供一个写的 DB,一个读的 DB,两边做同步。对吧…
那这里很大的一个区别是:对于数据库的读写拆散,是以数据库为核心,两边的数据库是截然不同的,甚至数据的存储构造也是截然不同的。
然而对于 CQRS 的读写拆散场景,是以业务为核心,两边存储的数据结构格局,往往是不一样的,甚至数据库都不是同一种。齐全围绕各自的读写业务逻辑,设计最佳技术选型。对于写场景,为了保障事务,咱们可能应用关系性数据库;对于读的场景,咱们为了进步性能,咱们可能会应用 Redis、HBase 等 Nosql 数据库。
当然 CQRS 也不是适宜所有场景,他往往比拟适宜:
●心愿同时满足高并发的写、高并发的读;
●写模型和读模型差异比拟大时;
●读 / 写比十分高时;
咱们方才讲了事件的 4 个利用场景,然而,事件不是万能的,就像软件研发也没有银弹,有很多场景也并不适宜 - 应用事件。包含:
- 强依赖 Response 的同步调用场景;
- 要求服务调用放弃事务强一致性的场景。
RocketMQ 对于事件的解决方案
须要什么样的能力?
首先,依照之前讲到的事件利用场景,咱们整顿下,如果咱们做好事件驱动这块,咱们的零碎,须要具备什么样的能力呢?
第一,咱们必定得有一个事件规范,对吧…因为,事件不是给本人看的,也不是给他看的,而是给所有人看的。方才,咱们也讲到事件是无冀望的,它没有明确的消费者,所有都是潜在的消费者,所以,咱们得规范化事件的定义,让所有人都能看得懂,高深莫测。
第二,咱们得有一个事件核心,事件核心外面有所有零碎,注册上来的各种事件,(这个跟音讯不一样,咱们没有音讯核心,因为音讯个别是定向的,是生产者和消费者约定的,有点像计划经济,音讯生产进去的时候,带着很强的目的性,是给谁谁生产的。而事件有点像市场经济,事件核心呢,)这个有点相似市场经济大卖场,玲琅满目,外面分类摆放了各种各样的事件,所有人即便不买,也都能够进来瞧一瞧,看一看,有哪些事件,可能是我须要的,那就能够买回去。
第三,咱们得有一个事件格局,用来形容事件的具体内容。这相当于市场经济的一个交易契约。生产者发送的事件格局是什么,得确定下来,不能总是变;消费者以什么格局接管事件也得确定下来,不然整个市场就乱套了。
第四,咱们得给消费者一个,把投递事件到指标端的能力。并且投递前,能够对事件进行过滤和转换,让它能够适配指标端 API 接管参数的格局,咱们把这个过程呢,对立叫做订阅规定。
第五,咱们还得有一个存储事件的中央,就是最两头的事件总线。
事件规范
对于方才提到的第一点事件规范,咱们选取了 CNCF 旗下的开源我的项目 CloudEvents,目前已被宽泛集成,算是一个事实上的规范。
它的协定也很简略,次要标准了 4 个必选字段:id,source、type、specversion;以及多个可选字段:subject、time、dataschema、datacontenttype 和 data。上图左边,咱们有一个简略的例子, 大家能够看下,这里就不具体开展了。
另外,事件的传输也须要定义一种协定,不便不同零碎之间的沟通,默认反对三种 HTTP 的传输方式:Binary Content Mode、Structured Content Mode 和 Batched Content Mode。通过 HTTP 的 Content-Type,就能够辨别这三种不同的模式。其中前两种,都是传递单个事件;第三种则是传递批量事件。
事件 Schema
事件的 Schema,用来形容事件中有哪些属性、对应的含意、束缚等等信息。目前咱们选取了 Json Schema. 和 OpenAPI 3.0,依据事件的 Schema 形容,咱们能够对事件进行合法性校验。,当然 Schema 自身的批改,也须要合乎兼容性准则,这里不作具体开展。
事件过滤和转换
对于事件的过滤和转换,咱们提供了 7 种事件过滤形式和 4 种事件转换形式,具体能够下图形容:
技术架构
咱们 RocketMQ 围绕事件驱动推出的产品,叫做 EventBridge,也是咱们这次要开源的新产品。
他的整个架构能够分为两局部:下面是咱们的管制面、上面是咱们的数据面。
管制面中最下面的 EventSource 是各个系统注册上来的事件源,这些事件能够通过 APIGateway 发送事件到事件总线,也能够通过配置的 EventSource,生成 SouceRuner,被动从咱们的零碎中,去拉取事件。事件达到事件总线 EventBus 之后,咱们就能够配置订阅规定了 EventRule,在规定 EventRule 里咱们设置了事件怎么过滤,以及投递到指标端前,做哪些转换。零碎基于创立的规定会生成 TargetRunner,就能够将事件推送到指定的指标端。
那这里 SouceRuner 和 TargetRunner 是什么呢?咱们具体能对接哪些上下游 Source 和 Target?
这些咱们都能够在上面的 SourceRegister 和 TargetRegister 提前进行注册。
所以 EventBridge 的数据面是一个凋谢的架构,他定义了事件处理的 SPI,底下能够有多种实现。比方,咱们把 RocketMQ 的 HTTPConnector 注册到 EventBridge 中,那咱们就能够把事件推送到 HTTP 服务端。
如果咱们把 Kafka 的 JDBC Connector 注册到 EventBridge 中,咱们就能够把事件推送到数据库。
当然,如果你的零碎不是通用的像 HTTP/JDPC 等协定,也能够开发本人的 Connector,这样就能将事件实时同步到 EventBridge,或则接管来自 EventBridge 的事件。
除此之外,咱们还会有一些附加的运维能力,包含:事件追踪、事件回放、事件剖析、事件归档。
RocketMQ-EventBridge 与云上
在所有开源的,与其余上下游零碎做集成的 Connector 当中,咱们有一个非凡的 Connector,叫:EventBridgeConnector,通过它能够不便的和阿里云云上的事件总线进行集成。这里有两个典型的利用场景:
第一个场景是:IDC 零碎外部产生的事件,不仅能够用来做外部零碎间的解耦,还能够实时同步到云上,驱动云上的一些计算服务,比方通过云上 Maxcompute 对外部产生的事件进行离线剖析,或则驱动云上的图像识别服务,实时剖析事件中标注的图片。
第二个场景是:如果 IDC 外部应用到了自建 MQ,咱们同样能够通过 MQConnector 和 EventBridgeConnector,实时同步事件到云上,逐渐将外部自建 MQ,迁徙到云上 MQ。
生态倒退
对于 EventBridge 的将来方向,咱们心愿是在开源,构建一个反对多云架构的事件总线生态。这个怎么了解?简略来说,咱们心愿在不同云厂商之间,包含云厂商和外部 IDC 零碎之间,能够通过事件,来突破围墙,实现互通。尽管,这几年云计算倒退很快,然而对于一些特地大的客户来讲,有时候并不心愿跟某家云厂商强绑定。这不光是市场充沛竞争的后果,也是大客户一种升高危险的伎俩。所以,这个时候,如何在不同云厂商之间,包含云厂商零碎和本人外部 IDC 零碎之间,灵便的交互,甚至灵便的迁徙,是企业十分重要的一个诉求。
当然,实现这个是有肯定难度的。不过如果咱们在进行企业架构设计的时候,是基于事件驱动架构进行设计开发——不同零碎之间的交互,围绕事件开展,就会容易很多。
事件,在这里,就好比一种通用语言,通过这个通用语言,就能够实现和不同零碎之间的沟通交流。比方:用 IDC 零碎外部的事件,去驱动阿里云上服务;甚至用阿里云上的事件,去驱动 AWS 上的服务运行;
为了实现这个指标,咱们在和不同云厂商,不同 SaaS 零碎服务商,进行零碎间集成的时候,须要开发与之对应的连接器。
也欢送大家,一起来共建 RocketMQ-EventBridge 的生态。
源码地址:
https://github.com/apache/roc…
感兴趣的小伙伴们能够扫描下方二维码退出钉钉群探讨(群号:44552972)
点击此处,进入 EventBridge 官网理解更多信息~