在我们实际的开发过程中,我们肯定会用到MQ中间件,常见的MQ中间件有kafka,RabbitMQ,RocketMQ。在使用的过程中,我们必须要考虑这样一个问题,在使用MQ的时候,我们怎么确保消息100%不丢失?

案例背景

以我们熟悉的淘宝系统为例子,在用户下订单的时候,通常会给客户发放一下优惠劵。在整个过程中,交易服务和发优惠劵服务就是通过MQ消息队列进行通信。在交易服务完成后,交易服务可以发送“发一个满100减5的优惠劵”的消息给MQ。优惠劵服务则在消费端消费这个消息,从而实现真正的优惠劵的发放。

MQ的作用

在实际工作中,引入MQ消息最直接的目的就是让系统解耦合流量控制,从而实现系统的高可用和高性能。

  • 系统解耦:用MQ可以隔离系统上下游环境变换带来的不稳定因素。比如无论优惠劵服务需求如何变化,交易服务不用做任何改变。即使优惠劵服务出现故障,交易流程也不会受到影响。从而使交易服务和优惠劵服务达到了解耦的目的。从而使整个系统高可用。
  • 流量控制:当遇到秒杀等流量突增的场景,通过MQ可以实现流量的“削峰填谷”的作用,可以根据下游的处理能力自动调节流量。

但是,引入了MQ虽然实现了系统解耦和流量控制,同时引入引入了新的问题。

  1. 引入MQ实现系统解耦,会影响系统之间的数据传输一致性。在分布式系统中,如果两个节点之间存在数据同步,就会带来数据一致性问题。同理,在使用MQ时,我们也要解决消息生产端和消息消费端的数据一致性问题。
  2. 引入MQ实现流量控制,会使消费端处理能力不足,从而导致消息积压。

在使用MQ时,如何确保消息不丢失?

我们要从如下几个方面分析:如何知道有消息丢失?哪些环节可能丢失消息?如何确保消息不丢失?

网络中传输数据是不可靠的。要想解决如何不丢失消息的问题。首先,我们要知道哪些环节可能丢失消息。

哪些环节可能出现消息的丢失

一条消息从从生产到消费,主要可划为三个阶段:消息的生产阶段,消息的存储阶段,消息的消费阶段。

  1. 消息的生产阶段:从消息被生产出来,然后提交给MQ,只要能正常接收到MQ broker的ack确认响应,就表示发送成功。所以,这个阶段只要处理好返回值和异常,这个阶段是不会出现消息丢失的
  2. 消息的存储阶段:这个阶段一般交由MQ来保证。但是我们需要知道它的原理。比如,Broker会做副本,保证一条消息至少同步给两个节点再返回ack
  3. 消息的消费阶段:消费端从Broker上拉取消息,只要消费端在收到消息后,不立刻发送ack给broker,而是等到执行完业务逻辑之后,在发送消费确认,也能保证消息不丢失。

这个方案看似万无一失,每个阶段都可以保证消息不丢失。但是在分布式系统中,故障是肯定会有的。作为消息生产者,我并不能保证MQ是否弄丢了你的消息,消费者是否消费了你的消息。所以,本着Design For Failure的设计原则,我们需要有一种机制来check消息是否丢失。

如何check消息是否丢失?

总体的方案:在消息生产端,给发出的每一个消息指定一个全局唯一的ID,在消费端做校验。

具体实现:我们可以使用拦截器。在生产端发消息之前,通过拦截器将消息的全局唯一ID注入消息中,然后,在消费端收到消息之后,在通过拦截器检测消息ID或者消费状态。这样实现的好处就是消息的检测不会侵入业务代码中,可以通过ID找到具体丢失的消息,进行进一步的排查。

如何解决消息重复消费的问题

例如,在消息生产过程中,如果出现失败的情况,通过补偿机制会执行重试,重试就可能产生重复的消息,那么我们应该如何解决这个问题?这其实就是消费端幂等性问题(幂等性:就是一条命令执行任意多次所产生的影响和执行一次的影响相同)。只要消费者具备了幂等性,那么重复消费消息的问题也就解决了。



最简单的方案就是,在数据库中建一个消息日志表,这个表记录消息ID和消息执行状态。这个我们消费消息的逻辑变为:在消息日志中增加一个消息记录,再根据消息记录,执行发送优惠劵业务。我们每次都会在插入之前检查该消息是否已存在。这样就不会出现一条消息被多次执行的情况。这里的数据库也可以使用redis/memcache来实现唯一约束方案。

如何解决消息积压问题

消息积压反应的是性能问题。因为消息发送之后才会出现积压,所以这个和消息生产者没有关系。绝大部分MQ单节点每秒几万的处理能力,相对比业务逻辑来说,性能一般不会在MQ的存储上。所以这个问题,我们主要是从消费端入手解决。

如果是线上的突发问题,要临时扩容,增加消费端的数量,与此同时,降级一些非核心业务。通过扩容和降级承担流量。

然后,需要排查异常问题。通过监控/日志等手段分析消费者的业务代码是否出现了问题。

最后,如果是消费端处理能力不足,可以通过水平扩容来提高消费端的并发处理能力。在扩容消费者实例数的时候,必须同步扩容Topic的分区数量,确保消费者实例数和分区数对等。如果只增加消费者数量,不增加分区数。由于分区是单线程消费的,这样扩容没有效果。

比如在Kafka中,一个Topic可以配置多个Partition。数据会被写入多个Partition中,但是kafka约定一个分区只能被一个消费者消费,Topic的partition数量也决定了最大消费者的数量。

除此之外,还有

  • 如何选型消息中间件?
  • 消息中间件中的队列模型与发布订阅模型的区别?
  • 为什么消息队列能实现高吞吐?
  • 序列化、传输协议,以及内存管理等问题?

    等问题。我们下一篇文章再讨论

MQ的消息丢失/重复/积压的问题解决的更多相关文章

  1. RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略

    消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...

  2. RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得

    前言 首先说一点,企业中最常用的实际上既不是RocketMQ,也不是Kafka,而是RabbitMQ. RocketMQ很强大,但主要是阿里推广自己的云产品而开源出来的一款消息队列,其实中小企业用Ro ...

  3. rabbitmq 重复ACK导致消息丢失

    rabbitmq 重复确认导致消息丢失 背景 rabbitmq 在应用场景中,大多采用工作队列 work-queue的模式. 在一个常见的工作队列模式中,消费者 worker 将不断的轮询从队列中拉取 ...

  4. 实际业务处理 Kafka 消息丢失、重复消费和顺序消费的问题

    关于 Kafka 消息丢失.重复消费和顺序消费的问题 消息丢失,消息重复消费,消息顺序消费等问题是我们使用 MQ 时不得不考虑的一个问题,下面我结合实际的业务来和你分享一下解决方案. 消息丢失问题 比 ...

  5. MQ在高并发环境下,如果队列满了,如何防止消息丢失?

    1.为什么MQ能解决高并发环境下的消息堆积问题? MQ消息如果堆积,消费者不会立马消费所有的消息,不具有实时性,所以可以解决高并发的问题. 性能比较好的消息中间件:Kafka.RabbitMQ,Roc ...

  6. Kafka在高并发的情况下,如何避免消息丢失和消息重复?kafka消费怎么保证数据消费一次?数据的一致性和统一性?数据的完整性?

    1.kafka在高并发的情况下,如何避免消息丢失和消息重复? 消息丢失解决方案: 首先对kafka进行限速, 其次启用重试机制,重试间隔时间设置长一些,最后Kafka设置acks=all,即需要相应的 ...

  7. MQ如何解决消息的顺序问题和消息的重复问题?

    一.摘要 分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 1.消息的顺序问题 2.消息的重复问题 二.关键特性以 ...

  8. 小程序:前端防止用户重复提交&即时消息(IM)重复发送问题解决

    背景: 最近参与开发的小程序,涉及到即时消息(IM)发送的功能: 聊天界面如下,通过键盘上的[发送]按钮,触发消息发送功能 问题发现: 功能开发完毕,进入测试流程:测试工程师反馈说: 在Android ...

  9. 【MQ】消息队列及常见MQ比较

    一.什么是消息队列 我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用.消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰. ...

随机推荐

  1. NC204382 中序序列

    NC204382 中序序列 题目 题目描述 给定一棵有 \(n\) 个结点的二叉树的先序遍历与后序遍历序列,求其中序遍历序列. 若某节点只有一个子结点,则此处将其看作左儿子结点 示例1 输入 5,[3 ...

  2. 等待唤醒机制代码实现_包子类&包子铺类和等待唤醒机制代码实现_吃货类&测试类

    资源类:包子类 设置包子的属性 皮 陷 包子的状态:有 true 没有 false public class BaoZi { //皮 String pi; //陷 String xian; //包子的 ...

  3. 算法竞赛进阶指南0x34矩阵乘法

    文章目录 矩阵的相关性质再回顾 矩阵加速大法: ACWing205. 斐波那契 代码 ACWing206. 石头游戏 解题思路: 感受: 代码 矩阵的相关性质再回顾 对于一个矩阵 满足结合律 满足乘法 ...

  4. C#async\await组合

    一.概述 编译器提供的便捷功能,就是语法糖.我的理解是为了优化代码.被async修饰的函数被称之为异步函数,主要用于异步编程,着重于靠await实现回调机制. 二.声明 //async用在方法名之前 ...

  5. C#任务并行库TPL--Task应用

    一.概念 TPL的核心就是任务,一个任务代表一个异步操作,该操作可以通过多种方式运行,一个任务也可以由多个任务组成. 二.应用 1.创建任务有三种方法: var t1 = new Task(() =& ...

  6. 学习nginx的一点记录

    一.nginx定义 Nginx是一款轻量级的.高性能的,具备HTTP.反向代理.负载均衡的web服务器,同时还提供IMAP/POP3/SMTP服务,其特点是占用内存少,并发能力强. 二.nginx基本 ...

  7. 金融任务实例实时、离线跑批Apache DolphinScheduler在新网银行的三大场景与五大优化

    在新网银行,每天都有大量的任务实例产生,其中实时任务占据多数.为了更好地处理任务实例,新网银行在综合考虑之后,选择使用 Apache DolphinScheduler 来完成这项挑战.如今,新网银行多 ...

  8. 点击>>>解锁Apache Hadoop Meetup 2021!

    " 10月16日,属于开源发烧友的狂欢日来啦! Apache Hadoop Meetup 2021 开源大数据行业交流盛会盛大开启!让我们相约北京,一起嗨翻初秋~ 在当今信息化时代,逐渐成熟 ...

  9. codeforces600E Lomsat gelral【线段树合并/DSU】

    第一次AC这道题,是三年前的一个下午,也许晚上也说不定.当时使用的\(DSU\) \(on\) \(tree\)算法,如今已经淡忘,再学习新的算法过程中,却与旧物重逢.生活中充满不可知会的相遇,即使重 ...

  10. luogu1419 寻找段落 (二分,单调队列)

    单调队列存坐标 #include <iostream> #include <cstdio> #include <cstring> #include <algo ...