An event represents a fact, something happened; and it is immutab.
事件代表着事实,代表着过去发生的某件事情,是不可变的。
既然事件代表着在过去某一时刻发生的某个事情,那么那必然具备一些基本的要素,就像现实生活中发生某件事情也具备时间、地点等几个要素。事件的基本要素包含time、source、key、header、metadata和payload。
日常开发中我们接触到的和事件最接近的应该是消息,这两者也比较容易混淆,难以说清楚它们的界限:什么是事件,而什么是消息?
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),可以承载任何数据,那么也可以用消息来承载事件,所以消息是事件的超集(事件可以认为是一类加上了一些特定限制的消息)。
还有一类日常中使用的远程调用的方式Command,通常我们执行一个RPC调用都是执行一个Command。Command和Event的区别在于Command着重于要做什么,用于传递一个要执行某个动作的请求。
因为Command具有以下特性:
结合和Message及Command的差异,总结一下Event具备的特征:
因为事件无处不在,DB中的数据发生了一次变更是一个事件,几台机器宕机了也是一个事件,并且每个发布者对事件的描述是不一致的,导致难以在一个大规模的范围内使用事件。
其实在每个特定的系统中我们都会制定事件的标准来解决这个问题,比如CDC(Change Data Capture)的场景中,类似Canal( https://github.com/alibaba/canal )这样的产品都会将DB中的变更事件封装成特定的Java对象,而这个对象实际就是事件的标准。面对这个问题,CNCF(Cloud Native Computing Foundation)站在更高的角度来抽象事件,而不是局限在特定的系统或者领域,制定了CloudEvents标准。
每个符合规范的CloudEvent都需要包含 必须(REQUIRED) 的属性,并且可以包括多个 非必须(OPTIONAL) 的属性。这些属性描述了事件,并且独立于事件的数据进行序列化,这样就可以在不必进行事件数据反序列化的情况下对事件进行检查。
{ "specversion" : "1.0-rc1", "type" : "com.github.pull.create", "source" : "https://github.com/cloudevents/spec/pull", "subject" : "123", "id" : "A234-1234-1234", "time" : "2018-04-05T17:31:00Z", "comexampleextension1" : "value", "comexampleothervalue" : 5, "datacontenttype" : "text/xml", "data" : "<much wow=/"xml/"/>" }
在讨论Event-Driven之前需要弄清楚Event-Driven的概念,这里就需要理清楚Event-Driven和Request-Driven的关系。
上面这张图来源于《Build Services on a Backbone of Events》一文,比较清楚的描述了Request-Driven和Event的区别:
日常我们使用的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模式有两种主要的结构:Mediator Topology和Broker Topology。Mediator Topology多用于需要有多个编排步骤的事件处理,而Broker 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的通知。
区别于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在某些方面是具有天然优势的,在另一些方面则对现行的开发模式增加了负担,比如:
Event-Driven Architecture是相对复杂的架构,因为它天然是异步的、分布式的。当选择采用Event-Driven来处理业务逻辑时,如何划分event-processor是非常重要的,因为event-processor是独立的,需要避免将带有事务语义的逻辑拆分到多个event-processor中。统一事件标准也是非常重要的,因为event-processor会随着业务的变化而不断的增长,使用统一的标准将降低event-processor之间的交互及新event-processor的接入成本。