写点什么

事件总线实现方式汇总

  • 2017-08-07
  • 本文字数:1744 字

    阅读完需:约 6 分钟

基于事件驱动的分布式异步架构模式多用于构建高可伸缩的反应式应用程序,适用于各种从简单到复杂的应用场景。它的核心思想是去耦合,将消息的发送和接收分开,实现异步处理消息事件。架构师 Mustafa Turan 在 Hackernoon 上分享了事件总线的几种实现方式,并总结了每一种实现方式的优缺点。

事件总线是实现基于事件驱动模式的方式之一,或者可以将其称为“Broker Topology”。事件发送者将事件消息发送到一个中心broker 上,事件订阅者向中心broker 订阅和接收事件,然后再处理接收到的事件。当然,订阅者不仅可以接收和消费事件,它们本身也可以创建事件,并将它们发送到事件总线上。下面列出了4 种事件总线的实现方式,并对它们的优缺点进行了总结。

事件总线和多个订阅者(绿色箭头)和通知发送者(红色箭头)

1. 向所有的订阅者发送事件

事件总线直接将输入事件(红色箭头)发送给订阅者(蓝色方块)

参与者

事件总线、订阅者(事件处理器)、事件创建者

实现

事件创建者向事件总线发送事件,事件总线将收到的事件发送给所有的订阅者。订阅者既可以处理接收到的事件,也可以创建新事件,然后把它们发送给事件总线。事件总线不关心订阅者是否成功接收到消息。

功能需求

通知、订阅、退订。

优势

实现起来很简单。

不足

事件总线需要将每一个事件消息复制一份给订阅者,也就是说,如果有 1000 个订阅者,每一个事件都会有 1000 份拷贝,这样会占用大量的内存。

事件总线不保证消息传递的可靠性,它会尝试给订阅者发送消息,而且只会尝试一次,如果出现错误,比如网络连接错误,订阅者可能就会收不到消息。
另外,事件总线不负责过滤消息,所以订阅者需要自己实现过滤逻辑。

Elixir 的参考实现

2. 向所有订阅者发送事件影子


事件总线和事件存储及事件观察者

参与者

事件总线、事件创建者、订阅者、事件存储(Event Store)、事件观察者(Event Watcher)

实现

事件总线在收到事件创建者发送过来的事件后,把事件保存到事件存储里,然后将事件影子(Event Shadow,也就是对原始事件的引用)发送给所有的订阅者。订阅者根据事件影子从事件存储里获取事件数据,再对数据进行处理。

事件总线为每一个事件创建一个事件观察者,观察者持有订阅者列表,当所有的订阅者都接收到消息后,观察者负责把事件从事件存储里删除。

事件总线仍然不保证订阅者一定会收到所有事件影子。如果有订阅者接收消息失败,相应的观察者就会被标记为“skipped”。

功能需求

通知、订阅、退订、保存 / 删除 / 获取、标记完成 / 跳过

优势

因为事件消息被保存在事件存储里,发送给订阅者的只是事件引用,所以占用内存会小很多。

不足

订阅者需要调用额外的方法,比如在收到事件影子之后要调用方法去获取事件数据,在处理完事件后还要调用方法通知事件总线已完成处理,或者通知事件总线跳过某个事件。另外,在事件总线端还要实现事件存储和事件观察者。这个对事件存储的实现要求比较高,如果订阅者数量很多,事件存储的读负载会很重,而且在写入事件时是阻塞式的。

这种实现方式仍然不会为订阅者过滤事件,所以订阅者还是需要自己实现事件过滤。

Elixir 的参考实现

3. 向经过过滤的订阅者发送事件影子

第三种实现方式与第二种是一样的,只不过不是将事件影子发送给所有订阅者,而是发送给经过过滤的订阅者,也就是说只发送给其中的一部分订阅者。事件总线需要记录订阅者感兴趣的主题,在这里可以使用正则过滤器为订阅者过滤主题。

这种方式的优势与不足和第二种也是一样的,只是多了事件过滤功能。

Elixir 的参考实现

4. 按顺序传递

为了保证顺序传递,可以对事件进行分区。关于如何通过分区来保证顺序传递,可以参考 Kafka 的论文,基本原理是让消费者消费属于自己的分区。

不足

动态增加分区或减少分区会变得很困难,而且需要自己实现分区器,消费者的实现也很复杂。

在实现订阅者时要注意的一些问题

对于第一种和第二种方式,需要通过阻塞的方式进行事件类型(也就是主题)匹配,避免进行不必要的事件拷贝,浪费了内存。

对于第三种和第四种方式,需要通过非阻塞的方式进行事件类型(也就是主题)匹配,因为订阅者只接收感兴趣的事件。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-08-07 19:004968
用户头像

发布了 322 篇内容, 共 151.6 次阅读, 收获喜欢 148 次。

关注

评论

发布
暂无评论
发现更多内容

李运华老师(前阿里P9)架构实战营 毕业总结

代廉洁

架构实战营

网络攻防学习笔记 Day92

穿过生命散发芬芳

网络攻防 8月日更

聊聊实时数仓架构设计

水滴

实时数仓 数仓架构 8月日更 数仓建设思路

Python OpenCV 图像处理之傅里叶变换,取经之旅第 52 篇

梦想橡皮擦

8月日更

Cobra 快速入门 - 专为命令行程序而生

xcbeyond

cobra Go 语言 8月日更

Java中的JVM是如何实现多线程的?

程序员阿杜

Java JVM 多线程 8月日更

架构训练营模块 2 作业 - 学生信息管理系统的架构文档 - 江哲

江哲

gopher成长之路(二):听万俊峰老师演讲有感

非晓为骁

学习 个人成长 gopher

菜鸡学习python

Augus

8月日更

架构师实战营 模块九总结

代廉洁

架构实战营

Discourse 图片上传的更新

HoneyMoose

pyinstaller 打包

橙橙橙橙汁丶

使用PSI(Pressure Stall Information)监控服务器资源

mazhen

Linux DevOps Linux Kenel linux运维

01 设计模式之单例模式

陈皮的JavaLib

设计模式 单例模式 8月日更

【前端 · 面试 】HTTP 总结(一)—— HTTP 概述

编程三昧

面试 大前端 HTTP 8月日更

「SQL数据分析系列」13. 索引和约束

Databri_AI

sql 索引 位图

01-架构的定义

Lane

八月未央,早睡早起

escray

读书笔记 8月日更

毕业设计-秒杀业务

白发青年

架构实战营

架构实战营-毕业设计

泄矢的呼啦圈

架构实战营

架构实战营-毕业总结

大可

现代分布式架构设计原则-可靠性

余先生

稳定性 可用性 弹性 可靠性

架构实战营毕业总结

白发青年

#架构实战营

非典型开发者的形象三变

脑极体

Java 代理模式

gin

Java 代理 Proxy

docker个人理解与安装

小鲍侃java

8月日更

架构师实战营 模块九作业(设计电商秒杀系统)

代廉洁

架构实战营

带你全面了解 Git 系列 02 - 常用 Git 技巧

淼💦 淼

git GitHub

爱你的人,会管着你。而不是惯着你。

石云升

娱乐圈 8月日更 刑法

Convolutional Neural Network (CNN)

毛显新

神经网络 深度学习 tensorflow 图像识别

手把手撸二叉树之叶子相似的树

HelloWorld杰少

面试 大前端 二叉树 数据结构与算法 8月日更

事件总线实现方式汇总_架构_Mustafa Turan_InfoQ精选文章