当前位置: 首页 > 科技观察

消息中间件系列介绍-传输和消费模式

时间:2023-03-18 14:57:41 科技观察

作者|葛贤良,单位:中国移动智能家居运营中心实验室简介近年来,互联网技术飞速发展,各行各业的信息量急剧膨胀。随着云计算和算力网络时代的到来,消息中间件在国内众多行业的重点应用中越来越受到重视。在高并发分布式场景下,合理使用消息中间件往往可以突破性能瓶颈,简化复杂度。从前期的“功能”和“协议”开始,读者对消息中间件技术有了初步的了解。本期从“传输模式”和“消费模式”入手,希望能让读者对消息中间件有更深入的了解。1、消息中间件的传输方式从传输的角度来看,MQ的消息方式可以分为“点对点”和“发布/订阅”两种模式。1.1点对点(PTP)顾名思义,PTP模型用于消息生产者和消息消费者之间的点对点通信。消息生产者根据一定的规则将消息发送给特定的消费者,通常是一个固定的消息队列(Queue),消息在传递给消费者之前存储在这个队列中。队列消息可以放在内存中,也可以持久化,以保证在消息服务失败时,消息仍然可以传递。在PTP模型中,一个队列可以有多个生产者和多个消费者。消息服务器根据接收消息的顺序将消息放入队列中。队列中的每条消息只能被一个消费者消费,消费后会从队列中移除。图1.1点对点模型需要注意的是,虽然使用了Queue的概念,但并不代表先进入队列的消息就先被消费。为了提高系统的并发性,很多MQ的实现引入了“消费者组”的概念,队列中的消息会分发给不同的消费者进行并行处理。这意味着消息在发送时是有序的,但在使用时是乱序的。有些MQ提供了“独占消费者”或“独占消费者”的概念,在这种情况下,只允许一个消费者消费队列中的消息。但是,这也牺牲了并发消费消息的能力。在消息量很大的情况下,会出现严重的消息积压。为了解决这个问题,一些MQ解决方案(如Kafka和RocketMQ)引入了分区的概念。同一主题的消息被不同的分区隔离。同一个分区的消息可以有序消费,不同分区的消息可以并行消费。.1.2发布订阅(Pub/Sub)Pub/Sub模式类似于广播消息。生产者将消息发布到一个主题(Topic),所有订阅该主题的下游消费者都可以接收到。这个消息。在此模型中,发布者和订阅者彼此不知情。与PTP模型中一条消息只能被一个消费者消费相比,Pub/Sub模型中的一条消息会发送给所有订阅了该主题的消费者进行消费。图1.2发布/订阅模型中Queue和Topic的区别:Queue实现了负载均衡,将生产者生产的消息发送到消息队列中供多个消费者消费。但是一条消息只能被一个消费者接受,当没有消费者可用时,消息将被保存直到有消费者可用。Topic实现了发布和订阅。当一条消息发布时,所有订阅该Topic的服务都可以获得这条消息;因此从1到N个订阅者都可以获得此消息的副本。2消息中间件的消费模式从消费的角度来看,MQ的消费模式可以分为“Push”和“Pull”两种模式。2.1推送消息模型(Push)消息生产者向消息服务器发送消息后,消息服务器主动将消息“推送”给消费者。2.1.1Push模型的优点Push模型最大的优点是实时性。因为服务器一有消息就可以推送,所以消息的消费没有“额外”的延迟。2.1.2Push模型的缺点Push模型看起来很漂亮,但是在实际使用中存在很多缺陷,以至于一些MQPush模型的底层也是通过Pull方式实现的。?Push模型存在以下问题:消费者的状态需要在broker端维护,不利于broker支撑大量消费者的场景;消费者的消费速度不一致,broker很难根据不同的消费情况选择相应的推送策略(时机);Broker很难处理Consumer无法消费消息的情况(Broker无法判断Consumer的失败是暂时的还是永久的);在Consumer的消费能力不足的情况下,大量的推送消息会增加Consumer的负载或不堪重负。2.2拉取消息模型(Pull)消息生产者将消息发送给消息服务器后,消息消费者主动从消息服务器拉取消息。2.2.1Pull模型的优点与Push模型相比,Pull模型有以下优点:Broker不再需要维护Consumer的状态(每次pull都包含offset等必要信息);状态由Consumer维护,Consumer可以很方便地从Broker获取消息的频率根据自身负载和其他状态决定;可以实现消息聚合。在Push模型中,broker无法预测消息写入的时间,只能在收到消息后立即推送给消费者,因此无法聚合消息再推送给消费者。在Pull模型中,Consumer主动获取消息,每次Pull一次,就可以批量获取Broker中的消息。2.2.2拉模型的缺点与优点和直接缺点相反,拉模型有以下缺点:实时性差。因为Consumer主动拉取消息,实时性与拉取周期相关,造成“额外”延迟。如果通过提高Pull的执行频率来降低??延迟,在没有消息的时候会产生大量的Pull请求(消息中间件完全解耦,Broker和Consumer无法预知下一条消息什么时候产生);在Pull模型中,状态是由Consumer维护的,因此多个Consumers之间需要相互协调,所以需要引入ZooKeeper(Kafka)或者实现NameServer(RocketMQ)等服务来完成Consumers之间的协调。2.3Push/Pull的区别?在实际应用场景中:高吞吐量的消息队列采用Pull方式而不是Push方式;采用Push模式时,消费者端的性能会影响整个消息队列服务器的性能;当采用Push方式时,很容易造成消息在broker上的积压,因为broker控制着消息的推送速率。如果消息数量多,每个消费者很难适应消息推送速率;2.4常见的消息中间件支持模型2.5Long-Polling上面说到,虽然有很多消息中间件都支持push方式,但是push语义实际上是通过pull方式实现的。比较成熟的做法是使用Long-Polling的方式,基于Push/Pull方案的优缺点,在性能和时效性之间找到平衡点。Long-Polling是Pull模式的一种变体。在Pull模型中,无论服务器上的数据有没有更新,客户端每隔一定时间拉取一次数据,返回的可能有更新的数据,也有可能什么都没有。LongPolling是指客户端发起LongPolling。这时候,如果服务端没有消息,它会一直持有请求,直到服务端有消息可以消费,或者超时才会返回请求。返回后,客户端会立即再次发起下一次LongPolling。这种方式解决了Pull方式下数据通知不及时的问题,减少了大量的无效轮询次数。Holdrequest是指服务端暂时不回复结果,保留相关请求,不关闭请求连接,等待相关数据准备好,再写回给客户端。可以看出,在Long-Polling模式下:当Broker一直有可读消息时,Long-Polling相当于执行间隔为0的Pull模式(每收到Pull结果,就发起下一次Pull请求,当然你也可以根据实际情况设置最小间隔保护时间或者单批最小消息条数);在Broker没有可读消息的情况下,请求在Broker中被阻塞,在下一条消息生成或请求“超时”之前将响应请求发送给Consumer。3总结虽然Push模式在语义上更符合事件驱动的架构风格,但是在当前互联网数据量大、并发高的背景下,Pull模式(包括Long-Polling)逐渐成为主流实现方案。对于用户来说,只有综合考虑两者的差异和特点,才能做出好的技术选型和分析决策。