mysql 重复下单_你的项目是如何处理重复请求/并发请求的?

2023-11-16

你的项目是如何处理重复请求/并发请求的?

对于一些用户请求,在某些情况下是可能重复发送的,如果是查询类操作并无大碍,但其中有些是涉及写入操作的,一旦重复了,可能会导致很严重的后果,例如交易的接口如果重复请求可能会重复下单。

重复的场景有可能是:

***拦截了请求,重放

前端/客户端因为某些原因请求重复发送了,或者用户在很短的时间内重复点击了。

网关重发

….

本文讨论的是如何在服务端优雅地统一处理这种情况,如何禁止用户重复点击等客户端操作不在本文的讨论范畴。

利用唯一请求编号去重

你可能会想到的是,只要请求有唯一的请求编号,那么就能借用Redis做这个去重——只要这个唯一请求编号在redis存在,证明处理过,那么就认为是重复的

代码大概如下:```

String KEY = "REQ12343456788";//请求唯一编号

long expireTime =  1000;// 1000毫秒过期,1000ms内的重复请求会认为重复

long expireAt = System.currentTimeMillis() + expireTime;

String val = "expireAt@" + expireAt;//redis key还存在的话要就认为请求是重复的

Boolean firstSet = stringRedisTemplate.execute((RedisCallback) connection -> connection.set(KEY.getBytes(), val.getBytes(), Expiration.milliseconds(expireTime), RedisStringCommands.SetOption.SET_IF_ABSENT));

final boolean isConsiderDup;

if (firstSet != null && firstSet) {// 第一次访问

isConsiderDup = false;

} else {// redis值已存在,认为是重复了

isConsiderDup = true;

}业务参数去重

上面的方案能解决具备唯一请求编号的场景,例如每次写请求之前都是服务端返回一个唯一编号给客户端,客户端带着这个请求号做请求,服务端即可完成去重拦截。

但是,很多的场景下,请求并不会带这样的唯一编号!那么我们能否针对请求的参数作为一个请求的标识呢?

先考虑简单的场景,假设请求参数只有一个字段reqParam,我们可以利用以下标识去判断这个请求是否重复。用户ID:接口名:请求参数

String KEY = "dedup:U="+userId + "M=" + method + "P=" + reqParam;

那么当同一个用户访问同一个接口,带着同样的reqParam过来,我们就能定位到他是重复的了。

但是问题是,我们的接口通常不是这么简单,以目前的主流,我们的参数通常是一个JSON。那么针对这种场景,我们怎么去重呢?

计算请求参数的摘要作为参数标识

假设我们把请求参数(JSON)按KEY做升序排序,排序后拼成一个字符串,作为KEY值呢?但这可能非常的长,所以我们可以考虑对这个字符串求一个MD5作为参数的摘要,以这个摘要去取代reqParam的位置。

`String KEY = "dedup:U="+userId + "M=" + method + "P=" + reqParamMD5;`

这样,请求的唯一标识就打上了!

注:MD5理论上可能会重复,但是去重通常是短时间窗口内的去重(例如一秒),一个短时间内同一个用户同样的接口能拼出不同的参数导致一样的MD5几乎是不可能的。

继续优化,考虑剔除部分时间因子

上面的问题其实已经是一个很不错的解决方案了,但是实际投入使用的时候可能发现有些问题:某些请求用户短时间内重复的点击了(例如1000毫秒发送了三次请求),但绕过了上面的去重判断(不同的KEY值)。

原因是这些请求参数的字段里面,是带时间字段的,这个字段标记用户请求的时间,服务端可以借此丢弃掉一些老的请求(例如5秒前)。如下面的例子,请求的其他参数是一样的,除了请求时间相差了一秒:

//两个请求一样,但是请求时间差一秒

String req = "{\n" +

""requestTime" :"20190101120001",\n" +

""requestValue" :"1000",\n" +

""requestKey" :"key"\n" +

"}";String req2 = "{\n" +

"\"requestTime\" :\"20190101120002\",\n" +

"\"requestValue\" :\"1000\",\n" +

"\"requestKey\" :\"key\"\n" +

"}";这种请求,我们也很可能需要挡住后面的重复请求。所以求业务参数摘要之前,需要剔除这类时间字段。还有类似的字段可能是GPS的经纬度字段(重复请求间可能有极小的差别)。

请求去重工具类,Java实现

public class ReqDedupHelper {/**

*

* @param reqJSON 请求的参数,这里通常是JSON

* @param excludeKeys 请求参数里面要去除哪些字段再求摘要

* @return 去除参数的MD5摘要

*/

public String dedupParamMD5(final String reqJSON, String... excludeKeys) {

String decreptParam = reqJSON;

TreeMap paramTreeMap = JSON.parseObject(decreptParam, TreeMap.class);

if (excludeKeys!=null) {

List dedupExcludeKeys = Arrays.asList(excludeKeys);

if (!dedupExcludeKeys.isEmpty()) {

for (String dedupExcludeKey : dedupExcludeKeys) {

paramTreeMap.remove(dedupExcludeKey);

}

}

}

String paramTreeMapJSON = JSON.toJSONString(paramTreeMap);

String md5deDupParam = jdkMD5(paramTreeMapJSON);

log.debug("md5deDupParam = {}, excludeKeys = {} {}", md5deDupParam, Arrays.deepToString(excludeKeys), paramTreeMapJSON);

return md5deDupParam;

}

private static String jdkMD5(String src) {

String res = null;

try {

MessageDigest messageDigest = MessageDigest.getInstance("MD5");

byte[] mdBytes = messageDigest.digest(src.getBytes());

res = DatatypeConverter.printHexBinary(mdBytes);

} catch (Exception e) {

log.error("",e);

}

return res;

}

}下面是一些测试日志:

public static void main(String[] args) {

//两个请求一样,但是请求时间差一秒

String req = "{\n" +

""requestTime" :"20190101120001",\n" +

""requestValue" :"1000",\n" +

""requestKey" :"key"\n" +

"}";String req2 = "{\n" +

"\"requestTime\" :\"20190101120002\",\n" +

"\"requestValue\" :\"1000\",\n" +

"\"requestKey\" :\"key\"\n" +

"}";

//全参数比对,所以两个参数MD5不同

String dedupMD5 = new ReqDedupHelper().dedupParamMD5(req);

String dedupMD52 = new ReqDedupHelper().dedupParamMD5(req2);

System.out.println("req1MD5 = "+ dedupMD5+" , req2MD5="+dedupMD52);

//去除时间参数比对,MD5相同

String dedupMD53 = new ReqDedupHelper().dedupParamMD5(req,"requestTime");

String dedupMD54 = new ReqDedupHelper().dedupParamMD5(req2,"requestTime");

System.out.println("req1MD5 = "+ dedupMD53+" , req2MD5="+dedupMD54);

}日志输出:

req1MD5 = 9E054D36439EBDD0604C5E65EB5C8267 , req2MD5=A2D20BAC78551C4CA09BEF97FE468A3F

req1MD5 = C2A36FED15128E9E878583CAAAFEFDE9 , req2MD5=C2A36FED15128E9E878583CAAAFEFDE9日志说明:

一开始两个参数由于requestTime是不同的,所以求去重参数摘要的时候可以发现两个值是不一样的

第二次调用的时候,去除了requestTime再求摘要(第二个参数中传入了”requestTime”),则发现两个摘要是一样的,符合预期。

总结

至此,我们可以得到完整的去重解决方案,如下:

String userId= "12345678";//用户

String method = "pay";//接口名

String dedupMD5 = new ReqDedupHelper().dedupParamMD5(req,"requestTime");//计算请求参数摘要,其中剔除里面请求时间的干扰

String KEY = "dedup:U=" + userId + "M=" + method + "P=" + dedupMD5;

long expireTime =  1000;// 1000毫秒过期,1000ms内的重复请求会认为重复

long expireAt = System.currentTimeMillis() + expireTime;

String val = "expireAt@" + expireAt;

// NOTE:直接SETNX不支持带过期时间,所以设置+过期不是原子操作,极端情况下可能设置了就不过期了,后面相同请求可能会误以为需要去重,所以这里使用底层API,保证SETNX+过期时间是原子操作

Boolean firstSet = stringRedisTemplate.execute((RedisCallback

final boolean isConsiderDup;

if (firstSet != null && firstSet) {

isConsiderDup = false;

} else {

isConsiderDup = true;

}http://jaskey.github.io/blog/2020/05/19/handle-duplicate-request/

总结了一些2020年的面试题,这份面试题的包含的模块分为19个模块,分别是: Java基础、容器、多线程、反射、对象拷贝、JavaWeb异常、网络、设计模式、Spring/SpringMVC、SpringBoot/SpringCloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM。

获取以下资料,关注公众号:【有故事的程序员】。

记得点个关注+评论哦~

![](https://img2020.cnblogs.com/blog/2210848/202101/2210848-20210126135033259-1024161091.jpg)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mysql 重复下单_你的项目是如何处理重复请求/并发请求的? 的相关文章

  • 虚拟机配置时间同步-ntp

    安装ntp yum y install ntp 验证是否安装成功 ntpd version 依次执行以下命令即可 ntpdate u ntp sjtu edu cn cp usr share zoneinfo Asia Shanghai e
  • 7 款炫酷的 VSCode 主题扩展

    关注后回复 进群 拉你进程序员交流群 作者丨小集 来源丨小集 ID zsxjtip 在 VSCode 中 安装自定义主题和图标包可以彻底改变 VSCode 的外观 VSCode 有数千种不同的包可用 在这里 我们推荐几个不错的主题扩展 Gi
  • 这是啥SQL,室友看了人傻了

    文章目录 SQLite适应常规基本应用场景 SQLite面对复杂场景尚有不足 SPL全面支持各种数据源 SPL的计算能力更强大 优化体系结构 SPL资料 可以在Java应用中嵌入的数据引擎看起来比较丰富 但其实并不容易选择 Redis计算能
  • 【数据结构】五种用于查询的数据结构 性能测试

    github项目地址 1 总体说明 本报告一共实现了五种用于查询的数据结构 二叉搜索树 二叉平衡树 二叉伸展树 跳表 数组 在完成各种数据的增删查功能的基础上 对于不同数据结构的查询效率进行了评测与对比 对空间性能进行了理论的分析 大致实验
  • AngularJS UI Router(ui.router)嵌套视图(Nested Views)

    1 dom结构 index html
  • UE_移动端测试使用

    教程流程 参照官方文档 android篇 https docs unrealengine com 5 1 zh CN android development requirements for unreal engine https docs
  • 电巢携手陕西理工大学“硬件研发岗位岗前项目实训”顺利开班!

    为深化校企合作 产教融合助力新工科建设 提升学生工程实践能力 电巢工程能力实训班按照不同岗位类别 匹配对应的企业岗位任职能力要求对学生开展分级培养 以产业需求为导向 培养创新型 应用型人才 7月27日下午3时 深圳电巢联合陕西理工大学物理与
  • dc-3 靶机渗透学习

    靶机修复 dc 3靶机可能会存在扫不到靶机ip的问题 可以参考下面这篇博客解决 编辑网卡配置文件时命令有点错误 vim etc network interfacers 改成 vim etc network interfaces Vulnhu
  • 【SpringCloudAlibaba】Nacos服务注册和配置中心配合nginx负载

    文章目录 概述 注册中心 POM YML 启动类 CAP 配置中心 POM YML 启动类 ConfigClientController Nacos中的匹配规则 三种方案加载配置 示例 集群部署 概述 部署模式 修改derby为mysql
  • Fiddler抓包工具配置+Jmeter基本使用

    目录 一 Fiddler抓包工具的配置和使用 局域网络配置 Fiddler配置 Fiddler抓包实例 二 Jmeter的基本使用 Jmeter的安装配置 第一个Jmeter脚本 一 Fiddler抓包工具的配置和使用 在编写网关自动化脚本
  • GCC入门详解

    一 基本概念 gcc编译源程序分为4个阶段 预处理 编译 汇编 链接 1 预处理阶段 将头文件的内容插入到源代码中 替换宏定义 去掉注释等 预处理后的文件后缀名为 i 2 编译阶段 编译器将预处理后的文件翻译成汇编代码文件 后缀名为 s 3
  • uni-app 连接逍遥模拟器 安卓模拟器 不显示 找不到 端口映射

    最近公司为了让我全面发展 给了一个小活练练手 由于Android和ios开发的小伙伴比较忙 我被拉来开发一个App 由于需要多端使用 最后选择使用uni app 来开发 刚开始都是在h5页面来调试 最后测试App的时候需要使用安卓模拟器来调
  • GameMode问题

    GameMode问题 1 缘由 初始化了两个关卡 一个登录关卡 一个内容关卡 配置了两个GameMode 分别在关卡中设置好了GameMode 通过调用OpenLevel实现关卡跳转 如下图 然 运行过程中 关卡完成了跳转 进入内容关卡后G
  • MusicGen一键音乐风格迁移

    想象一下 您可以随心所欲地创作轻快的乡村曲风 缠绵的蓝调 史诗般的管弦乐 视频BGM创作之路上 再也不会有任何阻碍 01 什么是MusicGen Meta MusicGen建立在强大的Transformer模型的基础上 追随ChatGPT等
  • Pcshare远控源码偏重分析(一)

    0x00背景 PcShare是一款功能强大的远程管理软件 可以在内网 外网任意位置随意管理需要的远程主机 该软件是由国内安全爱好者无可非议开发 在当时这款远控在大家应该比较熟悉了 VC编译器调出来的的小体积全功能木马 相比Delphi的灰鸽
  • Linux权限

    一 Linux权限的概念 Linux下有两种用户 超级用户 root 普通用户 超级用户 可以再linux系统下做任何事情 不受限制 普通用户 在linux下做有限的事情 超级用户的命令提示符是 普通用户的命令提示符是 1 1 用户间的切换
  • 最优清零方案python(蓝桥杯)

    1 问题描述 给定一个长度为 N 的数列 A1 A2 AN 现在小蓝想通过若干次操作将 这个数列中每个数字清零 每次操作小蓝可以选择以下两种之一 选择一个大于 0 的整数 将它减去 1 选择连续 K 个大于 0 的整数 将它们各减去 1 小
  • 【SSM】DispatcherServlet详解

    功能 SpringMVC的核心就是DispatcherServlet DispatcherServlet实质也是一个HttpServlet DispatcherSevlet负责将请求分发 所有的请求都有经过它来统一分发 大致看下Spring
  • Maven引入本地jar包的使用方法

    关于下载配置Maven的过程我这里就不多说了 网上可以自行查询 我简述一下关于jar如果直接下载到本地之后 怎么整合到自己的maven工程呢 方式挺多的 下面列举四种方式 1 上传到maven中心仓库 https oss sonatype

随机推荐

  • 显示web服务器登陆,web服务器登陆界面

    web服务器登陆界面 内容精选 换一换 云解析服务支持为域名快速添加网站解析 通过该功能可以简化解析记录的配置 包含如下两种场景 网站解析至IP地址 为域名的主域名和子域名分别添加一条A类型记录集网站解析至另一域名 为域名的主域名和子域名分
  • tensorRT 分类模型构建与推理

    tensorRT分类模型构建与推理示例代码classifier cpp tensorRT include 编译用的头文件 include
  • 第三方钩子 MouseKeyHook 监控鼠标键盘事件

    public partial class Form4 Form private static IKeyboardMouseEvents m GlobalHook public Form4 InitializeComponent privat
  • 大数据实训报告_重磅|数据酷客?大数据精准营销综合实训软件平台隆重发布...

    北京大数据研究院博雅大数据学院第二套大数据专业综合实训产品 数据酷客 大数据精准营销综合实训软件平台隆重发布 大数据精准营销综合实训软件平台 平台介绍 首先 通过对海量结构化数据和非结构化文本数据的深度分析和挖掘 构建全方位的客户标签体系
  • MATLAB打开后一直在初始化,或者初始化很慢问题

    问题描述 遇到MATLAB启动较慢 进入主界面后 一直停留在 正在初始化 的状态 浪费很多时间 这种问题大多是因为MATLAB软件在启动时寻找本机的许可证或者是设置了 LM LICENSE FILE 的环境变量 这个变量告诉 MATLAB
  • 软件工程毕业设计选题java_软件工程毕业设计选题

    2020 01 24 东哥毕设 1122 1 分管理员和用户 国资处 三个角色 国资处这个角色一定要有 管理员 1 对用户进行增删改查 2 对设备信息管理 基本信息里面需要包括设备存放地址这个字段 这里必须有 一个状态字段 管理员添加了设备
  • 价值创造链路及经营计划

    价值创造过程最主要的环节是建立链接 北京万柳书院在网上热议 其背后是人与人的大量链接 近期热议的湖南卫视春晚亦如是 这种链接为价值的设计 沟通 传递创造条件 企业以客户为中心设计产品 往大了说是企业的生存根本 往小了说则是经营技巧 产品就是
  • TCP协议(三次握手)

    TCP Transmission Control Protocol 协议的全称是传输控制协议 它负责为不同终端系统的应用进程之间提供面向连接的通信服务 即TCP协议能够对自己提供的连接实施控制 它是一种可靠的传输层协议 一 TCP协议简介
  • 西门子PPI通讯协议

    过硬件和软件侦听的方法 分析PLC内部固有的PPI通讯协议 然后上位机采用VB编程 遵循PPI通讯协议 读写PLC数据 实现人机操作任务 这种通讯方法 与一般的自由通讯协议相比 省略了PLC的通讯程序编写 只需编写上位机的通讯程序资源S7
  • 磁环相关的计算公式

    磁环相关的计算公式 公式来源 收集于各大网站 公式仅供参考 如有错误或不全的 欢迎留言指出 通过查磁环手册或咨询供应商可知的固有量 磁环外径 D 单位mm 磁环内径 d 单位mm 磁环高度 h 单位mm 磁环芯材磁导率 u 可推导的量 导磁
  • Java 获取两个List的交集和差集,以及应用场景

    背景介绍 在实际项目中 特别是一些管理后台类的项目 会遇到底层数据是按照一对多关系的数据表存储的管理界面 列表页是一对多关系中一对应的数据列表 二级的详情页中是一对多关系中多对应的多条数据展示 通常二级页面是能够增 删 改数据的编辑页面 在
  • 使用 pymysql 操作MySQL数据库

    安装PyMySQL PyMySQL是一个Python编写的MySQL驱动程序 让我们可以用Python语言操作MySQL数据库 首先 使用pip安装PyMySQL pip install PyMySQL 使用PyMySQL 简单使用 如果有
  • Redis学习笔记①基础篇_Redis快速入门

    若文章内容或图片失效 请留言反馈 部分素材来自网络 若不小心影响到您的利益 请联系博主删除 资料链接 https pan baidu com s 1189u6u4icQYHg 9 7ovWmA 提取码 eh11 在线视频 https www
  • Android Jetpack Compose之状态持久化与恢复

    目录 1 概述 2 实例解析 4 Compose提供的MapSaver和ListSaver 4 1 mapServer 4 2 ListSaver 1 概述 在之前的文章中 我们提到了remember 我们都知道remember可以缓存创建
  • 华为云技术开放日(第三季)活动报道

    智能共生 链接未来 华为云技术开放日 第三季 精彩回顾 7 月 29日 8 月 12 日 由华为云与中生代技术社区联合主办的华为云技术开放日 第三季 圆满落下帷幕 本次技术开放日邀请了华为云 MVP 李弋凡 华为公司5G产品线首席架构师廖尔
  • 虚函数与虚函数表

    虚函数与虚函数表 一 概述 为了实现C 的多态 C 使用了一种动态绑定的技术 这个技术的核心是虚函数表 下文简称虚表 本文介绍虚函数表是如何实现动态绑定的 二 类的虚表 每个包含了虚函数的类都包含一个虚表 我们知道 当一个类 A 继承另一个
  • LeetCode 189. 轮转数组

    前言 Wassup guys 我是Edison 今天是 LeetCode 上的 leetcode 189 轮转数组 Let s get it 文章目录 1 题目分析 2 题目图解 思路一 右旋 k 次 依次移动一个 思路二 额外开数组 思路
  • 【学习笔记之操作系统原理篇】进程管理

    进程的概念 进程 单道程序 特点 顺序性 封闭性 独占性 可再现性 多道程序系统 特点 间断性 相互制约竞争资源 失去封闭性 共性资源 多个程序操作相同资源 不可再现性 运行结果不唯一 因为执行顺序不唯一 进程特征 动态性 一个进程可以对应
  • 写博客有哪些好用的工具和软件?

    写博客有哪些好用的工具和软件 在各种平台上写文章 要有一个好用的编辑器 使用简单方便 不用花很多时间排版 能预览效果等 此外 文章还需要配图片 动图和视频等 所以需要好用的截图软件 视频录制和剪辑软件 本文介绍我自己使用的软件和工具 本人认
  • mysql 重复下单_你的项目是如何处理重复请求/并发请求的?

    你的项目是如何处理重复请求 并发请求的 对于一些用户请求 在某些情况下是可能重复发送的 如果是查询类操作并无大碍 但其中有些是涉及写入操作的 一旦重复了 可能会导致很严重的后果 例如交易的接口如果重复请求可能会重复下单 重复的场景有可能是