分布式通信:发布订阅
前言
分布式通信中的远程调用的核心是在网络服务层封装了通信协议、序列化、传输等操作,让用户调用远程服务如同进行本地调用一样。 通过网络服务层的封装实现了不同机器上不同进程之间的直接通信,因为是直接通信,所以通过线程阻塞的方式实现同步调用比较容易,因此通常被用于同步调用。比如,机器 1 上的进程 A 调用机器 2 上的进程 B,进程 A 被挂起,进程 B 开始执行,当进程 B 将值返回给 A 时,A 继续执行。
虽然这种方式也可以用于异步通信,但因为进程之间是直接交互的,所以当进程比较多时, 会导致进程维护通信的复杂度非常高,且一个进程通信接口改变,与其通信的进程都会受到影响。
随着业务和分布式计算规模的逐渐增大和复杂化,远程调用模型有点心有余力而不足了,为此出现了专门的异步通信模式,也就是消息发布订阅模式和消息队列模式。
什么是发布订阅?
发布订阅的思想在生活中随处可见。比如,学术届电子论文的订阅方式。通常各个会议方或出版社会将学术论文发布到论文网站(或平台上,比如 ACM、知网等),然后学生或老师向论文网站订阅自己感兴趣的论文。
当会议方或出版社将论文发布到论文网站后,论文网站会根据订阅信息,将相应的论文推送给订阅者(比如通过邮件的方式)。这里的会议方或出版社就相当于生产者,负责发布论文,学生或老师就相当于消费者,而论文网站就相当于一个消息中心。
发布订阅的三要素是生产者、消费者和消息中心,生产者负责产生数据放到消息中心,消费者向消息中心订阅自己感兴趣的消息,当发布者推送数据到消息中心后,消息中心根据消费者订阅情况将相关数据推送给对应的订阅者。
发布订阅的基本工作原理
在分布式通信领域中,消息系统一般有两种典型的模式:
- 点对点模式(P2P,Point to Point)
- 发布订阅模式(Pub/Sub,Publish/Subscribe)。
点对点模式
生产者将消息发送到消息中心,然后消费者从消息中心取出对应的消息进行消费。消息被消费后,消息中心不再存储该消息,因此其他消费者无法再消费该消息。点对点模式虽然支持多个消费者,但一个消息只能被一个消费者消费,不允许重复消费。
就好比限定了每篇论文只能被一个用户消费,比如现在有一篇分布式相关的论文,这篇论文推送给学生 A 之后,论文网站就必须将其删除或下架,其他用户无法再获取或阅读该论文了。
发布订阅模式。
生产者可以发送消息到消息中心,而消息中心通常以主题(Topic)进行划分,每条消息都会有相应的主题,消息会被存储到自己所属的主题中,订阅该主题的所有消费者均可获得该消息进行消费。
假设生产者 1 发布一个 Topic 相关数据或消息,消费者 1~3 均订阅了该 Topic 消息,则该消息会推送消费者 1~3,同一个消息被 3 个消费者消费了。
就好比不同的方向代表不同的主题,比如分布式领域代表一个主题,当会议方或出版社发布分布式相关的论文时,该论文会被存储到论文网站的分布式主题下,根据自己感兴趣的主题进行订阅。如果学生 A 订阅了分布式主题,那么当会议方或出版社发布分布式相关的论文后,会议网站会将这些论文推送给学生 A。
与点对点模式相比,发布订阅模式中一个消息可以被多个消费者进行消费,这也是和点对点模式的本质区别。
在分布式系统中,通常会为多用户服务,而多个用户通常会关注相同类型的消息,因此发布订阅模式在分布式系统中非常常见。
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 的核心。如下图所示:
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 存储溢出,丢弃一部分消息。
发布订阅实践应用
假设在电商购物平台中,用户首先在订单系统下单,下单后库存系统会进行出货,通知系统则负责通知用户,整个流程可以用发布订阅的模式进行,如下图所示:
订单系统对应发布订阅模式中的生产者,消息中心有个主题专门存放下单信息,每次用户下单后,订单系统会向该主题写入数据;库存系统和通知系统对应发布订阅模式中的消费者,它们会向消息中心订阅下单信息相关的主题;订单系统向消息中心发布订单信息后,库存系统和通知系统都会获取到相应的下单信息,然后进行各自后续的操作,即库存系统进行出货,通知系统通过短信或邮件等方式通知用户。
发布订阅模式的关键特征
:
-
实现了系统解耦,易于维护
。生产者 / 发布者只负责消息的发布,不需要知道订阅者 / 消费者的数量,也不需要知道订阅者 / 消费者获取消息用来做什么,而订阅者 / 消费者也不需要知道什么时候生产者 / 发布者会发布消息。 所以生产者 / 发布者和订阅者 / 消费者互相独立,进而实现了系统解耦,每个部分可以单独维护,减少了因为生产者和消费者的耦合引入的一些相互影响。比如,如果两者耦合在 一起,当生产者逻辑更改需要修改代码时,消费者部分的代码也受影响,因此每个部分单独维护降低了维护的复杂度。 -
实现了异步执行,避免高负载
。生产者 / 发布者发布消息到消息中心,当消息超过消息中心可以存储的容量后,消息中心会丢弃掉超出的消息,这样系统就不会因为消息数量多而导致系统故障。
知识扩展:观察者模式和发布订阅模式的区别是什么?
观察者模式下有观察者,就有被观察者。观察者负责监控被观察者的状态变更,如果被观察者的状态发生了改变,观察者根据状态的变更执行相关操作。举个例子,现在进程 A 是被观察者,进程 B 和进程 C 是观察者, 当进程 B 观察到进程 A 中变量 X 由 3 变为 4 时,执行 X+1 的操作;当进程 C 观察到进程 A 中变量 X 由 3 变为 4 时,执行 X-1 的操作。观察者模式,定义了被观察者与观察者的
直接
交互或通信关系。
发布订阅模式中存在发布者、订阅者和消息中心,订阅者需要向消息中心指定自己对哪些数据感兴趣,发布者推送的数据放入消息中心后,消息中 心根据订阅者订阅信息推送数据。发布者和订阅者之间引入了消息中心,实现的是
间接
通信。
观察者模式采用了直接通信,观察者和被观察者通信时延会低一些,但它们的依赖关系比较强,不管是被观察者还是观察者逻辑或接口有更改,另外一个均会受影响。
发布者和订阅者模式采用间接通信,引入了消息中心,相对比较厚重,且通信时延相对会高一 点,但实现了订阅者与发布者的解耦。
总结
发布订阅就是生产者产生消息发布到消息中心,消费者订阅自己感兴趣的消息,消息中心根据消费者的订阅情况将相关消息发给对应的消费者。
Kafka 是一个经典的发布订阅消息系统,采用多分区实现了消息备份、负载均衡,并引入消费组提高了消费者的消费能力,防止 Broker 因为存储资源不够丢弃消息的情况,从而提高了 Kafka 系统的可靠性。
发布订阅模式可以使系统松耦合易于维护,也可异步执行解决高负载问题,适用于系统解耦、流量削峰等场景。
发布订阅模式易于理解,与点对点模式很类似。不同的是,点对点模式中一个消息只能由一 个消费者消费,而发布者订阅者模式中一个消息可以由多个消费者消费。
不同的通信模式适用于不同的分布式场景,其中发布订阅模式适合具备多个生产者、多个消费者且异步处理的场景,比如现在的视频 App,多个用户都可以通过同一款 App 看同一部电视剧,当然这个电视剧可以是被不同的生产者发布。点对点模式由于其局限性,一般适用于需要进行点对点通信的场景,比如近场投屏等。