消息队列高手课
李玥
京东零售技术架构部资深架构师
立即订阅
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讲)
结束语 | 程序员如何构建知识体系?
消息队列高手课
登录|注册

05 | 如何确保消息不会丢失?

李玥 2019-08-01
你好,我是李玥。这节课我们来聊聊丢消息的事儿。
对于刚刚接触消息队列的同学,最常遇到的问题,也是最头痛的问题就是丢消息了。对于大部分业务系统来说,丢消息意味着数据丢失,是完全无法接受的。
其实,现在主流的消息队列产品都提供了非常完善的消息可靠性保证机制,完全可以做到在消息传递过程中,即使发生网络中断或者硬件故障,也能确保消息的可靠传递,不丢消息。
绝大部分丢消息的原因都是由于开发者不熟悉消息队列,没有正确使用和配置消息队列导致的。虽然不同的消息队列提供的 API 不一样,相关的配置项也不同,但是在保证消息可靠传递这块儿,它们的实现原理是一样的。
这节课我们就来讲一下,消息队列是怎么保证消息可靠传递的,这里面的实现原理是怎么样的。当你熟知原理以后,无论你使用任何一种消息队列,再简单看一下它的 API 和相关配置项,就能很快知道该如何配置消息队列,写出可靠的代码,避免消息丢失。

检测消息丢失的方法

我们说,用消息队列最尴尬的情况不是丢消息,而是消息丢了还不知道。一般而言,一个新的系统刚刚上线,各方面都不太稳定,需要一个磨合期,这个时候,特别需要监控到你的系统中是否有消息丢失的情况。
如果是 IT 基础设施比较完善的公司,一般都有分布式链路追踪系统,使用类似的追踪系统可以很方便地追踪每一条消息。如果没有这样的追踪系统,这里我提供一个比较简单的方法,来检查是否有消息丢失的情况。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《消息队列高手课》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(89)

  • 业余草
    一句话,消费做好幂等性即可!
    2019-08-01
    3
    39
  • ly
    老师,我有几个理解:
    当produer发送消息给blocker的时候(send方法),此方法会在blocker收到消息并正常储存后才返回,此期间应该会阻塞,也就是如果blocker配置同步刷盘,可能会增加调用时间(只能出现对消息敏感的场景)。
    另外拉消息的时候,消费者A进行pull后,没有返回确认给blocker就挂了(或者因代码问题导致一直阻塞),这时消息应该还在blocker的,消费者B如果此时pull消息,是否会拉取到刚刚那条给消费者A的消息?衍生的疑问就是两个消费者先后去拉消息是否能拉到同一条消息(在前者未给blocker发确认的前提下)。
    对于消费者处理重复消息的问题:一般消息中都会存在一个唯一性的东西,不管是消息队列本身的msgId还是业务订单号之类的,可以在db中存在一个消费表,对这个唯一性东西建立唯一索引,每次处理消费者逻辑之前先insert进去,让数据库来帮我们排重我觉得是最保险的。

    作者回复: 两个消费者先后去拉消息是否能拉到同一条消息?

    首先,消息队列一般都会有协调机制,不会让这种情况出现,但是由于网络不确定性,这种情况还是在极小概率下会出现的。

    在同一个消费组内,A消费者拉走了index=10的这条消息,还没返回确认,这时候这个分区的消费位置还是10,B消费者来拉消息,可能有2种情况:

    1. 超时前,Broker认为这个分区还被A占用着,会拒绝B的请求。
    2. 超时后,Broker认为A已经超时没返回,这次消费失败,当前消费位置还是10,B再来拉消息,会给它返回10这条消息。

    2019-08-02
    1
    21
  • godtrue
    如何确保消息不会丢失
    1:WAL
    2:分布式WAL
    除非地球爆炸,否则问题不大。
    猜测各种消息队列或者数据库,确保消息不丢只能这么玩,就连人也一样,脑袋记不住那就写下来,怕本子弄潮啦!那就光盘、U盘、磁盘、布头、木头、石头北京、上海、深圳都各写一份。
    consumer接到重复消息,那就业务去重,怎么去?
    1:业务处理逻辑本身就是幂等的,那天然就去掉了
    2:业务处理逻辑非幂等,那就消息先去重,根据业务ID(标识消息唯一性的就行),去查询是否消费过此消息了,消费了,则抛弃,否则就消费

    作者回复: 没毛病,很多底层的方法和技术就是通用的

    2019-08-20
    14
  • kane
    产生重复消息原因:
    (1).发送消息阶段,发送重复的消息
    (2) 消费消息阶段,消费重复的消息。
    解决办法:
    业务端去重
    1)建立一个消息表,consumer消费之前,拿到消息做insert操作,用消息id做唯一主键,重复消费会导致主键冲突。
    2)利用redis,给消息分配一个全局id,只要消费过该消息,将消息以K-V(< id,message>)形式写入redis,消费消息之前,根据key去redis查询是否有对应记录。
    2019-08-01
    9
    14
  • QQ怪
    建议老师加餐如何做幂等性

    作者回复: 不用加餐,这是教学大纲内的内容,下节课就会讲到滴。

    2019-08-01
    10
  • 王立光
    假如消费时由于某种原因,一直没发ack。rocketmq是不是会一直发这条消息,这样导致下面消息都无法被消费?

    作者回复: 是的。

    rocketmq为了解决这个问题,增加了一个死信队列,对于这种反复投递都无法成功的消息,会被移动到死信队列中,避免卡住其他消息。

    2019-08-15
    1
    6
  • Better me
    对于思考题,我认为也可以像老师说的那样查看消息是否丢失的方法,如果Producer的某条消息ack相应因为网络故障丢失,那么Producer此时重发消息的唯一标识应该和之前那条消息是一样的,那么只需要在Consumer接受消息前判断是否有相同标识的消息,如果有则拦截。还可以在消费端业务逻辑接口中做幂等判断,前面那种可以做到不侵入到业务代码中,老师看看有没有什么问题

    作者回复: 非常好!但你需要考虑一下,在分布式环境中“Consumer接受消息前判断是否有相同标识的消息”该如何实现呢?

    2019-08-01
    1
    6
  • TH
    幂等性是一种办法,如果做不到幂等性,那么在消费端需要存储消费的消息ID,关键这个ID什么时候存?如果是消费前就存,那么消费失败了,下次消费同样的消息,是否会认为上次已经成功了?如果在消费成功后再存,那么消费会不会出现部分成功的情况?除非满足事务ACID特性。

    关于消息丢失检查还有一点疑问:如果靠ID连续性来检查,是不是说一个producer只能对应一个consumer?

    作者回复: 不用,Producer发消息的时候带着ProducerId并要指定分区发送,Consumer消费的时候,需要按照每个Producer来检查序号的连续性。

    2019-08-01
    4
  • 游弋云端
    1、消费端支持幂等操作,业务上一般有难度;
    2、消费端增加去冗余机制,例如缓存最新消费成功的N条消息的SN,收到消息后,先确认是否是消费过的消息,如果是,直接应该ACK,并放弃消费。

    作者回复: 思路是没问题的。

    2019-08-01
    3
  • DC
    对于重复消息风险的处理代码,必须做好幂等。
    有一种场景,消息发出后因为网络问题没有得到响应,此时服务挂掉,也无法重新发起消息,这种情况这个消息算丢失了吧。
    思路是在发消息前需要记录消息发送记录,发送完成后标记完成,重启服务后查看发送消息,确无响应的消息,进行重发。不知道我提到的场景是否有问题

    作者回复: 且听下回分解。

    2019-08-01
    3
  • 芥末小龙
    玥哥好,我jio着只要在消费端做好幂等就可以,业务借口最好都要做幂等性校验,

    作者回复: 你这结论都是用无数bug换来的呀。

    2019-08-01
    3
  • 月下独酌
    消息需要入库可以靠唯一索引或主键约束,判断为重复的数据无法插入
    2019-08-01
    3
  • 陈天柱
    老师您好,我最近处理一个分布式事务场景,就是支付成功以后回调处理刷新订单状态和刷新优惠券状态的分布式事务问题。我有一个疑问就是,假如刷新订单状态失败,就没有发消息到队列中,而是在刷新订单状态成功了以后,再发消息到队列中,只要保证消息不丢的前提下,分布式事务能得到保证吗?换言之,就是消息不丢的话,可以保证最终一致性,事务消息的回查也能保证最终一致性,两者的概念感觉有一些交集。所以希望老师能抽空帮忙分析一下,万分感谢🙏

    作者回复: 你这个例子中,最终一致性应该是指:“经过一段时间后,订单状态和优惠券状态最终会保持一致。”

    但是,你这个场景不太适合使用事务消息来解决,虽然和我们上节课中的例子相比,只是把购物车换成了优惠券。但你有没有考虑到,有人会恶意利用这个短暂的不一致时间来刷优惠券?比如,利用下单成功,但优惠券还没来得及扣减这个时间差,一个优惠券反复下单?

    2019-10-19
    1
    2
  • Cc
    rocketmq默认失败重试次数是2,如果2次均失败,或者说重试次数都失败了,这种情况一般在实际生产中是怎么处理的?

    作者回复: 放到私信队列中,人工处理。

    2019-08-04
    1
    2
  • skyun
    老师,我关于事务消息有个疑问:如果生产者在执行完本地事务后向broke提交确认,但是此时broke挂了,提交失败,broke因为挂了也无法进行回查,那么此时这条消息是不是就丢了,从而导致两个系统中数据不一致,还是说这个不一致只是暂时的,等broke重启后,依旧会根据halfMessage进行回查?望解答

    作者回复: 。如果Broker是集群模式,其他的Broker会替代宕机的Broker来继续进行反查。如果Broker是单节点,只能等到Broker恢复后再继续进行反查。无论哪种模式,消息不会丢,是保存在磁盘上的。

    2019-08-02
    1
    2
  • linqw
    1、老师有个疑问检测消息丢失是在还没上线之前做的测试,但是会不会可能在线下没出现消息不一致,但是在线上的时候出现消息丢失了?线上
    检测消息丢失逻辑会关闭,那线上是会有其他的检测机制么?

    作者回复: 这个检测逻辑可以在线上做,不会影响业务的。

    2019-08-01
    2
  • 业余草
    一句话,消费做好幂等性即可!
    2019-08-01
    2
  • 张学磊
    首先看消费端业务是否可以保证幂等,比如审核流程通过的消息需要修改流程状态,这种业务可以保证幂等得业务只需要保证业务当幂等就可以。
    如果业务无法设计成幂等可以看消息是否有唯一标识,如果没有可以在消费端通过CRC算法计算出一个代表消息标识的属性,以此来判断消息是否消费过。
    2019-08-01
    2
  • 陈华应
    老师,再看一次这个的时候有个疑问:
    1,broker给producer发送ack,网络原因或者其他原因producer没收到,这种情况broker这条消息怎么办?
    2,broker给producer发送ack,broker怎么知道producer有没有成功收到这条ack信息呢?

    作者回复: Broker不知道ack有没有送达到producer,它也不管是不是送达了。

    因为,这个阶段的消息可靠性是由producer来保证的,它发了消息,收到了ack,那消息就一定送达了。如果没收到ack,那消息有可能送达了(ack丢失),也有可能没送达(消息丢失),对于这种情况,producer一般会重发消息,确保消息送达。这也就是为什么消息会有重复的原因之一。

    2019-10-19
    1
  • angel😇txy🤓
    思考题,从两个环节展开,生产端到broker,MQ系统内部必须生成一个inner-msg-id,作为去重和幂等的依据,这个内部消息ID的特性是:

    (1)全局唯一

    (2)MQ生成,具备业务无关性,对消息发送方和消息接收方屏蔽
    有了这个inner-msg-id,就能保证上半场重发,也只有1条消息落到MQ-server的DB中,实现上半场幂等。
    broker到consumer,由消费端做好幂等性,比如根据业务流水号去重

    作者回复: 这个总结非常到位!

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