事件驱动架构(EDA/SEDA/DEDA/ESB/CQRS/EventSourcing)

什么是事件驱动架构

事件代表过去发生的事件,事件既是技术架构概念,也是业务概念。以事件为驱动的编程模型称为事件驱动架构EDA。

 

EDA是一种以事件为媒介,实现组件或服务之间最大松耦合的方式。传统面向接口编程是以接口为媒介,实现调用接口者和接口实现者之间的解耦,但是这种解耦程度不是很高,如果接口发生变化,双方代码都需要变动,而事件驱动则是调用者和被调用者互相不知道对方,两者只和中间消息队列耦合。

 

事件驱动有以下特征:

  1. 生产者producer发生实时事件
  2. 推送通知
  3. 生产者发射即完成fire-and -orget
  4. 消费者consumer立即响应
  5. 事件与命令是有区别的

借助消息系统异步模型的特点,事件驱动也有异步特征,传统方法调用比如调用b.xxmethod()是一种同步模型,这时必须等待b的方法执行完才能继续执行其他代码,RPC远程方法调用也是一种同步模型,而对于异步模型来说,事件生产者发出事件后,不必等待回应,可以继续执行下面的代码。

 

事件跟消息有什么区别

事件

(1)事件是代表发生了一些事情(发生在某事之后);

(2)事件被广播到正在监听的任何代码(代码可以对事件做出反应)。

消息

一种信息,传递给系统的信息

区别

“事件是一个动作——用户触发的动作。 消息是一个信息——传递给系统的信息。 事件与消息的概念在计算机中较易混淆,但本质不同: 事件由用户(操作电脑的人)触发且只能由用户触发,操作系统能够感觉到由用户触发的事件,并将此事件转换为一个(特定的)消息发送到程序的消息队列中

 

 

架构演进过程

SOA-》ESB-》SEDA-》DEDA

SOA: service original architecture,面向服务架构

ESB:Enterprise Service Bus,企业级服务总线

SEDA:step eda,分阶段 事件驱动

DEDA:Domain Eda,领域事件驱动

 

 

SOA架构下,通过ESB的实现的EDA:

 

 

不是所有使用消息系统的架构都是EDA,SOA面向服务驱动的架构中也使用消息系统作为ESB,两者使用方式不同,三种不同交互方式:

  1. 时间驱动:比如cron定时计划执行
  2. 请求驱动:客户端和服务器端之间,常见SOA
  3. .事件驱动:以事件为特征。实时。

请求驱动+消息系统和事件驱动+消息系统有本质区别,前者是由请求者作为消息生产者,主要目的是为了得到响应,因此是一种请求响应模型;而后者重点是在消息消费者,不是在消息生产者,业务逻辑站在消费者角度完成,业务逻辑的完成靠事件驱动来执行,而前者业务逻辑是在消息生产者完成,当业务逻辑中需要什么依赖或资源,依靠发送消息来拉取完成。这两种区别本质是拉Poll和推Push的区别。

 

DEDA

正是因为EDA这种和传统SOA的本质区别,现在诞生一种领域EDA,其中包括CQRS EventSourcing 领域事件等等。同时,传统的SOA将业务领域逻辑切分成不同系统,对外表现为服务,这种方式导致业务逻辑跨越多个系统,导致业务逻辑散落各处,寻找维护不方便,造成业务逻辑的污染和膨胀。

使用EDA改造传统SOA,比如,如果一个报表系统想知道交易系统的状态,它不是发送一个消息给交易系统,拉取它当前的状态,而是向事件总线订阅,这样当交易系统有状态报告时,将发出事件通知报表系统。

 

 

未来演进方向(SEDA、ES、函数编程、)

SEDA:Step Eda,分阶段 事件驱动

SEDA将应用分通过事件队列链接的网状Stage,每个Stage由一个线程池,一个业务相关的事件处理器(Event Handler),一个事件输入队列和一个多个资源控制器组成。如图6所示。

事件驱动架构(EDA/SEDA/DEDA/ESB/CQRS/EventSourcing)_第1张图片

 

 Staged event-driven (SEDA) HTTP server: This is a structural representation of Haboob, the SEDA-based Web server, described in detail in Chapter 6. The application is composed as a set of stages separated by queues. Edges represent the flow of events between stages. Each stage can be independently managed, and stages can be run in sequence or in parallel, or a combination of the two. The use of event queues allows each stage to be individually load-conditioned, for example, by performing admission control on its event queue. For simplicity, some event paths and stages have been elided from this figure.

SEDA的几点重要设计:

 

       Efficient, event-driven concurrency: SEDA并发模型依赖于Event-driven concurrency 模型来支持高并发。利用一组线程来处理每个请求,而不是每一个请求一个线程,利用 Nonblocking I/O来避免资源的阻塞。

 

       Dynamic thread pooling: 为了避免设计事件调度和达到非阻塞操作的要求,SEDA利用一系列线程组(Thread pool), 每一个事件处理器利用一个动态的线程池。这样就可以利用OS的线程调度来调度事件的处理,并且可以允许事件处理代码阻塞一小段时间,因为有多个线程(一个动态线程池)可以处理一个事件。

 

       Structured queues for code modularity and load management: 通过事件队列将一个应用分割成一系列的Stage,可以让应用开发者只关注具体的业务逻辑(事件处理器),然后通过队列将各个State组装成应用,也可以动态的添加何卸载Stage。这样可以独立开发每一个Stage。

       应用也可以在每个Stage中控制每一个请求的执行,如执行路径的改变,或终止请求等。每一个Stage可以更具自身的状态来控制请求的执行,如某一个Stage的负载太高,可能会拒绝处理这个请求。当然也可以让资源管理与控制在更细的粒度,没一个Stage都可以有自己的资源管理宇控制。

 

Stage

Stage是SEDA中的核心基本单元,每一个Stage是一个自包含的应用组件,包含一个事件处理器(Event Handler),一个输入事件队列(incoming event queue),一个线程池(thread pool)和一个或多个资源控制器(Controller)。资源控制器负责资源消耗的管理,调度,线程分配和回收等。Stage的线程从事件输入队列批量取出事件,并调用事件处理器代码,事件处理器处理每一个事件,并输出0个或多个事件到其他的Stage的事件输入队列。如图7所示。

 

事件驱动架构(EDA/SEDA/DEDA/ESB/CQRS/EventSourcing)_第2张图片

 

ES:Event Sourcing

Event sourcing事件溯源是借鉴数据库事件日志的一种数据持久方式,在事件日志中记录导致状态变化的一系列领域事件。通过持久化记录改变状态的事件,通过重新播放获得状态改变的历史。 事件回放可以返回系统到任何状态。

在ES中,事务单元变得更细粒度,使用一系列有序的事件来代表存储在数据库中的领域模型状态,一旦一个事件被加入事件日志,它就不能被移走或重新排序,事件被认为是不可变的,事件序列只能被追加方式存储。

由于事件流本身具有逻辑上严格次序性,因此使用统一的事件流(事件日志)能够很自然实现事务机制,无需额外ACID机制或2PC之类同步强硬方式。

 

 

 

事件驱动架构(EDA/SEDA/DEDA/ESB/CQRS/EventSourcing)_第3张图片

 

该图表达了用户操作者和被操作者事物之间的本质关系,以用户和购物车为案例,从购物车这个事物角度看:领域聚合实体表达的是购物车这个事物的分析设计方法,以一种静态结构性来表达事物;从用户购买者这个角度看:用户将选购的商品放入购物车,删除购物车已有商品,这些都是用户的操作行为,每一个操作行为相当于发出一个个命令command,在一定场景中转化为事件,事件会改变购物车状态,这是一种以动态行为(面向函数)来表达与人有关的需求。

 

区块链

通过引入事件概念,可以实现多线程并发到分布式去中心化计算的平滑过渡,区块链本质上就是一种事件链。 通常区块链是指一种分布式账簿,账本其实是记账明细,记录着每笔进出明细,这些发生的每笔明细其实代表系统发生的事件,因此,记账明细其实就是事件日志,区块链其实就是一种分布式事件链,只不过另外增加了一层安全层。

事件溯源的适用场景是和区块链的应用场景类似的,因为两者本质上是一致的,可以说区块链就是一种事件溯源,传统的集中式的事件溯源是将事件日志集中保存到事件库或Apache Kafka之类流处理框架,而区块链则是将事件日志分别保存在各个聚合所在的点,如何向这个大而全的事件日志写入新事件?传统分布式系统通过PaxosRaft选举主节点单独向事件日志写入事件,而区块链通过工作证明算法选举,CRDT通过事件数据本身携带的向量时钟合并,可谓各有巧妙。

 

函数式编程

在微服务等无状态应用架构中,我们不是需要状态时就发出命令从数据库中查询获得,这样,可变的状态会遍布整个应用代码中,带来很多副作用,而我们将这些状态操作统一为事件流声明式订阅,订阅了某个事件流,通过重播事件流中各个事件一直到最新最后的事件,也就获得了最终的状态。函数式编程Stream风格为这种播放提供了方便,具体Reactive框架有RxJS、React.js、RxJava、Reactor等等。

工作流

 

 

 

 

挑战

最大的挑战就是在实现跨服务的业务逻辑时,如何保持服务之间的数据一致性

 

ES:

  • 开发人员管理状态和构建聚合不是很自然的方式,需要时间来习惯
  • 查询超出一个聚合更难(您必须为要添加到系统的每种类型的查询构建投影),
  • 事件模式更改比关系模型(缺少标准模式迁移工具)困难得多
  • 你必须从一开始就考虑版本控制处理。

 

 

参考:

SEDA并发模型https://blog.csdn.net/hui12345685/article/details/42555615

https://www.jdon.com/event.html

https://www.tuicool.com/articles/uiymAjI

https://www.jdon.com/52278

 

 

 

你可能感兴趣的:(事件驱动)