什么是Event?

An event represents a fact, something happened; and it is immutab.

事件代表着事实,代表着过去发生的某件事情,是不可变的。

既然事件代表着在过去某一时刻发生的某个事情,那么那必然具备一些基本的要素,就像现实生活中发生某件事情也具备时间、地点等几个要素。事件的基本要素包含time、source、key、header、metadata和payload。

Event vs Message

日常开发中我们接触到的和事件最接近的应该是消息,这两者也比较容易混淆,难以说清楚它们的界限:什么是事件,而什么是消息?

A message is an item of data that is sent to a specific destination. An event is a signal emitted by a component upon reaching a given state.

上面是我觉得的解释是比较好的,消息是发送到特定目标的数据项,而事件是标识某个组件达到了某个状态。消息更关注于行为,即“要做一件什么事情”——要把某一条消息发送到服务端;而事件关注于事实,即“发生了什么事情”。

消息没有特定的意图(no special intent),可以承载任何数据,那么也可以用消息来承载事件,所以消息是事件的超集(事件可以认为是一类加上了一些特定限制的消息)。

Event vs Command

还有一类日常中使用的远程调用的方式Command,通常我们执行一个RPC调用都是执行一个Command。Command和Event的区别在于Command着重于要做什么,用于传递一个要执行某个动作的请求。

因为Command具有以下特性:

  • 意味着行为即将发生,但是还没发生
  • 可能被拒绝:可能被拒绝执行,或者因为某些原因无法执行
  • 有明确的源(发起者)和目标(执行者)

结合和Message及Command的差异,总结一下Event具备的特征:

  • 代表着已经发生的事情->已经发生的事情不可改变
  • 事件有特定的一些属性,表明特定的含义,事件是消息的子集
  • 事件有明确的源,但没有明确的目标

CloudEvents

因为事件无处不在,DB中的数据发生了一次变更是一个事件,几台机器宕机了也是一个事件,并且每个发布者对事件的描述是不一致的,导致难以在一个大规模的范围内使用事件。

  • 没有统一的标准去描述事件意味着开发者需要为每一个事件源编写逻辑
  • 没有统一的标准去描述事件意味着没有通用的类库、工具、基础设置来支持事件的处理、分发
  • 没有统一的标准去描述事件意味无法进行移植,可能无法跨平台的去使用

其实在每个特定的系统中我们都会制定事件的标准来解决这个问题,比如CDC(Change Data Capture)的场景中,类似Canal(https://github.com/alibaba/canal)这样的产品都会将DB中的变更事件封装成特定的Java对象,而这个对象实际就是事件的标准。面对这个问题,CNCF(Cloud Native Computing Foundation)站在更高的角度来抽象事件,而不是局限在特定的系统或者领域,制定了CloudEvents标准。

每个符合规范的CloudEvent都需要包含必须(REQUIRED)的属性,并且可以包括多个非必须(OPTIONAL)的属性。这些属性描述了事件,并且独立于事件的数据进行序列化,这样就可以在不必进行事件数据反序列化的情况下对事件进行检查。

REQUIRED Attributes

  • id: String类型,标识Event,必须保证Source+ID能唯一确定一个Event
  • source:URI-reference类型,事件发生的“源”
  • specversion:String类型,标识事件使用的CloudEvents Spec版本
  • type:String类型,描述事件的类型

OPTIONAL Attributes

  • datacontenttype:String类型,Event内容的类型,例如"application/xml"
  • dataschema:URI类型,指明Event内容的Schema信息
  • subject:String类型,Event的Subject信息
  • time:Timestamp类型,事件发生的时间

Example

{
"specversion" : "1.0-rc1",
"type" : "com.github.pull.create",
"source" : "https://github.com/cloudevents/spec/pull",
"subject" : "",
"id" : "A234-1234-1234",
"time" : "2018-04-05T17:31:00Z",
"comexampleextension1" : "value",
"comexampleothervalue" : ,
"datacontenttype" : "text/xml",
"data" : "<much wow=\"xml\"/>"
}

什么是Event-Driven?

在讨论Event-Driven之前需要弄清楚Event-Driven的概念,这里就需要理清楚Event-Driven和Request-Driven的关系。

上面这张图来源于《Build Services on a Backbone of Events》一文,比较清楚的描述了Request-Driven和Event的区别:

  • Request-Driven包含Command和Query两种,Command意为执行命令,会导致状态的变更;而Query仅查询数据,不会导致状态变更。Request-Driven是希望执行某段业务逻辑。
  • Event-Driven则是事件驱动,事件在状态变更后触发,是业务逻辑执行完后导致的状态变更的通知。

日常我们使用的RPC服务都可以理解为是Request-Driven,都是请求执行某个命令;而日常使用的消息中间件都是Event-Driven。如果由Event来驱动执行逻辑,那么称为Event-Driven的应用。一个应用往往即会处理RPC请求,也会订阅消息,那么它有一部分是Request-Driven的,有一部分是Event-Driven的(除非是Function或者是特定领域的简单的应用——比如做消息的转换,很少会有一个纯粹的Event-Driven的应用)。没有必要过分纠结一个应用是否是纯粹的Event-Driven的,更重要的是理解Event-Driven的思想,将它融入到架构的思想中来把业务系统做的更好。

什么是Event-Driven Architecture?

Event-Driven Architecture是一种用于构建可扩展的分布式异步处理模式,由高度解耦的、单一职责的事件处理器组成。

Event-Driven Architecture模式有两种主要的结构:Mediator Topology和Broker Topology。Mediator Topology多用于需要有多个编排步骤的事件处理,而Broker Topology用于链式的事件处理。

Mediator Topology

Mediator Topology用于Event需要多个步骤进行处理,且需要一些编排能力的场景。比如一个Event进来之后,系统需要决定处理的顺序,以及其中哪些步骤可以并发的执行。Mediator Topology中有四个核心的组件:event -queue、event-mediator、event-channel、event-processor。整个流程通过客户端发送一个Event到event-queue开始,然后由event-queue将Event传输到event-mediator,event-mediator收到初始的Event之后通过异步的发送额外的Event到event-channel来完成编排,event-processor从event-channel接收Event并执行特定的业务逻辑来完成处理。

在Event-Driven Architecture中会有成百上千Queue,Event-Driven Architecture并不绑定Queue的实现,它可以由MQ实现,也可以由其他组件实现。

在Mediator模式下有两类Event,一类是initial event,一类是processing event。initial event是外部输入的初始Event,processing event是由Mediator产生的,由Event Processor处理的Event。

event-mediator的职责是编排initial event,对于initial event的每个处理步骤event-mediator发送一个processing event给event-channel,event-mediator并不执行任何真正的业务逻辑。

event-channel用于event-mediator将Event异步的投递给event-processor处理,event-channel可以使用message queue或者message topic实现。message topic是更常用的,因为event可以被投递给多个event processor处理。

event-processor包含了用于处理processing event的业务逻辑。event-processor是应用中用于执行特定任务的、自包含的、独立的、高度解耦的组件。明确event-processor是单一职责的非常重要,event-processor执行任何一个任务不应该依赖于其他event-processor的处理。

上图是用户通过保险公司投保并修改地址的例子。首先event-mediator收到初始事件,然后执行change address操作(发送Processing Event: change address event给ConsumerProcessor处理),再之后并发的执行RecalcQuote和UpdateClaims事件,接着执行AdjustClaims调整报价,最后执行Insured的通知。

Broker Topology

区别于Mediator Topology,Broker Topology没有中心式的event-mediator,Event在event-processor之间以一种链式的结构流转,在事件流相对简单,不需要集中编排的场景下这种模式是非常适用的。

在Broker Topology下只有两个核心的组件:broker和event-processor。Broker可以是中心式的,也可以是分布式的,包含事件流中所需要的所有event-channel。Broker中的event-channel可以是message queue或者message topic,也可以是它们的组合。

Broker Topology结构如上图所示,在图中没有一个event-mediator来编排event,仅有event-processor来处理event并在处理完成之后产生新的event来表示它刚刚执行的操作。

同样以Mediator中的例子来看的话,在Broker模式下的处理流程如下:

其中ChangeAddress的Processor直接处理Event,处理完毕后产生一个ChangeAddress完成的事件,然后由QuoteProcess和ClaimsProcess订阅这个事件并执行各自的处理逻辑(这一步是并发的),之后AdjustProcess会订阅UpdateClaims的Event,而NotificationProcess会同时订阅RecalcQuote和UpdateClaims的Event。

总结

Event-Driven Architecture在某些方面是具有天然优势的,在另一些方面则对现行的开发模式增加了负担,比如:

  • 敏捷度:EDA模式中event-processor都是解耦的且单一职责的,所以再进行变更时会非常容易,能够快速的变更完成业务,所以有较好的敏捷性。
  • 易于部署:EDA模式是非常容易部署的,特别是Broker的模式,因为各个组件是相互独立的。
  • 可测试性:EDA模式的测试相对于REQUEST驱动的模式会复杂很多,因为需要构建各个Event并且都是异步的。
  • 性能:EDA模式各个组件独立解耦,且异步执行,这大大提升了系统的性能。
  • 可伸缩性:EDA进行了组件的解耦,所以天然的具备的非常好的伸缩性,可以根据各个组件的性能执行不同的伸缩策略。
  • 易于开发:EDA模式下,比如Broker模式每个event-processor只需要处理自己关注的事件,并决定是否产出一个新的事件,逻辑开发是简单的,但是如何将event-processor组合起来完成整个业务是相对复杂的。

Event-Driven Architecture是相对复杂的架构,因为它天然是异步的、分布式的。当选择采用Event-Driven来处理业务逻辑时,如何划分event-processor是非常重要的,因为event-processor是独立的,需要避免将带有事务语义的逻辑拆分到多个event-processor中。统一事件标准也是非常重要的,因为event-processor会随着业务的变化而不断的增长,使用统一的标准将降低event-processor之间的交互及新event-processor的接入成本。

Event-Driven Architecture思考的更多相关文章

  1. Event Driven Architecture

    在微服务中使用领域事件   稍微回想一下计算机硬件的工作原理我们便不难发现,整个计算机的工作过程其实就是一个对事件的处理过程.当你点击鼠标.敲击键盘或者插上U盘时,计算机便以中断的形式处理各种外部事件 ...

  2. 【转】Event Driven Programming

    FROM: http://lazyfoo.net/tutorials/SDL/03_event_driven_programming/index.php Event Driven Programmin ...

  3. event driven的一些概念

    1. event :Something that happens during your application that requires a response. 2.event object:Th ...

  4. JS运行机制之 Event Loop 的思考

    先举个栗子,如下: for (var i = 0; i < 5; i++) { setTimeout(function() { console.log('i: ',i); //一秒之后输出几乎没 ...

  5. event driven model

    http://www.jdon.com/eda.html http://blog.csdn.net/gykimo/article/details/9182287 事件代表过去发生的事件,事件既是技术架 ...

  6. Domain Driven Design and Development In Practice--转载

    原文地址:http://www.infoq.com/articles/ddd-in-practice Background Domain Driven Design (DDD) is about ma ...

  7. 基于“事件”驱动的领域驱动设计(DDD)框架分析

    摘抄自 从去年10月份开始,学了几个月的领域驱动设计(Domain Driven Design,简称DDD).主要是学习领域驱动设计之父Eric Evans的名著:<Domain-driven ...

  8. [转] DDD领域驱动设计框架分享

    从去年10月份开始,学了几个月的领域驱动设计(Domain Driven Design,简称DDD).主要是学习领域驱动设计之父Eric Evans的名著:<Domain-driven desi ...

  9. Disruptor——一种可替代有界队列完成并发线程间数据交换的高性能解决方案

    本文翻译自LMAX关于Disruptor的论文,同时加上一些自己的理解和标注.Disruptor是一个高效的线程间交换数据的基础组件,它使用栅栏(barrier)+序号(Sequencing)机制协调 ...

随机推荐

  1. 一篇文章教会你使用Python定时抓取微博评论

    [Part1--理论篇] 试想一个问题,如果我们要抓取某个微博大V微博的评论数据,应该怎么实现呢?最简单的做法就是找到微博评论数据接口,然后通过改变参数来获取最新数据并保存.首先从微博api寻找抓取评 ...

  2. PHP丨PHP基础知识之条件语IF判断「理论篇」

    if语句是指编程语言(包括c语言.C#.VB.java.php.汇编语言等)中用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行给出的两种操作之一. if语句概述 if语句是指编程语言(包 ...

  3. WeChair项目Beta冲刺(9/10)

    团队项目进行情况 1.昨日进展    Beta冲刺第九天 昨日进展: 项目开始扫尾 2.今日安排 前端:前端工作已经完成 后端:扫码占座后端测试,实现对超时预约座位下座的功能 数据库:和后端组织协商扫 ...

  4. talonspilder的的提问

    有个问题:我的代码是: [1]summary=TextField(css_select="#intro>p") [2]def tal_summary(self,summary ...

  5. SpringBoot——项目启动时读取配置及初始化资源

    介绍   在开发过程中,我们有时候会遇到非接口调用而出发程序执行任务的一些场景,比如我们使用quartz定时框架通过配置文件来启动定时任务时,或者一些初始化资源场景等触发的任务执行场景. 方法一:注解 ...

  6. 千金良方说:"我现在奉上179341字的MySQL资料包,还来得及吗?有"代码段、附录、和高清图!!"

    上一篇"上发布过"一不小心,我就上传了 279674 字的 MySQL 学习资料到 github 上了",我在更早之前,在微信公众号"老叶茶馆"上发布 ...

  7. javaScript的三种储存方式

    (一).SessionStorage     会话储存 (二).localStorage           本地储存 (三).Cookier                   现实中为:饼干    ...

  8. eclipse 导入下载或拷贝的java Web项目时报错 ,或者是报错Unbound classpath container: 'JRE System Library

    在Problems里报错Description Resource Path Location Type Unbound classpath container: 'JRE System Library ...

  9. .Net Core api 中获取应用程序物理路径wwwroot

    如果要得到传统的ASP.Net应用程序中的相对路径或虚拟路径对应的服务器物理路径,只需要使用使用Server.MapPath()方法来取得Asp.Net根目录的物理路径,如下所示: // Classi ...

  10. JavaScript基础函数的配置对象Configuration Objects(020)

    配置对象通常用在API库的实现中,当程序中需要编写要多次的模块,也可以采用这种模式.这种模式的好处是接口明确,扩展方便.比如,一个 addPerson在设计的最初需要两个参数作为初始化时人的姓名: f ...