如果对方没做幂等!记一次生产订单重复的反思

最近公司公司的旧系统中发现了一个bug。业务部门反馈,尽管用户只支付了一年的服务费用,系统却将有效期增加了两年。

原因分析:

到底是什么原因呢?
经过日志分析,发现消息队列(MQ)向第三方服务发送了两次消息。由于第二方服务的接口没有实现幂等性控制,导致了这一重大的bug。

问题反思:

想一想,其实这种问题很简单,怎么会出这种问题呢?

一般来说系统开发中不免会出现不少类似的问题,类似问题的出现并不罕见。一般系统都是从无到有,业务从少到多、早期可能也就几个或者一个研发人员开发出来的,后面升级或重构甚至推倒重来。。。

问题严重性:


这类bug对于系统和业务的影响极大,尤其是涉及金钱的业务,可能会导致严重的财务损失和信誉问题。

在系统开发过程中,正常的服务会确保服务的幂等性,尤其是在涉及金钱交易的业务中。

下面我们就来复盘一下重复消息的产生原因及相应的解决方案:

一、消息重复原因

在消息队列(MQ)系统中,消息重复的情况主要有以下几种原因:

1、生产者重复或重试

生产者代码没有阻止重复请求或处理发送消息后的响应情况,在连接超时等异常情况下,重复发送了相同的消息。当然还有一种情况就是生产者本身设置了重试机制,但重试机制不完善造成重复发送消息。

2、消费者代码问题

消费者在处理消息过程中出现异常,没有正确地手动确认消息,那么该消息会重新投递,导致重复。

3、网络问题

网络延迟或临时断开连接可能导致MQ消费者没有收到确认消息,从而重新消费同一条消息。

4、消息队列集群问题

MQ集群节点之间的状态同步问题,可能导致消息被多个节点重复投递。

当然还有别的一些原因.....

二、消息重复解决方案

针对消息重复的问题,可以从以下几个方面采取解决措施:

1、生产者端解决方案

消息的生产者端的消息溯源还是用户的请求。

第一道防线

首先,前端可以通过禁用按钮、显示加载状态等方式防止用户重复点击提交按钮。禁用按钮是指在用户点击提交按钮后,立即将该按钮禁用,防止再次点击。显示加载状态则是在提交请求后显示加载动画或状态提示,告知用户请求正在处理中。高级一点的做法是使用JavaScript脚本控制按钮的状态,确保用户无法重复提交。不过,前端防重只是第一道防线,因为前端措施容易被绕过,例如通过浏览器开发者工具修改页面元素或捕获和重发请求报文。

第二道防线

如果前端防重措施被绕过,用户可以直接通过程序生成大量请求,此时服务后台需要采取进一步的防重措施。后台可以通过请求频率限制、幂等键、时间戳、哈希值等方式来防止重复请求。请求频率限制是指限制每个用户在一定时间内的请求次数,防止短时间内大量重复请求。幂等键则为每个请求生成唯一的标识符,并在后台存储和检查这些标识符,确保每个请求只处理一次。时间戳和哈希值也是有效的防重手段,通过附加时间戳验证请求的时效性,或对请求内容生成哈希值并检查其唯一性,确保相同内容的请求只处理一次。

第三道防线

在消息的生产者端,也需要采取类似的防重措施以确保消息不被重复发送。例如,在发送消息前生成唯一的消息ID(例如UUID),并将其包含在消息体中。发送消息后,同步等待服务器的回执确认,确保消息只发送一次。此外,可以将消息ID存储在数据库或缓存中,并在发送前检查该ID是否已存在,防止重复发送。当然生产者端一般不会利用这个ID或者叫幂等键来去重,一般会结合消息者端一起来实现。

通过这些多层次的防重措施,能够有效减少消息重复的发生,保障系统的稳定性和可靠性。

2、消费者端解决方案

消息到达消费者端后

第一道防线

消费者端可以使用幂等键来判断请求是否已经处理过。通常情况下,缓存不宜存放过多数据,而重复请求大多数集中在一定时间范围内,因此将幂等键存储在缓存中是一种有效的解决方案。

具体来说,幂等键可以与请求的唯一标识关联,并存储在缓存系统(如Redis或Memcached)中。为每个幂等键设置一个合理的过期时间,可以有效地过滤掉在这个时间范围内的重复请求。通过这种方法,大部分重复请求可以在缓存层被拦截,从而减少对后端服务的压力。

第二道防线

尽管缓存机制可以一定程度上确保幂等性,但是当缓存过期后,可能会再次收到相同的请求。为了更加彻底地解决这个问题,我们需要在数据库层面进一步加强幂等性保证。

当收到请求到达数据库层时,首先检查该请求的幂等键是否已经存在于数据库中。如果存在,则说明该请求已经被处理过,直接返回之前的结果即可;如果不存在,则继续执行请求的业务逻辑。处理完成后,将请求的幂等键及结果持久化到数据库中。当然大多数人还是会选择更简单点的直接把幂等键设置为唯一索引,当报键值重复异常时就忽略些请求直接返回。

3、消息队列层解决方案

  • 利用消息队列中间件的去重功能,如设置成手动ACK、去重插件等。
  • 设置消息的有效时间(TTL),防止过期消息重复投放。
  • 配置死信队列(DLX),存储处理失败的消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/761551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

想用AI高端算力训练模型?试试英智BayStone平台

随着生成式人工智能的迅猛增长,各大公司纷纷推出强大的 AI产品以提升自身核心竞争力,对于依赖基础模型进行推理训练,同时需要高级基础设施的人工智能初创企业,急需使用高端智算算力来加速模型训练与产品研发创新。 算力是否充足&…

HiBit Uninstaller:软件批量卸载,一触即得

名人说:莫道谗言如浪深,莫言迁客似沙沉。 ——刘禹锡《浪淘沙》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、软件介绍1、HiBit Uninstaller2、核心功能 二、下载安装1、下载2、安装 …

【Sklearn-驯化】一文从基础帮你搞懂svm算法做分类和回归的原理以及实践

【Sklearn-驯化】一文从基础帮你搞懂svm算法做分类和回归的原理以及实践 本次修炼方法请往下查看 🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地! 🎇 免费获取相关内容文…

【运维】如何在Ubuntu中设置一个内存守护进程来确保内存不会溢出

文章目录 前言增加守护进程1. 编写监控脚本2. 创建 systemd 服务文件3. 启动并启用服务4. 验证服务是否运行注意事项 如何修改守护进程1. 修改监控脚本2. 重新加载并重启服务3. 验证服务是否运行总结 如何设置一个日志文件来查看信息1. 修改监控脚本以记录日志方法一&#xff1…

antd DatePicker日期选择框限制最多选择一年

实现效果 实现逻辑 import React, { useState } from react;const ParentComponent () > {const [dates, setDates] useState(null);const disabledDate (current) > {if (!dates) {return false;}const tooLate dates[0] && current.diff(dates[0], days) &…

危险!属性拷贝工具的坑!

1. 背景​ 之前在专栏中讲过“不推荐使用属性拷贝工具”,推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。 不推荐的主要理由是: 有些属性拷贝工具性能有点差有些属性拷贝工具有“BUG”使用属性拷贝工具容易存在一些隐患&#xff08…

【你也能从零基础学会网站开发】(了解)关系型数据库的基本架构体系结构与概念理解

🚀 个人主页 极客小俊 ✍🏻 作者简介:程序猿、设计师、技术分享 🐋 希望大家多多支持, 我们一起学习和进步! 🏅 欢迎评论 ❤️点赞💬评论 📂收藏 📂加关注 关系型数据库的…

安装 Docker 环境(通过云平台创建一个实例实现)

目录 1. 删除原有 yum 2. 手动配置 yum 源 3. 删除防火墙规则 4. 保存防火墙配置 5. 修改系统内核。打开内核转发功能。 6. 安装 Docker 7. 设置本地镜像仓库 8.重启服务 1. 删除原有 yum rm -rfv /etc/yum.repos.d/* 2. 手动配置 yum 源 使用 centos7-1511.iso 和 Xi…

Bunny的PT+SFT训练

GitHub - BAAI-DCAI/Bunny: A family of lightweight multimodal models.A family of lightweight multimodal models. . Contribute to BAAI-DCAI/Bunny development by creating an account on GitHub.https://github.com/BAAI-DCAI/Bunny1.环境安装 conda create -n bunny …

HEC-HMS水文模型教程

原文链接:HEC-HMS水文模型教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247607904&idx5&sn1a210328a3fc8f941b433674d8fe2c85&chksmfa826787cdf5ee91d01b6981ebd89deac3e350d747d0fec45ce2ef75d7cb8009341c6f55114d&token90645021…

桥感应加热主电路拓扑结构及控制原

1 桥感应加热主电路拓扑结构及控制原理 1.1 主电路拓扑 本文所述中频感应加热电源采用交—直—交的变频原理,三相50Hz的正弦交流输入电压经过整流滤波为540V平滑直流电压,再经逆变器将直流电压变成不同频率的交流电压供负载使用。本文采用半桥串联谐振…

PDF 生成(5)— 内容页支持由多页面组成

当学习成为了习惯,知识也就变成了常识。 感谢各位的 关注、点赞、收藏和评论。 新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn 文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。 回顾 在本篇开始…

【echarts】如何关闭dataZoom-silder 组件中数据阴影(缩略图、数据走势图)

echarts开启 “滑动条型数据区域缩放组件(dataZoomInside)”后,默认会显示数据的走势图。 但有时候我们并不需要。 如何关闭呢? 官方有提供一个属性:showDataShadow https://echarts.apache.org/zh/option.html#da…

C++初学者指南-2.输入和输出---从输入流错误中恢复

C初学者指南-2.输入和输出—从输入流错误中恢复 文章目录 C初学者指南-2.输入和输出---从输入流错误中恢复怎么了&#xff1f;解决方案&#xff1a;出错后重置输入流 怎么了&#xff1f; 示例&#xff1a;连续输入 int main () {cout << "i? ";int i 0;cin…

vue项目创建+eslint+Prettier+git提交规范(commitizen+hooks+husk)

# 步骤 1、使用 vue-cli 创建项目 这一小节我们需要创建一个 vue3 的项目&#xff0c;而创建项目的方式依然是通过 vue-cli 进行创建。 不过这里有一点大家需要注意&#xff0c;因为我们需要使用最新的模板&#xff0c;所以请保证你的 vue-cli 的版本在 4.5.13 以上&#xff…

基于X86+FPGA+AI的芯片缺陷检测方案

应用场景 随着半导体技术的发展&#xff0c;对芯片的良率要求越来越高。然而集成电路芯片制造工艺复杂&#xff0c;其制造过程中往往产生很多缺陷&#xff0c;因此缺陷检测是集成电路制造过程中的必备工艺。 客户需求 小体积&#xff0c;低功耗 2 x USB,1 x LAN Core-i平台无…

YOLOv8 目标检测程序,依赖的库最少,使用onnxruntime推理

YOLOv8 目标检测程序&#xff0c;依赖的库最少&#xff0c;使用onnxruntime推理 flyfish 为了方便理解&#xff0c;加入了注释 """ YOLOv8 目标检测程序 Author: flyfish Date: Description: 该程序使用ONNX运行时进行YOLOv8模型的目标检测。它对输入图像进行…

AQS同步队列、条件队列源码解析

AQS详解 前言AQS几个重要的内部属性字段内部类 Node同步队列 | 阻塞队列等待队列 | 条件队列 重要方法执行链同步队列的获取、阻塞、唤醒加锁代码流程解锁 条件队列的获取、阻塞、唤醒大体流程 调用await()方法1. 将节点加入到条件队列2. 完全释放独占锁3. 等待进入阻塞队列4. …

【刷题汇总--数字统计、两个数组的交集、点击消除(栈)】

C日常刷题积累 今日刷题汇总 - day0011、数字统计1.1、题目1.2、思路1.3、程序实现 2、两个数组的交集2.1、题目2.2、思路2.3、程序实现 3、点击消除(栈)3.1、题目3.2、思路3.3、程序实现 今日刷题汇总 - day001 1、数字统计 1.1、题目 请统计某个给定范围[L, R]的所有整数中…

reactjs18 中使用@reduxjs/toolkit同步异步数据的使用

react18 中使用reduxjs/toolkit 1.安装依赖包 yarn add reduxjs/toolkit react-redux2.创建 store 根目录下面创建 store 文件夹&#xff0c;然后创建 index.js 文件。 import { configureStore } from "reduxjs/toolkit"; import { counterReducer } from "…