MQ系列1:消息中间件执行原理

MQ系列2:消息中间件的技术选型

MQ系列3:RocketMQ 架构分析

MQ系列4:NameServer 原理解析

MQ系列5:RocketMQ消息的发送模式

MQ系列6:消息的消费

MQ系列7:消息通信,追求极致性能

MQ系列8:数据存储,消息队列的高可用保障

MQ系列9:高可用架构分析

1 介绍

我们实际系统中有很多操作,不管你执行多少次,都应该产生一样的效果或返回一样的结果。 例如:

  • 前端页面重复提交选中的数据,服务端只产生对应这个数据的一个反应结果,只保存一次数据。
  • 我们发起一笔付款请求,也只能扣用户账户一次钱,即使遇到网络重发或系统bug重发,也应该只扣一次金额。
  • 消息通知,也应该只能收到一次,如果收到多次的扣款通知短信,会让用户误解的。
  • 创建商品订单,一次业务请求只能创建一个,创建多个就会变成购买多次,就会出问题。

以上等等很多重要的场景,都需要幂等的特性来支持。

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

例如,“getUserSex()和setRight()”函数就是一个幂等函数,包括数据库中的查询和删除也是一样的道理,它是天然幂等的。总之,幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的 。

2 消息队列中如何保证幂等性

2.1 消息队列的基本构成

我们先来回顾下 Message Queue的构成,这边以RocketMQ为例子:

RocketMQ主要有四大核心组成部分:NameServer、Broker、Producer以及Consumer四部分。

  • NameServer:Name Server是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。NameServer 是整个 RocketMQ 的 "中央大脑 " ,它是 RocketMQ 的服务注册中心,所以 RocketMQ 需要先启动 NameServer 再启动 Rocket 中的 Broker。
  • Broker: 消息服务器,作为Server提供消息核心服务, 它接收并存储Producer生产的消息,也提供消息给Consumer消费。Broker一般会分主从,Master 可读可写,Slave 只读。
  • Producer: 消息生产者,消息的发送方,负责生产消息传输给broker。RocketMQ提供了发送:同步、异步和单向(one-way)的多种模式。
  • Consumer: 消息消费者,消息的处理方,负责从broker获取消息并进行业务逻辑处理。

    另外其他如 Topic、 Message,也是重要的组成部分:
  • Topic:主题,发布/订阅模式下的消息统一汇集地,不同生产者向topic发送消息,由MQ服务器分发到不同的订阅者,实现消息的广播
  • Message:消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输。

2.2 消息队列的幂等分析

可以看出,消息发送和消息消费两个步骤是有可能产生消息不幂等的问题。

为保证消息的正确性发送,超时重试、异常重试、消费完成确认机制等能力都是可以使用,并对业务产生影响的。

我们举个例子,如果你购买一件商品,用户付款完成之后,通过MQ消息的异步通知,告知下游服务出库和通知。如果消息通知出现了问题或者下游消息消费出现了问题,导致无法ACK,都有可能导致重复的出库和通知。

2.2.1 消息生产的幂保证

MQ消息生产部分,就是下图中的步骤1、步骤2、步骤3:

  • 步骤1:消息生产端 MQ-Client Producer 将消息发给服务端MQ-server
  • 步骤2:消息队列服务 MQ-Server 将消息持久化存储
  • 步骤3:息队列服务 MQ-Server 返回确认信息(ACK \ CONSUME_SUCCESS \ offset)给消息生产端 MQ-Client Producer

如果3 消息确认故障导致消息丢失,则消息生产端 MQ-Client Producer 超时后会重发消息,这时候可能就有重复消息,如何保证幂等呢?

因为消息重发也是MQ-Client Producer发起的,消息的处理是消息队列的服务MQ-Server处理的,MQ-Server将数据进行了持久化么,这时候我们可以设计一个唯一的 msgId,作为去重的依据,无论重发多少次,msgId都是一样的,然后在DB数据库中将这个msgId设置为unique key,不允许重复,他有如下特性:

  • 全局唯一,不允许重复
  • MQ生成与业务无耦,对消息的生产和消费也是无强相关。

使用这个 msgId,可以保证只有1条消息落地到数据库中,就保证了消息生产端的幂等。

2.2.2 消息消费的幂保证

MQ消息消费部分,就是下图中的步骤4、步骤5、步骤6:

  • 步骤4:消息队列服务 MQ-Server 将消息发给给消费端 MQ-Client Consumer
  • 步骤5:消费端 MQ-Client Consumer 返回确认信息 (ACK \ CONSUME_SUCCESS \ offset) 给 消息队列服务
  • 步骤6:消息队列服务 MQ-Server 将持久化的消息数据删除,根据msgId精确删除

★ 说明:以上步骤须做一致性保障

这边重灾区就是步骤5,如果因为故障导致消息丢失,消息队列服务 MQ-Server 在超时后会重发消息,这样 MQ-Client Producer/Consumer 就会重复收到消息。

因为消息重发是 消息队列服务 MQ-Server 发起的,MQ-Client Consumer 负责消息消费,消息重发必然会导致业务重复消费(比如重复发消息、重复出库)。所以一样的道理,必然使用msgId来做判断,如果存在库中就进行消费,然后精确删除库中的数据。如果数据库中不存在,就忽略,避免重复消费。

同样的,这个msgID的特性如下:

  • 全局唯一,不允许重复
  • MQ生成与业务无耦,对消息的生产和消费也是无强相关。
  • 业务消息消费方 MQ-Client Consumer 负责判重,保证幂等性

这种方式最常见应用在:商品下单、消费支付、帖子点赞和留言等。

2.3 总结说明

无论是何种消息队列,造成重复消费原因其实都是类似的。正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。

只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念,每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。

那造成重复消费的原因? 就是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

如何解决?这个问题针对业务场景来答分以下几点

(1)给这个消息做一个唯一主键,做数据库insert,如果出现重复消费情况,会导致主键冲突,避免数据库出现脏数据。

(2)update 和 delete 支持天然幂等性,拿到这个消息做redis的set的操作,那就容易了,不用解决,set操作天然幂等操作。

(3)第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

MQ系列10:如何保证消息幂等性消费的更多相关文章

  1. kafka 保证消息被消费和消息只消费一次

    1. 保证消息被消费 即使消息发送到了消息队列,消息也不会万无一失,还是会面临丢失的风险. 我们以 Kafka 为例,消息在Kafka 中是存储在本地磁盘上的, 为了减少消息存储对磁盘的随机 I/O, ...

  2. RabbitMQ消息丢失问题和保证消息可靠性-消费端不丢消息和HA(二)

    继续上篇文章解决RabbitMQ消息丢失问题和保证消息可靠性(一) 未完成部分,我们聊聊MQ Server端的高可用和消费端如何保证消息不丢的问题? 回归上篇的内容,我们知道消息从生产端到服务端,为了 ...

  3. rabbitmq系列(三)消息幂等性处理

    一.springboot整合rabbitmq 我们需要新建两个工程,一个作为生产者,另一个作为消费者.在pom.xml中添加amqp依赖: <dependency> <groupId ...

  4. MQ系列5:RocketMQ消息的发送模式

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 在之前的篇章中,我们学习了RocketMQ的原理, ...

  5. MQ系列6:消息的消费

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 在之前 ...

  6. 消费端如何保证消息队列MQ的有序消费

    消息无序产生的原因 消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer) ...

  7. 【消息队列】kafka是如何保证消息不被重复消费的

    一.kafka自带的消费机制 kafka有个offset的概念,当每个消息被写进去后,都有一个offset,代表他的序号,然后consumer消费该数据之后,隔一段时间,会把自己消费过的消息的offs ...

  8. 《RabbitMQ》如何保证消息不被重复消费

    一 重复消息 为什么会出现消息重复?消息重复的原因有两个:1.生产时消息重复,2.消费时消息重复. 1.1 生产时消息重复 由于生产者发送消息给MQ,在MQ确认的时候出现了网络波动,生产者没有收到确认 ...

  9. Pulsar の 保证消息的顺序性、幂等性和可靠性

    原文链接:Pulsar の 保证消息的顺序性.幂等性和可靠性 一.背景 前面两篇文章,已经介绍了关于Pulsar消费者的详细使用和自研的Pulsar组件. 接下来,将简单分析如何保证消息的顺序性.幂等 ...

  10. 关于MQ的几件小事(五)如何保证消息按顺序执行

    1.为什么要保证顺序 消息队列中的若干消息如果是对同一个数据进行操作,这些操作具有前后的关系,必须要按前后的顺序执行,否则就会造成数据异常.举例: 比如通过mysql binlog进行两个数据库的数据 ...

随机推荐

  1. vue+elementUi实现将数字转化为 对应的字符串内容

    文章目录 1.实现的效果 2.template 3.方法中的数据 4.实际运用 1.实现的效果 数据库状态字段 vue前端效果 2.template prop是你的数据库的字段名称 <el-ta ...

  2. 测试开发HTTP请求过程(一)

    测试开发HTTP请求过程 HTTP请求过程: 首先要熟悉http请求过程: 1,服务端建立socket监听 2,客户端发送http请求 3,客户端与服务端建立socket连接 4,客户端------t ...

  3. day50-正则表达式01

    正则表达式01 5.1正则表达式的作用 正则表达式的便利 在一篇文章中,想要提取相应的字符,比如提取文章中的所有英文单词,提取文章中的所有数字等. 传统方法是:使用遍历的方式,对文本中的每一个字符进行 ...

  4. 论文解读(GLA)《Label-invariant Augmentation for Semi-Supervised Graph Classification》

    论文信息 论文标题:Label-invariant Augmentation for Semi-Supervised Graph Classification论文作者:Han Yue, Chunhui ...

  5. 10.APIView视图

    from rest_framework import status from rest_framework.response import Response from snippets.models ...

  6. maven的下载、安装、配置,idea中配置Maven

    下载 下载链接: 点击下载地址 : 找到:对应版本的包下载 安装 下载后的压缩包解压出来,然后将解压后的包放到日常安装软件的位置即安装成功,当然取决于个人意愿,也可以不移动. 打开安装包后的目录结构简 ...

  7. JS学习笔记 (五) 函数进阶

    1.函数基础 1.1 函数的基本概念 函数是一段JavaScript代码,只被定义一次,但是可以被调用或者执行许多次.函数是一种对象,可以设置属性,或调用方法. 函数中的参数分为实参和形参.其中,形参 ...

  8. pod(八):pod的调度——将 Pod 指派给节点

    目录 一.系统环境 二.前言 三.pod的调度 3.1 pod的调度概述 3.2 pod自动调度 3.2.1 创建3个主机端口为80的pod 3.3 使用nodeName 字段指定pod运行在哪个节点 ...

  9. java反序列化_link_six

    cc_link_six 0x01前言 经过cc链一的学习,然后jdk的版本一更新那两条链子就不能用了,然后这种反序列化的话就很不不止依赖于cc包的引入还有jdk版本,于是就出现了cc_link_six ...

  10. MSP430中断小实验——通过按键改变小灯闪烁频率

    本小实验基于MSP430f5529,不同的型号可能管脚和中断配置有所不同. 实现的功能为: 第一次按下按键后,系统以每 2 秒钟,指示灯暗 1 秒,亮 1 秒的方式闪烁.程序采用默认时钟配置: 第二次 ...