消费模型的选择
主流消息队列一般支持 Pull、Push、Pop 三种消费模型。
Pull 模型
Pull(拉)模型是指客户端通过不断轮询的方式向服务端拉取数据。
它的好处是客户端根据自身的处理速度去拉取数据,不会对客户端和服务端造成额外的风险和负载压力。缺点是可能会出现大量无效返回的 Pull 调用,另外消费及时性不够,无法满足一些需要全链路低耗时的场景。
为了提高消费性能,Pull 模型都会支持批量读,即在客户端指定需要拉取多少条数据或者拉取多大的数据,然后传递给服务端。
为了解决空请求带来的问题,一般服务端会协助处理,有如下两个思路。
- 服务端 hold 住请求
当客户端根据策略拉取数据时,如果没有足够的数据,就先在服务端等一段时间,等有数据后一起返回给客户端。这种方案的好处是,可以尽量提高吞吐能力,不会有太多的空交互请求。缺点是如果长时间不给客户端回包,会导致客户端请求超时,另外当数据不够时,hold 住请求的时间太长就会提高消费延时。
- 服务端有数据的时候通知客户端
当服务端不 hold 住请求,立刻返回空数据,客户端收到空数据时则不再发起请求,会等待服务端的通知。当服务端有数据的时候,再主动通知客户端来拉取。这种方案的好处是可以及时通知客户端来拉取数据,从而降低消费延时。缺点是因为客户端和服务端一般是半双工的通信,此时服务端是不能主动向客户端发送消息的。
在 Pull 模型中,比较合适的方案是客户端告诉服务端:最多需要多少数据、最少需要多少数据、未达到最小数据时可以等多久三个信息。然后服务端首先判断是否有足够的数据,有的话就立即返回,否则就根据客户端设置的等待时长 hold 住请求,如果超时,无论是否有数据,都会直接给客户端返回当前的结果。
Push 模型
的本意是指当服务端有数据时会主动推给客户端,让数据的消费更加及时。
在实际的 Push 模型的实现上,一般有 Broker 内置 Push 功能、Broker 外独立实现 Push 功能的组件、在客户端实现伪 Push 功能三种思路。
第一种,Broker 内置 Push 功能是指在 Broker 中内置标准的 Push 的能力,由服务端向客户端主动推送数据。
缺点是当消费者很多时,内核需要主动维护很多与第三方的长连接,并且需要处理各种客户端异常,在性能上容易对 Broker 形成反压,导致 Broker 本身的性能和稳定性出现问题。
这种方案在主流消息队列中用得较少,比如 RabbitMQ 和某些金融证券领域的消息队列,为了保证消息投递的高效及时(比如全链路的毫秒级耗时),才会采用这种方案。
第二种,Broker 外独立实现 Push 功能的组件是指独立于 Broker 提供一个专门实现推模型的组件。通过先 Pull 数据,再将数据 Push 给客户端,从而简化客户端的使用,提高数据消费的及时性。
解决了 Broker 的性能和稳定性问题,也能实现 Push 的效果。缺点是虽然实现了 Push 的模型,但其本质还是先 Pull 再 Push,从全链路来看,还是会存在延时较高的问题,并且需要单独开发独立的 Push 组件,开发和运维成本较高。
这种模型的使用场景较为有限,主要是因为通过第三方组件的 Push 灵活性不够,性能会比 Pull 低。
第三种,在客户端实现伪 Push 功能是指在客户端内部维护内存队列,SDK 底层通过 Pull 模型从服务端拉取数据存储到客户端的内存队列中。
在客户端实现伪 Push,是目前消息队列在实现 Push 模型上常用的实现方案,因为它解决了客户体验上的主动回调触发消费问题。
Pop模型
Pop 模型想解决的是客户端实现较重,重平衡会暂停消费并且可能时间较长,从而出现消费倾斜的问题。
它的思路是客户端不需要感知到分区,直接通过 Pop 模型提供的 get 接口去获取到数据,消费成功后 ACK 数据。
从实现上来看,它将分区分配的工作移到了服务端,在服务端完成了消费者的分区分配、进度管理,然后暴露出了新的 Pop 和 ACK 接口。客户端调用 Pop 接口去拿取数据,消费成功后调用 ACK 去确认数据。
分区消费模式的设计
在数据的消费模式上主要有独占消费、共享消费、广播消费、灾备消费四个思路。
独占消费的好处是可以保证分区维度的消费是有序的。缺点是当数据出现倾斜、单个消费者出现性能问题或 hang 住时,会导致有些分区堆积严重。现在大部分消息队列默认支持的就是独占消费的类型,比如 Kafka、RocketMQ、Pulsar 等。
共享消费是指单个分区的数据可以同时被多个消费者消费。即分区的数据会依次投递给不同的消费者,一条数据只会投递给一个消费者。
广播消费是指一条数据要能够被多个消费者消费到。即分区中的一条数据可以投递给所有的消费者,这种方式是需要广播消费的场景。
灾备消费是独占消费的升级版,在保持独占消费可以支持顺序消费的基础上,同时加入灾备的消费者。当消费者出现问题的时候,灾备消费者加入工作,继续保持独占顺序消费。
此文章为11月Day8学习笔记,内容来源于极客时间《深入拆解消息队列 47 讲》