介绍基于事件的架构

程序地带 www.yehe.org

译自:Introduction to Event-Driven Architecture

后面将引入几篇与EDA相关的文章,目的在于充分掌握EDA架构的优劣势。

目录

  • 介绍基于事件的架构

    • 简单定义

      • 永远不会发生的事件
      • 通道传输的事件
      • 通过异步性和通用性进行解耦
    • 事件处理的方式
      • 离散事件处理
      • 事件流处理
      • 复杂事件处理
    • 什么时候使用EDA
    • EDA的好处
    • EDA的缺点
    • 需要注意的点
    • 总结

在前面的微服务介绍一文中讨论了服务的颗粒度,以及保证松耦合的必要性。文中提出,服务应该是自治且完整独立的,并尽量减少同步通信。今天,我们将讨论松耦合意味着什么,并探索一种在微服务社区中越来越受欢迎的"交易技巧"-事件驱动架构。

简单定义

事件驱动架构(EDA)是一个促进生产和消费事件的软件架构规范。

一个事件表示一个感兴趣的动作。通常,事件对应一个创建或修改某些实体状态的动作。例如,在电子商务应用程序中下订单是一个事件,分发一个已下单的产品也是一个事件。一个消费者提交一个对接收的产品的评论也是一个事件。

永远不会发生的事件

关于事件的奇特之处在于它们不会明确地传达给可能关心它们的特定服务。事件"只会发生"。更为重要的是事件只会单纯地发生,与是否存在关心这些事件的特定服务无关。这听起来像是经常被引用的哲学思想:"如果一颗森林中的树,没有人听到它,那么它会发出声音吗?"。但这也是事件之所以强大的原因--事件会转换为一条对某些正在发生的事情的(自包含)记录,事件及其扩展程序(从根本上讲)与它们的处理程序是分离的。实际上,事件记录的生产者并不知道消费者是谁,甚至不知道是否存在消费者。

一条记录通常包含描述一个事件的信息。在之前的订单为例,其对应的事件的JSON描述如下:

{"orderId": "760b5301-295f-4fec-95f8-6b303a3b824a","customerId": 28623823,"productId": 31334,"quantity": 1,"timestamp": "2021-02-09T11:12:17+0000"
}

Node:尽管记录和事件存在细微的差别,但它们经常可以互换,即术语"事件"通常指代一个事件的"记录",为了简化描述,本文中将自由地使用这两个术语。

上述是对一个订单的高度简化。发起订单(购物车服务)的应用并不知道谁(如何,以及为什么)处理该订单。生产者会保证潜在的消费者能够捕获处理事件所需的一切信息。也就是说,订单记录不一定严格包含实现订单所需的每个属性。例如,不一定会直接指定产品的尺寸,存放位置以及消费者的送货地址等信息,但可以解析通过捕获的订单记录中的ID间接获得这些信息。关系数据库中的外键概念也同样适用于事件。

通道传输的事件

如果生产者和消费者都互不感知对方,那么两者该如何通信?

答案是通过术语"记录"进行粘合。事件通常被持久化到一个众所周知的位置,称为日志(有时也会用到术语"账簿")。日志是底层的,只能在后续消费者可以访问的地方附加生产者保存的事件数据结构。brokers(位于生产者和消费者之间的持久化中间件)负责操作日志。一旦产生了一个事件,任何人都可以消费该事件。

当处理事件驱动系统时,我们经常会使用术语"流"来描述一个或多个日志接口。日志是物理上的概念(使用文件实现),一条流是逻辑上的概念,表示构成事件的一组没无边界的记录,但记录要遵守某种特定的顺序。不同的流平台可能使用专有名称指代一条流。Apache Kafka使用topics和partitions来描述流。

生产者、消费者和流的关系如下:

Event-Driven Architecture Reference Model

回顾一下相关概念:

  • 事件是在离散时间点发生的感兴趣的动作:可能从外部进行观察和描述。
  • 事件持久化为记录:事件和记录尽管是相关的,但在技术上是不同的。一个事件表示事情的发生(如状态变更),本身是无形的。而一条记录是对该事件的精确描述。我们通常使用术语"事件"来指代其对应的记录。
  • 生产者是通过将相应的记录发布到流中来检测事件的接收器。(发布一条记录则表示发生了一个事件)
  • 流是持久化的有序的记录。它们通常由一个或多个基于磁盘的日志来进行持久化,当然,也可以使用数据库表、布式共识协议,甚至是区块链式的分散账本来支持持久化。
  • Brokers 负责对流的访问,方便读写操作,处理消费者状态以及在流上执行各种"内务"。例如,一个broker可能在记录溢出时对流的内容进行截取。
  • 消费者读取流,然后对接收到的记录作出回应。消费者对事件的回应可能会伴随一些额外的操作。例如,一个消费者可能会在本地数据库中持久化一条表项(通过发布的"更新"事件来重构远端实体的状态)(即更新对远端实体的描述)。
  • 消费者和生产者可能会重叠。例如,对事件的回应方,也可能产生一个或多个派生的事件。

通过异步性和通用性进行解耦

为什么EDA能够大大降低耦合度?

对耦合的一种比较务实的定义是:一个组件受其他组件影响的程度。耦合存在于空间(组件在结构上相关联)和时间(时间会影响组件之间的关系程度)上。对于后者,一个比较好的例子是,一个服务同步调用其他服务的REST API。如果被调用的服务down,则该服务将无法继续处理(响应被阻塞)。如果两个服务必须同时运行,则二者之间会存在一定程度的临时耦合(temporal coupling)。如果两个服务高度依赖,则称之为强耦合,反之,则称为松耦合。

Conceptual model of coupling

EDA采用两种方法来抑制耦合。

  • 回顾一下,事件是不能通信的,它们只会发生。发起事件的组件(通过发布记录)并不知道其他组件是否存在。因此,即使消费者不可用,生产者也不会停止工作---broker会暂时缓存事件,而不会对生产者施加反向压力。
  • broker对事件记录的持久化大大消除了时间观念。一个生产者可能会在T1时间发布一个事件,而一个消费者可能会在T2事件才会读取该事件,T1T2之间的间隔可能是毫秒级别的(所有组件正常)或小时级别的(如果某些消费者down或忙于其他事情)。

EDA并不是银弹,它没有一并消除耦合的概念(否则,系统中的组件将不再共同作用)。现在将关注点转移到broker上:为了让生产者和消费者有意义地进行解耦,它们必须依赖一个broker。这种方式增加了系统架构的复杂度,并引入了其他故障点。这也是为什么brokers必须是高性能且具有容错能力,否则,我们只是将一组问题换成另一组。

事件处理的方式

时间处理通常分为三种常用的方式。这些方式并不互斥,它们经常会同时存在于一个大型的事件驱动系统中。

离散事件处理

用于处理离散事件:例如在社交媒体平台上发布一个帖子。离散事件处理的特征在于出现的事件之间通常并无关联,可以独立处理。

事件流处理

用于处理一系列相关联的无边界事件流,事件的记录以某种顺序呈现,并携带一些与发生的事件有关的信息。例如,当一个业务实体发生联合变更时,消费者可能会按照生产者指定的顺序进行变更,并在本地数据库中保存一份该实体的副本。由于需要关注事件处理的顺序,因此不能离散地处理这类事件。消费者需要避免条件竞争,即多个消费者实例可能会同时修改数据库中的某条记录,进而由于乱序更新而导致数据不一致。

比较有名的流事件平台,如Kafka会依赖记录的key和partitions来保留更新顺序。Kafka同时也保证对一个实体的所有变更会被某个消费者处理,避免多个消费者并行处理事件而导致并发竞争。

复杂事件处理

复杂事件处理(CEP)是一种从一系列简单事件中得出或识别复杂事件的模式。例如监控一座建筑内的温度和延误感应器,并于推断是否发生了火情,并进行持续跟踪。单独的温度变化并不足以引发报警。更具意义的是温度峰值和变化率聚合而成的群体事件,进而有可能挽救生命。

通常更多会涉及此类处理,要求事件处理器持续跟踪先前的事件,并提供一个有效的方式进行请求和聚合。

什么时候使用EDA

一些场景下可以使用事件驱动架构带来的优势:

  • 不透明的消费者生态系统。这种情况下,生产者并不了解消费者,后者可能是一个短暂的过程,可能在短时间内来来往往!
  • 高扇出。一个事件可能由多个不同的消费者处理的场景。
  • 复杂的模式匹配。可能将事件串在一起来推断出更复杂的事件。(这类场景可能需要进行聚合,即上面描述的复杂事件处理)
  • 命令查询的责任分离。CQRS是一种分离数据存储区的读取和更新操作的模式。实现CQRS可以提高应用的可扩展性和弹性(在数据一致性上进行了取舍)。 这种模式通常与EDA相关。

EDA的好处

  1. 缓存和容错能力。事件消费的速率可能与生产者不同步,生产者不能为了与消费者保持一致而放慢速率。
  2. 生产者和消费者解耦,避免笨拙的点到点集成。EDA下很容易添加新的生产者和消费者,也很容易修改生产者和消费者的实现(前提是遵守约束事件记录的合同/方案)。
  3. 大规模扩展。通常会把部分部分事件流切分为若干不相关的自流,然后并行处理。随着事件的积压,我们也可以扩展消费者的数量来满足负载需求。像Kafka这样的平台会严格按序处理事件,并允许跨流进行大规模并行处理。

EDA的缺点

  1. 仅限异步处理。虽然EDA是一种有效的系统解耦模式,但它也将应用限制为异步事件处理。EDA并不能很好地处理像请求响应这样的交互(发起者必须等待响应才能继续处理)。
  2. 引入额外的复杂度。传统的客户端-服务器以及请求-响应仅会涉及两方,在采用EDA之后则引入了第三方-broker,作为生产者和消费者之间的媒介。
  3. 故障掩盖。这一点比较奇特,因为它似乎与解耦系统的本质背道而驰。当系统高度耦合时,一个系统中的错误会快速传递下去,并引起我们的关注。大多数场景下,我们需要避免这种情况:当一个组件失败时,尽量减小它对其他组件的影响。故障掩盖的负面影响是,它会在不经意间隐藏本应引起我们注意的问题。可以通过为每个事件驱动的组件添加实时监控和日志来解决,但这样做也带来了新的复杂度。

需要注意的点

EDA不是万能药,与很多强大的工具一样,它有可能被错误地使用。下面列出的内容不应该被认为是EDA的缺点,而应该作为开发人员和架构师在设计和实现事件驱动的系统时应注意的一系列陷阱。

  1. 复杂的编排。使用松耦合组件,用户可能会感到困惑,整个架构看起来像是一个Rube Goldburg机器(可以借助下图理解Rube Goldburg),整个业务逻辑也被实现为一系列(带有副作用的包装的)事件:一个组件发起的事件可能触发另一个组件发起另一个事件,然后触发另一个组件发起事件,以此类推。这种组件间的交互很快会变得无法理解。

  2. 将命令和事件混淆。一个事件用于单纯地描述发生的事情。它不会指定如何处理事件。而一个命令是针对特定组件的直接指令。由于命令和事件都是某种类型的消息,非常容易混淆,把命令误以为是一个事件。

    命令也可以放到EDA下,但要分清与事件的区别。命令可能会修改系统状态,通常会需要回滚方案。

  3. 消费者不可知。事件应该以某种方式捕获相关的属性,但并不会限制如何处理这些事件。说起来容易,做起来难。有时我们可能会无法获得足够的信息来限制添加到事件记录的内容(无法确定这些添加到记录中的信息是否最终有用)。

个人认为最重要的是上面的第二点,要区分命令和事件。

总结

微服务架构模式是构建更可维护、可扩展、更健壮的软件系统所涉及的难题之一。从问题分解的角度来看,微服务非常棒,但也带来了很多棘手的问题,其中一个就是耦合。与一开始相比,随意将系统拆分为少数微服务的做法可能会使您处于更糟糕的局面。有一个术语可以对其进行描述"分布一体式"。

为了帮助解决困惑,并定位耦合的问题,我们引入了事件驱动架构。

EDA是一个可以帮助降低系统组件间的耦合的有效工具,它是一种使用生产者、消费者、事件和流进行交互的模型。一个事件表示一个感兴趣的动作,任何组件都可能异步地发布和消费事件,而无需感知对方的存在。EDA允许组件独立操作和演化。但它不是解决所有问题的银弹。EDA是一个不错的选择,它带来的好处大大超过了采用它的成本。可以说,EDA是成功部署微服务的必要要素。

混合云中的事件驱动架构相关推荐

  1. 浅析企业私有云中的存储架构

    1 云计算 1.1 云计算 云计算(Cloud Computing)是分布式计算(Distributed Computing).并行计算(Parallel Computing).效用计算(Utilit ...

  2. 【事件驱动架构】专家组:事件驱动的大规模架构

    赖斯:欢迎来到我们关于架构的专题小组,你们一直想知道轨道.该专题小组称为事件驱动的大规模架构.当您思考事件驱动架构时,您会想到什么?这是规模.性能和灵活性的好处吗?也许你想到了一个你可能经历过的特殊问 ...

  3. 深度剖析 Apache EventMesh 云原生分布式事件驱动架构

    一.前言 近年来,随着微服务.云原生和 Serverless 概念的普及以及容器化技术的发展,事件驱动也再次成为热点,引起 IT 界广泛的关注.事件驱动架构是一种用于设计应用的软件架构和模型.对于事件 ...

  4. 如何最小化混合云中影子IT带来的风险

    尽管云计算有很多的好处,但缺点也不可避免,影子IT就是其中之一.受到云计算的低成本驱使,影子IT作为一种服务模式,将传统IT和CIO的职能转移到企业的生产部门.尽管影子IT的风险在之前已经被讨论过,但 ...

  5. 阿里云 EventBridge 事件驱动架构实践

    作者:周新宇 审核&校对:白玙.佳佳 编辑&排版:雯燕 本文内容整理自 中国开源年会 演讲 首先做一个自我介绍,我是 RocketMQ 的 PMC member 周新宇,目前负责阿里云 ...

  6. EDA 事件驱动架构与 EventBridge 二三事

    作者|肯梦 当下比较成功的企业已然认识到,要想最大限度提升运营效率和客户体验,务必将业务和技术两方面的举措紧密结合起来.运营事件或业务形势的变化是时下众多企业关注的焦点,这些变化能够为企业领导者带来切 ...

  7. node.js事件驱动_了解Node.js事件驱动架构

    node.js事件驱动 by Samer Buna 通过Samer Buna 了解Node.js事件驱动架构 (Understanding Node.js Event-Driven Architect ...

  8. 云中台技术架构_为什么开放基础架构在云中很重要

    云中台技术架构 在阅读红帽首席执行官Jim Whitehurst的最新文章时,我对OpenStack与州际公路系统之间的比较感到震惊. < 华尔街与科技>上的文章名为" Open ...

  9. [译]理解 Node.js 事件驱动架构

    原文地址:Understanding Node.js Event-Driven Architecture 大部分 Node 模块,例如 http 和 stream,都是基于EventEmitter模块 ...

最新文章

  1. 高效管理 Elasticsearch 中基于时间的索引——本质是在利用滚动模式做数据的冷热分离,热索引可以用ssd...
  2. 深度探索C++ 对象模型(5)-Initialization list(2)
  3. TLS1.3握手流程以及参数详解
  4. java中channelmessage,MessageStore支持的QueueChannel与Spring Integration Java Config
  5. JEECG V3版本组合查询实现方法
  6. es6 实例:Web 服务的客户端
  7. 相机翻转设计的华硕旗舰ZenFone 6固件更新:新增“超级夜间模式”
  8. Python练习题(四)
  9. Unity3D之NGUI基础8:UI锚点与深度
  10. php laravel设置创建目录的权限,在Laravel5中正确设置文件权限的方法
  11. mysql开启slowquery_log_MySQL slow_query_log慢查询日志配置详解
  12. 【数学建模】2016年全国大学生数学建模-系泊系统全面解析(附MATLAB实现部分代码)
  13. cad插件_CAD插件自动标注安装教程
  14. 判断斗地主卡牌类型的思路
  15. 开机加速——在注册表里禁止开机自检硬盘
  16. 常喝酸奶,远离糖尿病
  17. jQuery诞生记-原理与机制
  18. Python 之有趣的自幂数
  19. php 公众号 欢迎,关注公众号的欢迎语怎么设置?公众号欢迎语怎么加链接?
  20. 【区块链论文整理】VLDB篇

热门文章

  1. ML基础教程:线性建模fitlinear
  2. 参观消防队社会实践活动,切身感受消防官兵的工作内容和日常生活安排
  3. selenium网页自动登录、打卡(二)
  4. html 页面模糊效果,前端 - 页面滤镜成效及高斯模糊效果
  5. C++输入输入字符串的几种方法
  6. 透明背景flash html5,DW中实现flash的透明背景
  7. python3 迭代器实现斐波那契数列
  8. linux怎么日历写入文件夹,用c写的一个日历文件
  9. 机械行业ERP系统实施能成功吗?
  10. SOLIDWORKS Simulation带接头的装配体分析方法