比较官方的定义:事件驱动是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。在计算机编程、公共关系、经济活动等领域均有应用。 事件驱动是很早作为术语出现在了 GUI 编程中,比如,用户在界面点击了按钮,就会发送一个 “点击” 事件,而相应的会有一个处理 “点击” 事件的事件处理器会来处理该事件。但实际 GUI 编程对他的定义与我们企业级的框架设计并不处于同一抽象级别。
系统间的通讯方式一般可分为同步通信和异步通信两种,我们可以将将同步通讯理解为打电话,需要实时响应,而异步通信则可理解为发送短信,不需要马上回复。我们往往会在面对超高吐吞量的场景下采取异步通讯,因为这就好比一个人不可能同时接打很多电话,但是他可以同时接收很多的电子邮件一样。
同步调用虽然让系统间只耦合于接口,而且实时性也会比异步调用要高,但是我们也需要知道同步调用会带来如下几个问题:
所以,异步通讯相对于同步通讯来说,除了可以增加系统的吞吐量之外,最大的一个好处是其可以让服务间的解耦更为彻底,系统的调用方和被调用方可以按照自己的速率而不是步调一致,从而可以更好地保护系统,让系统更有弹力。
异步通讯通常来说有三种方式:请求响应的方式、消息订阅的方式、使用 Broker 的方式。
在同步的请求响应下,发送方(sender)会直接请求接收方(receiver),被请求方接收到请求后,直接返回请求方需要的数据。但是在异步的场景下,对于被请求方来说,收到请求后往往是需要一段相对较长的处理事件,是无法直接返回的,对于这种场景,常见的是两种方式:接口轮询、方法回调。
很明显,这种情况下还是有一定耦合的。是发送方依赖于接收方,并且要把自己的回调发送给接收方,处理完后回调。
消息订阅的方式的情况下,接收方(receiver)会来订阅发送方(sender)的消息,发送方会把相关的消息或数据放到接收方所订阅的队列中,而接收方会从队列中获取数据。
这种方式下,发送方并不关心订阅方的处理结果,它只是告诉订阅方有事要干,收完消息后给个 ACK 就好了。
很明显在这种异步通讯的方式下,我们抽出了一种“事件”,并于此同时,达到了无状态服务(Stateless)的目的(请求数据、返回数据,服务里面还可能需要保存调用的状态),这其中有太多的好处,无状态意味着我们可以非常方便地运维。
对于“消息订阅”方式下,接收方需要向发送方订阅事件,所以是接收方依赖于发送方。这种方式还是有一定的耦合。所以我们可以将事件的承接方抽象出来,这就是使用 Broker 的方式。
所谓 Broker,就是一个中间人,发送方(sender)和接收方(receiver)都互相看不到对方,它们看得到的是一个 Broker,发送方向 Broker 发送消息,接收方向 Broker 订阅消息。如下图所示。
这是完全的解耦。所有的服务都不需要相互依赖,而是依赖于一个中间件 Broker。这个 Broker 是一个像数据总线一样的东西,所有的服务要接收数据和发送数据都发到这个总线上,这个总线就像协议一样,让服务间的通讯变得标准和可控。
比较官方的定义:事件驱动是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。在计算机编程、公共关系、经济活动等领域均有应用。
事件驱动是很早作为术语出现在了 GUI 编程中,比如,用户在界面点击了按钮,就会发送一个 “点击” 事件,而相应的会有一个处理 “点击” 事件的事件处理器会来处理该事件。但实际 GUI 编程对他的定义与我们企业级的框架设计并不处于同一抽象级别。
上面我们所提到的 “消息订阅” 和 “使用 Broker” 的两种方式都抽象出了事件,并且二者也都是比较著名的事件驱动架构(EDA – Event Driven Architecture),因此事件驱动架构可以使用或不使用消息传递来实现:
我们常常在提起事件驱动的同时提起消息驱动,并也常将二者做比较并试图找出区别,这里我借用,stackoverflow 上一位答主的回答,这也是我比较信服的:
假设您正在为电子商务网站构建支付服务。下订单时,订单服务将要求您的付款服务授权客户的信用卡。只有当信用卡被授权时,订单服务才会将订单发送到仓库进行包装和运输。 您需要与处理订单服务的团队就信用卡授权请求如何从他们的服务发送到您的服务达成一致。有两种选择。
由此可以看出,即使对二者含义加以区分时,也不过可以将我们前面提到的 “消息订阅” 和 “使用 Broker” 的两种方式,分别概括为事件驱动与消息驱动。因此笔者的结论是,二者的本质上一致的,都是抽象出了“消息“或”事件”来达到无状态异步调用的目的,只是在实现方式上存在一定差别,而且大部分人对二者认知也不统一,所以在大的场景下,二者可以当作同一回事,即文中所提到的“事件驱动”!
# 事件驱动编程的优点
# 事件驱动编程的缺点
事件循环器(Event Loop)是一个程序结构,用于等待和发送消息和事件。事件驱动编程的代码核心就是事件循环器,根据实现的方式不同,在网络编程中基于事件驱动主要有两种设计模式: Reactor 和 Proactor。这也分别对应了我们前面提到的 “消息订阅” 和 “使用 Broker” 的两种方式。这里只做简单介绍:
# Reactor 模式
首先来回想一下普通函数调用的机制:
和普通函数调用的不同之处在于:应用程序不是主动的调用某个 API 完成处理,而是恰恰相反,应用程序需要提供相应的接口并注册到 Reactor 上,如果相应的事件发生,Reactor 将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
# Proactor 模式
在 Proactor 模式下,可以理解为当 I/O 事件触发的一瞬间,I/O事件会自动进入一个队列中,或者说一个容器中。当然进入容器前可能还要做一些处理,比如将数据写入用户指定的缓存区等。而 Proactor 只需要主动地去该容器中取事件,将 IO 完成的信息通知给用户线程。
EventBus 可以被用来在各种自定义的监听事件中使用,包括不限于 Activity、Fragment、Service 等等等等需要进行数据传递的地方,不过应该只局限于 app 内部。
对于 EventBus ,各种编程语言中都有着自己的实现,这里不做过多赘述!
目前系统中的异步通信主要是采用消息中间件,消息中间件采用的异步方式为 broker 方式。
比较常见的 MQ 实现: