消息队列高手课
李玥
京东零售技术架构部资深架构师
立即订阅
8426 人已学习
课程目录
已完结 41 讲
0/4登录后,你可以任选4讲全文学习。
课前必读 (2讲)
开篇词 | 优秀的程序员,你的技术栈中不能只有“增删改查”
免费
预习 | 怎样更好地学习这门课?
基础篇 (8讲)
01 | 为什么需要消息队列?
02 | 该如何选择消息队列?
03 | 消息模型:主题和队列有什么区别?
04 | 如何利用事务消息实现分布式事务?
05 | 如何确保消息不会丢失?
06 | 如何处理消费过程中的重复消息?
07 | 消息积压了该如何处理?
08 | 答疑解惑(一) : 网关如何接收服务端的秒杀结果?
进阶篇 (21讲)
09 | 学习开源代码该如何入手?
10 | 如何使用异步设计提升系统性能?
11 | 如何实现高性能的异步网络传输?
12 | 序列化与反序列化:如何通过网络传输结构化的数据?
13 | 传输协议:应用程序之间对话的语言
14 | 内存管理:如何避免内存溢出和频繁的垃圾回收?
加餐 | JMQ的Broker是如何异步处理消息的?
15 | Kafka如何实现高性能IO?
16 | 缓存策略:如何使用缓存来减少磁盘IO?
17 | 如何正确使用锁保护共享数据,协调异步线程?
18 | 如何用硬件同步原语(CAS)替代锁?
19 | 数据压缩:时间换空间的游戏
20 | RocketMQ Producer源码分析:消息生产的实现过程
21 | Kafka Consumer源码分析:消息消费的实现过程
22 | Kafka和RocketMQ的消息复制实现的差异点在哪?
23 | RocketMQ客户端如何在集群中找到正确的节点?
24 | Kafka的协调服务ZooKeeper:实现分布式系统的“瑞士军刀”
25 | RocketMQ与Kafka中如何实现事务?
26 | MQTT协议:如何支持海量的在线IoT设备?
27 | Pulsar的存储计算分离设计:全新的消息队列设计思路
28 | 答疑解惑(二):我的100元哪儿去了?
案例篇 (7讲)
29 | 流计算与消息(一):通过Flink理解流计算的原理
30 | 流计算与消息(二):在流计算中使用Kafka链接计算任务
31 | 动手实现一个简单的RPC框架(一):原理和程序的结构
32 | 动手实现一个简单的RPC框架(二):通信与序列化
33 | 动手实现一个简单的RPC框架(三):客户端
34 | 动手实现一个简单的RPC框架(四):服务端
35 | 答疑解惑(三):主流消息队列都是如何存储消息的?
测试篇 (2讲)
期中测试丨10个消息队列热点问题自测
免费
期末测试 | 消息队列100分试卷等你来挑战!
结束语 (1讲)
结束语 | 程序员如何构建知识体系?
消息队列高手课
登录|注册

03 | 消息模型:主题和队列有什么区别?

李玥 2019-07-27
你好,我是李玥。这节课我们来学习消息队列中像队列、主题、分区等基础概念。这些基础的概念,就像我们学习一门编程语言中的基础语法一样,你只有搞清楚它们,才能进行后续的学习。
如果你研究过超过一种消息队列产品,你可能已经发现,每种消息队列都有自己的一套消息模型,像队列(Queue)、主题(Topic)或是分区(Partition)这些名词概念,在每个消息队列模型中都会涉及一些,含义还不太一样。
为什么出现这种情况呢?因为没有标准。曾经,也是有一些国际组织尝试制定过消息相关的标准,比如早期的 JMS 和 AMQP。但让人无奈的是,标准的进化跟不上消息队列的演进速度,这些标准实际上已经被废弃了。
那么,到底什么是队列?什么是主题?主题和队列又有什么区别呢?想要彻底理解这些,我们需要从消息队列的演进说起。

主题和队列有什么区别?

在互联网的架构师圈儿中间,流传着这样一句不知道出处的名言,我非常认同和喜欢:好的架构不是设计出来的,而是演进出来的。 现代的消息队列呈现出的模式,一样是经过之前的十几年逐步演进而来的。
最初的消息队列,就是一个严格意义上的队列。在计算机领域,“队列(Queue)”是一种数据结构,有完整而严格的定义。在维基百科中,队列的定义是这样的:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《消息队列高手课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(140)

  • 发条橙子 。 置顶
    老师, 初学者有一些疑问的地方 ,希望老师可以帮忙解答 😁

    Rocket mq的模型图有些没有看懂 , 模拟下场景;比如生产者是商品中心发送商品状态的更新(主题)消息(比如下架),那商品中心有多台机器就对应多个producer 。消费者组有两个,分别是导购中心 和 活动中心。

    疑问一 :图中主题的队列是有多少个消费者组就有多少个队列么,是根据我们配置的消费者组数,mq自动增加主题队列个数么

    疑问二 :看到图中每个producer的消息都往所有的队列中添加一条消息,每个消费者组消费自己的队列,但没有看懂这个队列是如何支持 “当水平扩展消费者机器” 可以加快消息的处理 。 每个消费组应该都是按照队列等前一条处理完了,才能去处理下一条(ps:这样来看 ,rb mq也是这个样子,如何通过水平扩展机器来加快消息的处理)

    疑问三 : 图中队列的消费位置有点没看懂,我看是全剧唯一的,这是为什么,每个队列不能都是从0到1么

    作者回复: A1: 不是,消费者组和队列数没有关系,你这个例子中消费者组的数量是2个。队列数量可以根据数据量和消费速度来合理配置。RocketMQ和Kafka都可以支持水平扩容队列数量,但是都需要手动操作。

    A2:producer会往所有队列发消息,但不是“同一条消息每个队列都发一次”,每条消息只会往某个队列里面发送一次。

    对于一个消费组,每个队列上只能串行消费,多个队列加一起就是并行消费了,并行度就是队列数量,队列数量越多并行度越大,所以水平扩展可以提升消费性能。

    A3:每队列每消费组维护一个消费位置(offset),记录这个消费组在这个队列上消费到哪儿了。

    2019-07-27
    7
    34
  • linqw
    RocketMQ业务模型的理解,老师有空帮忙看下哦
    1、主题(topic)中有多个队列(队列数量可以水平进行扩容),生产者将其消息发送给主题中的某个队列(根据一定的路由规则,比如取模之类的),主题不保证消息的有序,只有队列中的消息才是有序的。
    2、从主题中的所有队列中取出消息给所有消费组进行消费,消息只能被消费组中的一个线程进行消费,有点类似线程池的形式,工作线程消费来自不同队列的消息,感觉这也是RocketMq,低时延的原因,不同队列中的消息可以同时被消费,并且消费组的线程也可以并发的消费不同的消息。
    3、由于主题中的一个队列都会被多个消费组进行消费,为此需要为每个消费组的消费的不同队列为此一个下标(每个消费组可以一直消费队列中的消息,无需等待其他消费组的确认),主题中的队列消息是有序的,为此需要等到所有消费组对此条消息进行确认,才能从队列中移除,感觉每个消费组的队列下标,可以一个队列维护一个CurrentHashMap来为此每个消费组的下标,这样的话可以防止锁的竞争。
    课后习题:尝试回答下课后习题,感觉队列可以维护一个全局的下标,消费队列时,使用CAS进行下标的获取,由于不保证消息消费的有序,这样的话可以并发的消费消息,由于有全局下标,不会出现获取队列的空洞消息。

    作者回复: 总结的非常到位!
    课后作业也完成的非常好!
    小红花走起!

    2019-07-30
    1
    31
  • a、
    今天的思考题,我觉得应该是,把消息队列的先进先出,改成数组的随机访问,用offset来控制消息组具体要消费哪条消息,mq不主动删除消息,消息有过期时间,如果到了过期时间,只能确认不能重新该消费,只保留最大可设置天数的消息。超过该天数则删除,还要维护客户端确认信息,如果有客户端没确认,需要有重发机制。不知道这个想法对不对?

    作者回复: 现代的消息队列大多就是这么实现的。

    2019-07-27
    31
  • Geek_de6f9a
    老师你好,想请问一下消费的顺序问题。
    对于有的消息,需要保证顺序性,比如交易状态和im消息。像im消息还要保证唯一性。

    Q1: rocketmq,一个消费组在一个主题下的多个队列并发消费就无法保证消息的顺序性。这种该如何处理?

    Q2: 客户端和mq要保持一种重试的机制,如果在网络延迟出现问题的时候,前面的消息一直未收到ack响应,若不做任何处理,后面的就会阻塞,还是重试之后放弃,若是不能发生丢失的信息该如何处理。

    Q3: 如何保证消息的唯一性,在重试的过程中,第一条消息已经发送,未收到ack,则进行第二次重试。此时网络故障恢复,则客户端会收到两条消息,客户端如何保证消息的唯一性。

    作者回复: A1:按照订单ID或者用户ID,用一致性哈希算法,计算出队列ID,指定队列ID发送,这样可以保证相同的订单/用户的消息总被发送到同一个队列上,就可以确保严格顺序了。

    A2:会有一个超时,超时之前会阻塞,超时之后就解除锁定,允许其他消费者来拉消息,由于消费位置没变,下次再有消费者来这个队列拉消息,返回的还是上一条消息。

    A3:这个问题我在后面的课中会专门来讲。

    2019-07-28
    6
    28
  • flyamonkey
    不要求严格顺序的话,应该是可以做到单个队列并行的,但这种情况下消息的消费可能就是个出队操作,而非等待消费端的ack后再出队了,这样势必会造成消息的丢失,所以需要有一定的补偿机制,如消息的重传和持久化等。个人见解,不知道是不是准确,还请老师指点~

    作者回复: 没错!具体可以看一下RocketMQ的并行消费的实现。

    2019-07-27
    1
    8
  • 陈泽坛
    可以这样理解吗?请老师解答:
    主要是rocketmq的部分。
    生产者允许多生产者同时生产消息,每条消息只会被主题中的某条队列接收,消费组内的消费者竞相消费所有队列,消费者会根据消费组在队列上的数来记录已消费位置,做到的就是队列上的有序,但是有可能整个topic下,是无序的。有可能图中的4要先被消费,但是另一队列中的3还没被消费。
    所以如果需要有序,就需要发送到同一条队列中去了。

    作者回复: 非常正确!

    2019-07-30
    1
    6
  • 渔夫
    老师讲得真好!我有几个问题想问下老师:
     1. rocket mq 和 kafka 同样处理能力的情况下,哪个开销比较小,相差多吗?
    2. 如果要保障消息有序,生产者通过负载hash固化发送到某一个队列,此时一个消费组中多个消费者就没什么意义了吧,因为只能从一个队列取数据
    3. 多个消费组的消费速度不一样,队列又是所有消费组共享的,这似乎有些不妥,实践中什么比较好的解法,请老师教我

    作者回复: A1:这个我没有测试过,你可以自己测试一下。

    A2:有意义,比如我们通过一个主题来传输一个多库MySQL实例的binlog,这个是必须严格有序的。但是,不用真的全局有序,只要更新同一个库的操作保证有序就行了。一条更新B库和一条更新A库的Binlog,就不需要严格有序。这样我们可以以库名为key进行hash,确保同一个库的消息都路由上同一个队列上就可以了。

    3. 正常情况下,多个消费组,他们的消费速度的上限就是生产速度(你消费再快也得等着消息生产出来),下限也是生产速度(否则就会出现消息积压),所以正常情况下,所有消费组的消费速度都应该和生产速度差不多。

    异常情况就是有的消费组会出现消息积压,如何解决积压的问题,我们后面会专门讲到。

    2019-07-30
    2
    6
  • Geek_87338d
    有三个问题没太想清楚,希望老师解答一下。
    1. rocket mq的模型,是不是每有一个新的consumer,都需要对mq进行配置新增一个queue?(我预设了一个前提是1queue有且只有1consumer来消费,1consumer只消费1queue不知道对不对)这样下游机器重启或者加机器,运维要累死。但没想明白它是怎么解决新增或者减少consumer的问题的?
    2. rocket mq的那个流程图,不能保证消息在全局顺序处理(比如处理0号消息的consumer1可能比处理1号消息的consumer2要慢,对于整个系统,1号消息被先处理),那么保证单个queue顺序处理的意义或者场景是什么呢?好像是为了消息的ack机制?
    3. 每个消息都确认(tcp是发送方一直发,接收方只确认最后的sequence,这样快得多)效率很低,那是怎么做到打满网卡的?靠大量的queue并发吗?

    作者回复: A1:队列只有一份,无论有多少订阅,所以不存在你说的问题。
    A2:目前的这种设计也是没办法的办法,还没有什么完美的解决方案既在topic上保证严格顺序,又要保证高性能和数据可靠性。但是目前这种实现也可以解决很多对顺序有要求的场景的问题。

    A3:实际上并不是一条一条确认的,而是一批一批确认的。一般consumer取一批消息,然后确认的时候直接提交这批消息中最后一条消息的位置来确认这批消息。

    2019-07-28
    1
    6
  • 见哥哥
    没啥问题,就是想点个赞,老师的声音很nice!

    作者回复: 谢谢

    2019-07-27
    6
  • 书策稠浊
    Rocket mq那张图是不是有问题,consumer是不直接对topic的,group才直接对topic,求解答,谢谢。

    作者回复: consumer在某个时刻对应的是某个queue(图中的实线),consumer group 对应 topic(同样是虚线方框),我理解这张图和你的描述是一致的。

    2019-07-27
    1
    4
  • Better me
    老师想问下RocketMq模型图中是消费组中每个消费者都对应一个队列以及相应的消费位置么,因为我理解应该是每个消费组都对应着一个具有相同消息的消息队列,只是相应的消费位置不同而已,这样每个消费组都能消费相同的消息

    作者回复: 不是这样的,无论有多少个消费组,队列只有一份,他们(消费组)都去这个队列上读取消息,由于消费快慢不同,每个消费组都会自己维护在这个队列上的消费位置。
    消费组之间是完全互不影响的。

    2019-07-28
    3
    3
  • rainbowbox
    类似TCP可靠传输方式,使用滑窗实现消息删除
    2019-07-27
    3
  • 川杰
    老师你好,RocketMQ中,消费位置(5)记录了当前消费组GroupA在A队列中的消费位置,(5)之前都被消费过,(5)之后都没有;那么这个(5)最终的作用是什么?
    是当GroupA再取下一个消息时,用来判断在队列A中的消息位置用的吗?除此之外还有其他作用吗?

    作者回复: 就是记录哪些消息消费了,哪些没消费。由于消费者是不记录消费位置的,它消费的时候只管去找Broker要消息,Broker必须知道消费到哪儿了,好找出下一条或下一批消息给客户端。

    2019-07-27
    3
  • ly
    老师您好,关于rocketmq的那张图有几个疑问:
    consumergourp中的某个consumer是和某个具体的queue一一关联绑定的么?还是说某consumer每次都随机从某queue消费,另外如果是一一关联的话,那某个consumer挂了,那关联的那个queue的消息该由哪个consumer来接替消费呢?
    另外product发给topic的消息是否是被topic随机分配到某个queue中的?还是说product必须指定发到哪个queue中?

    作者回复: 第一个问题,consumer和queue不是强关联的,但是在任何一个时刻,某个queue在同一个consumer group中最多只能有一个consumer占用。

    第二个问题,producer和queue不需要关联,简单点儿说,就是发到哪个queue都可以。RocketMQ的默认策略是轮询选择每个queue。

    2019-07-27
    2
    3
  • Penn
    维护一个offset抽象,offset由单个位置变成一个集合,集合中包含多个单个位置。类似多值信号量的机制

    作者回复: ✅

    2019-07-27
    3
  • 君莫笑
    老师,我有几个问题,就是rocket MQ模型图上,1、某一个消费组中的各个消费者可以消费某一个主题中的多个队列吗;2、如果可以,消费者拉取消息的时候是完全随机消费某一个队列还是可以指定策略呢?3、如果可以,那是不是主题下的每个队列都要对应给每一个消费者(注意不是消费组)维护一个offset来记录当前消费者消费位置呢?(或者是每个消费者对应每个队列维护呢),求解答

    作者回复: A1:可以。
    A2:有消费策略的,当然随机也是一种消费策略。
    A3:不是。首先,不同的消费组,消费位置是完全独立的,互不干扰。同一个消费组内:消息队列为每个队列维护一个消费位置(而不是给消费者实例)。因为,我们关心的是整个消费组能消费到全部队列的消息就可以了,不关系组内每个消费者消费多少消息,同一条消息,给A消费消费,还是给B消费者消费,是无所谓的。

    2019-07-31
    1
    2
  • ForEverLearning
    老师好,有一个问题请教下。
    以Kafka为例,目前MQ里已经积压了大量消息,且为了提升消费能力对消费端进行了水平扩展。
    那么对于已经积压的那些消息,Kafka会重新把它们分配到新的队列上去吗?

    作者回复: 不会,这部分只能慢慢消费了。

    针对这种情况,RocketMQ支持开启单个队列的并行消费,可以解决你的问题。

    2019-09-09
    1
  • locust
    RocketMQ中一个消费组有多个消费者,一个消费组消费topoic中的一个quene,要在队列上保证消息的有序性,消费组中一个消费者在消费消息的过程中,其余的消费者都是空闲的,是这样吗老师?

    作者回复: 消费组中某个消费者在消费一个队列的时候,其他同组的消费者是不能消费这个队列的,但是他们可以去消费同主题的其它队列,所以并不是空闲的。并且,即使是这些消费者并行消费不同的队列,在每个队列上,还是可以保证严格顺序的。

    2019-09-03
    1
  • 张三丰
    RocketMQ引入队列实际上是为了解决消息空洞的问题,只是间接提升了并发处理能力,也就是如果生产者连续向mq发送了3条消息,根据规则,路由到三个队列,这时候不同的消费组均可以同时消费这三条消息,这个时候就要看你的业务逻辑,如果你的目的是想去重,那么当一个消费组成功消费之后,获取分布式锁并记录一条消费日志,然后释放锁,当另一个消费组拿到锁之后发现有消费日志,这个时候不再消费,这样可以达到去重的目的。如果RocketMQ没有队列这个功能,那么就意味着你的多个消费组只能有一个消费组能消费到消息,如果消费组A消费到了,不巧这个时候消费组A网络抖动,那么这个时候整个消费集合都被卡住,系统崩溃。
    2019-08-29
    1
  • godtrue
    打卡,这节讲的清晰明了,小结一下
    1:消息模型的分类?
    1-1:队列模型——点对点模式——P2P
    P->Q->C
    1-2:发布订阅模型——多对多模式——P/S
    3P->3Q->3C
    这两模式的本质是一样的,可以相互转化,最大的区别在于,P/S模式的一条消息可以被多个消费者多次消费。
    P——生产者
    Q——队列,逻辑上就如一个数据结构中的队列,物理上可以有多个队列或分区组成,主题在某种情况下和队列是等价的,一个主题可以对多个分区。
    C——消费者

    2:典型的消息模型实现?
    2-1:rabbitMQ是P2P模式的典型应用
    2-2:kafka和rocketMQ是P/S模式的典型应用,注意他们是业务模式完全一样,但物理实现并不相同。

    3:P/S简化后就是P2P,那是不是世间只有这一张消息模式?

    单队列并行消费我觉得应该没问题,物理单队列可以划分为多个逻辑队列,对每个逻辑队列进行消费应该可以,有点像ConcurrentHashMap。
    其实多队列简化后能成为单队列,那单队列复杂化后应该也能成为多队列。只是做减法相对容易,做加法难一点。

    对于下面一段内容,有几个疑问?
    如果生产者没有收到服务端的确认或者收到失败的响应,则会重新发送消息;在消费端,消费者在收到消息并完成自己的消费业务逻辑(比如,将数据保存到数据库中)后,也会给服务端发送消费成功的确认,服务端只有收到消费确认后,才认为一条消息被成功消费,否则它会给消费者重新发送这条消息,直到收到对应的消费成功确认。
    这个确认机制很好地保证了消息传递过程中的可靠性,但是,引入这个机制在消费端带来了一个不小的问题。什么问题呢?为了确保消息的有序性,在某一条消息被成功消费之前,下一条消息是不能被消费的,否则就会出现消息空洞,违背了有序性这个原则。
    也就是说,每个主题在任意时刻,至多只能有一个消费者实例在进行消费,那就没法通过水平扩展消费者的数量来提升消费端总体的消费性能。为了解决这个问题,RocketMQ 在主题下面增加了队列的概念。
    1:老师讲的这种情况,应该是一个主题只有一个队列的时候吧?
    2:消息空洞的概念具体指什么?具体怎么形成?怎么解决?
    3:如果生产者没有收到服务端的确认或者收到失败的响应,则会重新发送消息——重复不会一直重复吧?应该有重试次数吧?如果有重设次数,超过重试次数,是否意味着消息就发送失败丢失啦?
    4:在消费端,消费者在收到消息并完成自己的消费业务逻辑(比如,将数据保存到数据库中)后,也会给服务端发送消费成功的确认,服务端只有收到消费确认后,才认为一条消息被成功消费,否则它会给消费者重新发送这条消息,直到收到对应的消费成功确认——同样这里有重试次数吗?如果有超过重试次数有该如何处理?

    作者回复: A1:是的。
    A2:我在08答疑中会详细解释这个问题,你可以看一下。
    A3:这个取决于生产者的业务代码是如何编写的。
    A4:有些消息队列会把这种“坏消息”放到一个特殊死信队列中,避免卡主整个队列消费。

    2019-08-20
    1
收起评论
99+
返回
顶部