在我们实际的开发过程中,我们肯定会用到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. 图文带你看懂JavaScritpt引擎V8与JS执行过程

    浏览器原理 浏览器内核与js引擎 浏览器内核又称"排版引擎","渲染引擎","浏览器引擎",叫法很多,简单来说干的活就是将代码(HTML,X ...

  2. Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo

    前言   上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍.基础使用,并将其封装一层Qt.  本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt ...

  3. k8s+crio+podman搭建集群

    前言 在传统的k8s集群中,我们都是使用docker engine做为底层的容器管理软件的,而docker engine因为不是k8s亲生的解决方案,所以实际使用中会有更多的分层.之前我们也讲过,k8 ...

  4. 5-1 Vant | 移动组件库

    Vant 什么是Vant Vant是一个轻量,可靠的移动端组件库,2017开源 目前 Vant 官方提供了 Vue 2 版本.Vue 3 版本和微信小程序版本,并由社区团队维护 React 版本和支付 ...

  5. Java代码优化的30个小技巧

    前言 我之前写过两篇关于优化相关的问题:<聊聊sql优化的15个小技巧>和<聊聊接口性能优化的11个小技巧>,发表之后,在全网受到广大网友的好评.阅读量和点赞率都很高,说明了这 ...

  6. Scala 练习题 学生分数案例

    一.相关信息题目:1.统计班级人数2.统计学生的总分3.统计总分年级排名前十学生各科的分数4.统计总分大于年级平均分的学生5.统计每科都及格的学生6.统计偏科最严重的前100名学生数据样例(部分数据) ...

  7. EfficientFormer:轻量化ViT Backbone

    论文:<EfficientFormer: Vision Transformers at MobileNet Speed > Vision Transformers (ViT) 在计算机视觉 ...

  8. 并查集和kruskal最小生成树算法

    并查集 先定义 int f[10100];//定义祖先 之后初始化 for(int i=1;i<=n;++i) f[i]=i; //初始化 下面为并查集操作 int find(int x)//i ...

  9. php date函数和首位带0问题

    一.带零 echo date('Y-m-d'); 2012-08-08 二.不带零 echo date('Y-n-j'); 2012-8-8 以下为参数详解(转载): a - "am&quo ...

  10. show create table底层流程跟踪

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 导语 SHOW CREATE TABLE语句用于为指定表/视图显示创建的语句,本文将简要描述如何在MySQL源码里跟踪和学 ...