发布订阅模式成就分布式通信技术,干货满满
前面我们一起学习了分布式通信中的远程调用(分布式通信技术之远程调用:RPC)。远程调用的核心是在网络服务层封装了通信协议、序列化、传输等操作,让用户调用远程服务如同进行本地调用一样。
其实,这种方式就是通过网络服务层的封装实现了不同机器上不同进程之间的直接通信,因为是直接通信,所以通过线程阻塞的方式实现同步调用比较容易,因此通常被用于同步调用。比如,机器 1 上的进程 A 调用机器 2 上的进程 B,进程 A 被挂起,进程 B 开始执行,当进程 B 将值返回给 A 时,A 继续执行。
虽然这种方式也可以用于异步通信,但因为进程之间是直接交互的,所以当进程比较多时,会导致进程维护通信的复杂度非常高,且一个进程通信接口改变,与其通信的进程都会受到影响。
随着业务和分布式计算规模的逐渐增大和复杂化,远程调用模型有点心有余力而不足了,为此出现了专门的异步通信模式,也就是消息发布订阅模式和消息队列模式。在接下来的两篇文章中,我将与你详细讲述这两种通信模式。
什么是发布订阅?
其实,发布订阅的思想在我们的生活中随处可见。
比如,学术届电子论文的订阅方式。通常,各个会议方或出版社会将学术论文发布到论文网站(或平台上,比如 ACM、知网等),然后学生或老师向论文网站订阅自己感兴趣的论文,比如分布式相关的、AI 相关的等。
当会议方或出版社将论文发布到论文网站后,论文网站会根据订阅信息,将相应的论文推送给订阅者(比如通过邮件的方式)。这里的会议方或出版社就相当于生产者,负责发布论文,学生或老师就相当于消费者,而论文网站就相当于一个消息中心。
由此可以看出,发布订阅的三要素是生产者、消费者和消息中心,生产者负责产生数据放到消息中心,消费者向消息中心订阅自己感兴趣的消息,当发布者推送数据到消息中心后,消息中心根据消费者订阅情况将相关数据推送给对应的订阅者。这种将数据送到消费者手里的行为,是不是和我们现在常说的“送货上门”一样呢?
发布订阅的原理及应用
这个论文订阅的例子,充分体现了发布订阅的思想。接下来,我就与你进一步分析下发布订阅的原理吧。
发布订阅的基本工作原理
在分布式通信领域中,消息系统一般有两种典型的模式。一种是点对点模式(P2P,Point to Point),另一种是发布订阅模式(Pub/Sub,Publish/Subscribe)。接下来,我们就一起看看这两种模式,以帮助你深入理解发布订阅模式的原理。
首先,我们一起看一下什么是点对点模式。
生产者将消息发送到消息中心,然后消费者从消息中心取出对应的消息进行消费。消息被消费后,消息中心不再存储该消息,因此其他消费者无法再消费该消息。也就是说,点对点模式虽然支持多个消费者,但一个消息只能被一个消费者消费,不允许重复消费。
这种模式就好比,限定了每篇论文只能被一个用户消费,比如现在有一篇分布式相关的论文,这篇论文推送给学生 A 之后,论文网站就必须将其删除或下架,也就是说其他用户无法再获取或阅读该论文了。(当然实际情况并不是这样的,这里只是为了方便你理解,我做了相应的假设。)
接下来,我们看一下发布订阅模式。
生产者可以发送消息到消息中心,而消息中心通常以主题(Topic)进行划分,每条消息都会有相应的主题,消息会被存储到自己所属的主题中,订阅该主题的所有消费者均可获得该消息进行消费。
比如图中假设生产者 1 发布一个 Topic 相关数据或消息,消费者 1~3 均订阅了该 Topic 消息,则该消息会推送消费者 1~3,也就是说同一个消息被 3 个消费者消费了。
这种模式就好比,不同的方向代表不同的主题,比如分布式领域代表一个主题,当会议方或出版社发布分布式相关的论文时,该论文会被存储到论文网站的分布式主题下,同时学生或老师也会根据自己感兴趣的主题进行订阅。如果学生 A 订阅了分布式主题,那么当会议方或出版社发布分布式相关的论文后,会议网站会将这些论文推送给学生 A。
与点对点模式相比,发布订阅模式中一个消息可以被多个消费者进行消费,这也是和点对点模式的本质区别。
以上就是发布订阅中的两种典型模式了
在分布式系统中,通常会为多用户服务,而多个用户通常会关注相同类型的消息,因此发布订阅模式在分布式系统中非常常见。接下来,我再结合经典的分布式发布订阅消息系统 Kafka 的发布订阅原理及工作机制,来帮助你巩固对发布订阅的理解。
Kafka 发布订阅原理及工作机制
Kafka 是一种典型的发布订阅消息系统,其系统架构也是包括生产者、消费者和消息中心三部分。
生产者(Producer)负责发布消息到消息中心,比如电子论文的会议方或出版社;
消费者(Consumer)向消息中心订阅自己感兴趣的消息,获得数据后进行数据处理,比如订阅电子论文的老师或学生;
消息中心(Broker)负责存储生产者发布的消息和管理消费者订阅信息,根据消费者订阅信息,将消息推送给消费者,比如论文网站。在 Kafka 中,消息中心本质上就是一组服务器,也可以说是 Kafka 集群。
Kafka 的架构图,如下所示:
可以看到,Kafka 中除了 Producer、Broker、Consumer 之外,还有一个 ZooKeeper 集群。Zookeeper 集群用来协调和管理 Broker 和 Consumer,实现了 Broker 和 Consumer 的解耦,并为系统提供可靠性保证。
ZooKeeper 集群可以看作是一个提供了分布式服务协同能力的第三方组件,Consumer 和 Broker 启动时均会向 ZooKeeper 进行注册,由 ZooKeeper 进行统一管理和协调。
ZooKeeper 中会存储一些元数据信息,比如对于 Broker,会存储主题对应哪些分区(Partition),每个分区的存储位置等;对于 Consumer,会存储消费组(Consumer Group)中包含哪些 Consumer,每个 Consumer 会负责消费哪些分区等。
接下来,我们看看分区和消费组的原理和作用吧。
从上面的介绍可以看出,Broker 负责存储消息数据,Consumer 负责消费数据,Consumer 消费数据的能力会影响 Broker 数据存储是否溢出的问题。若 Consumer 消费太慢,会导致 Broker 存储溢出,Broker 就会丢弃一部分消息。
因此,Broker 和 Consumer 是 Kafka 的核心。接下来,我将带你进一步了解 Kafka 中 Broker 和 Consumer 的关键技术,如下图所示:
首先,我们看一下 Broker。
在 Kafka 中,为了解决消息存储的负载均衡和系统可靠性问题,所以引入了主题和分区的概念。其中,主题是一个逻辑概念,指的是消息类型或数据类型,就好比电子论文案例所讲的分布式是一个主题。
而分区是针对主题而言的,指的是一个主题的内容可以被划分成多个集合,分布在不同的 Broker 上,不同的 Broker 在不同的节点上。这里的集合就是分区,其中同一个分区只属于一个 Broker。
那么,分区有什么好处呢?
在我看来,分区的好处主要包括如下两点:
实现负载均衡,避免单个 Broker 上的负载过高。比如,Topic 0 被分为 Partiton-0、Partiton-1 和 Partiton-2 三个分区,分别分布在 Broker 0、Broker 1 和 Broker 2 上。这,就使得 Topic 0 的消息可以分布在这 3 个分区中,实现负载均衡。
实现消息的备份,从而保证系统的高可靠。比如,Topic 1 包含两个分区 Partiton-0、Partiton-1,每个分区内容一致,分别存储在 Broker 0 和 Broker 1 上,借此实现了数据备份。
接下来,我们再看看Consumer吧。
Kafka 中的消费组,指的是多个消费者的一个集合。一个消费组中的消费者共同消费主题消息,并且主题中每个消息只可以由消费组中的某一个消费者进行消费。
引入消费组的目的是什么呢?我们知道,在消息过多的情况下,单个消费者消费能力有限时,会导致消费效率过低,从而导致 Broker 存储溢出,丢弃一部分消息。Kafka 为了解决这个问题,所以引入了消费组。
这样一来,我们对发布订阅的基本工作机制就比较清楚了。接下来,我们再结合电商购物平台的例子,来看看发布订阅技术的具体应用吧。
发布订阅实践应用
假设在电商购物平台(为了方便理解,我对电商购物平台做了一定的简化)中,用户首先在订单系统下单,下单后库存系统会进行出货,通知系统则负责通知用户,整个流程可以用发布订阅的模式进行,如下图所示:
订单系统对应发布订阅模式中的生产者,消息中心有个主题专门存放下单信息,每次用户下单后,订单系统会向该主题写入数据;
库存系统和通知系统对应发布订阅模式中的消费者,它们会向消息中心订阅下单信息相关的主题;
订单系统向消息中心发布订单信息后,库存系统和通知系统都会获取到相应的下单信息,然后进行各自后续的操作,即库存系统进行出货,通知系统通过短信或邮件等方式通知用户。
接下来,我们总结下发布订阅模式的关键特征吧。
实现了系统解耦,易于维护。生产者 / 发布者只负责消息的发布,不需要知道订阅者 / 消费者的数量,也不需要知道订阅者 / 消费者获取消息用来做什么,而订阅者 / 消费者也不需要知道什么时候生产者 / 发布者会发布消息。
所以,生产者 / 发布者和订阅者 / 消费者互相独立,进而实现了系统解耦,每个部分可以单独维护,减少了因为生产者和消费者的耦合引入的一些相互影响。比如,如果两者耦合在一起,当生产者逻辑更改需要修改代码时,消费者部分的代码也受影响,因此每个部分单独维护降低了维护的复杂度。
实现了异步执行,避免高负载。生产者 / 发布者发布消息到消息中心,当消息超过消息中心可以存储的容量后,消息中心会丢弃掉超出的消息,这样系统就不会因为消息数量多而导致系统故障。
总结,我首先通过论文订阅的案例,与你介绍了什么是发布订阅以及发布订阅的基本原理,然后介绍了一个经典的分布式发布订阅消息系统 Kafka,最后以一个电商购物平台的案例描述了发布订阅模式的应用场景。希望对你有帮助
下一篇预告:分布式通信消息队列
在下方公众号【架构师修炼】菜单中可自行获取专属架构视频资料,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢我的哈
往期精选
今天来设计一套高可用高并发、海量存储以及可伸缩的消息中间件生产架构
消息队列Broker主从架构详细设计方案,这一篇就搞定主从架构
【分布式技术】分布式系统调度架构之两层调度,解决单体调度问题