如何处理java接口防重提交

高并发场景下如何保证接口幂等性?常见幂等性处理方案防重令牌(token)、随机字符串(noncestr)、幂等表、防重表、数据库唯一索引 在高并发场景下,保证接口幂等性是非常重要的,以防止重复操作导致的数据不一致等问题。以下分别介绍防重令牌(token)、随机字符串(noncestr,本质和 token 类似)、幂等表、防重表(和幂等表概念相近)、数据库唯一索引这几种保证接口幂等性的方式,并给出相应的代码示例(以 Java 和 MySQL 为例)。防重令牌(token)和随机字符串(noncestr)优点:灵活性高,适用于多种业务场景。可以通过缓存等方式优化查询性能,减少数据库压力。缺点:需要额外的令牌生成和管理逻辑,增加了系统复杂度。 阅读详情

1.什么是接口防重?

在一定的时间内多次请求同一接口,同一参数。由于请求是 健康请求 ,会执行 正常的业务逻辑 ,从而产生大量的废数据。

2.问题的产生及引发的问题

举一个最简单的例子:日常开发中crud在业务系统中普遍存在,在服务端没有做任何处理,客户端没有做 节流、防抖 等限流操作时,同一秒一个用户点了两次新增按钮,导致数据库中存在同样两条数据,其结果可想而知,同理修改、删除同样的道理;查询本身具有 幂等性 ,但是在同一秒钟同样的操作,查询多次和一次,有区别吗?区别大了去了,不谈用户体验如何,光是 网络开销、流量占用、带给服务器的压力 等等,生产中一点小的问题,如何不及时处理,可能会引发灾难性bug。

3.处理方法

第一种:前台在请求接口的时候,传递一个唯一值,然后在对应接口判断该唯一值,在一定的时间内是否被消费过

第二种:采用 Spring AOP 理念,实现请求的切割,在请求执行到某个方法或某层时候,开始拦截进行,获取该请求的参数,用户信息,请求地址,存入redis中并放置过期时间,进行防重(推荐使用)

4.谈谈以下两种处理方法的利弊

第一种:局限性太高,前台必须传递一个唯一值,就算请求到达指定后台服务,写一个拦截器,需要配置太多不需要拦截的方法,也许你会说,可以拦截有规则的请求地址,这样真的好吗?
第二种(推荐):采用 AOP面向切面编程的思想 ,在不污染源代码的情况下,进行增强功能,切入到要防重的接口上,实现统一防重处理、业务解耦。此处采用 AOP + 自定义注解 ,灵活实现防重功能。
5.具体代码(采用第二种)
注解类

import java.lang.annotation.*;

/**
 * 防重
 * @date 2020/8/12
 * @return
 */
//标识该注解用于方法上
@Target({ElementType.METHOD})
//申明该注解为运行时注解,编译后改注解不会被遗弃
@Retention(RetentionPolicy.RUNTIME)
//javadoc工具记录
@Documented
public @interface PreventSubmit 
{
}

切面类

import com.qianxian.common.exception.AppException;
import com.qianxian.common.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交
 * @date 2020/8/12
 * @return
 */
@Component
@Aspect
@Slf4j
public class PreventSubmitAspect {

    /**
     * 放重redis前缀
     */
    private static String API_PREVENT_SUBMIT = "api:preventSubmit:";

    /**
     * 放重分布式锁前缀
     */
    private static String API_LOCK_PREVENT_SUBMIT = "api:preventSubmit:lock:";

    /**
     * 失效时间
     */
    private static Integer INVALID_NUMBER = 3;

    /**
     * redis
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 分布式锁
     */
    @Autowired
    private RedissonClient redissonClient;


    /**
     * 防重
     * @date 2020/8/12
     * @return
     */
    @Around("@annotation(com.qianxian.user.annotation.PreventSubmit)")
    public Object preventSubmitAspect(ProceedingJoinPoint joinPoint) throws Throwable {

        RLock lock = null;

        try {

            //获取目标方法的参数
            Object[] args = joinPoint.getArgs();

            //获取当前request请求
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

            //获取请求地址
            String requestUri = request.getRequestURI();

            //获取用户ID
            Long userId = null;
            try {
                userId = TokenUtil.getUserId(request);
            }catch (Exception e){}

            //拼接锁前缀,采用同一方法,同一用户,同一接口
            String temp = requestUri.concat(Arrays.asList(args).toString()) + (userId != null ? userId : "");
            temp = temp.replaceAll("/","");

            //拼接rediskey
            String lockPrefix = API_LOCK_PREVENT_SUBMIT.concat(temp);
            String redisPrefix = API_PREVENT_SUBMIT.concat(temp);

            /**
             * 对同一方法同一用户同一参数加锁,即使获取不到用户ID,每个用户请求数据也会不一致,不会造成接口堵塞
             */
            lock = this.redissonClient.getLock(lockPrefix);
            lock.lock();

            String flag = this.stringRedisTemplate.opsForValue().get(redisPrefix);
            if(StringUtils.isNotEmpty(flag)){
                throw new AppException("您当前的操作太频繁了,请稍后再试!");
            }

            //存入redis,设置失效时间
            this.stringRedisTemplate.opsForValue()
                                       .set(redisPrefix,redisPrefix,INVALID_NUMBER, TimeUnit.SECONDS);

            //执行目标方法
            Object result = joinPoint.proceed(args);
            return result;

        }finally {
            if(lock != null){
                lock.unlock();
            }
        }

    }

}

以上内容仅供个人理解,如有不对,欢迎指正。

S32K系列之PDB框图详解及PDB触发ADC 可编程延迟模块PDB,PDB可提供从内部或外部触发或可编程间隔信号到ADC硬件触发输入的可控制延迟。 PDB可以选择提供脉冲输出,用作CMP块中的采样窗口。 S32K系列的PDB资源如下表: PDB功能框图 在下图中,以下字母表示的意义: N:PDB通道的总数 n:PDB通道号,范围从0到N-1 M:每个PDB通道的总可用预触发 m:预触发编号,范围从0到M-1 Y:脉冲输出的总数 y:脉冲数... 阅读详情

相关推荐

ST7102驱动移植与调试实战

本文详细介绍了在君正T40平台上移植和调试ST7102 LCD驱动芯片的实战经验。内容涵盖硬件接口配置、时序参数调整、寄存器初始化序列编写及常见问题解决,重点解析了ST7102单芯片集成显示与触控功能的特性,为嵌入式设备显示驱动开发提供实用指南。

k6l7m8n9的博客 1406

Java实战:Spring Boot接口防止重复提交

本文将详细介绍如何在Spring Boot应用程序中防止接口重复提交。我们将探讨重复提交的基本概念,以及如何使用Spring Boot和第三方库来实现接口的防重复提交功能。

oandy0的博客 2291

初识机会网络仿真器ONE中ExternalMovement移动模型(导入外部移动数据)

最近半个月一直在研究ONE中关于ExternalMovement移动模型的源代码,在网上几乎没有找到相关的资料,很是苦恼,如果有共同研究方向的小伙伴欢迎联系我,共同讨论进步。 下面记录一下几点感悟: 1、使用外部数据的前提是在ONE的配置文件中增加及修改相关代码: Group.movementModel = ExternalMovement ExternalMovement.file = move...

qq_36974792的博客 655

java中对接口防重复调用方案(上手即用)

这段代码的主要功能是在使用注解的方法上实现防止重复提交的机制。通过使用缓存来存储正在处理的请求标记,若在有效时间内再次请求相同的操作,则会返回 503 错误,避免重复处理。此代码适用于需要保护幂等性或避免重复操作的场景,如支付、提交表单等。04 在controller对应的业务上添加注解key最好唯一,且修改跟删除较多的地方可以考虑使用05 前端回显因为这里设置了全局异常拦截,前端直接配置就可以获取到接口返回的异常信息。

2403_89110232的博客 2253

在WebView中对第三方H5页面的文本密码框添加自定义随机键盘

前言: 首先介绍一下这个需求的背景,由于公司是涉及到金融行业的需要与银行对接资金存管。出于保密性这里不直接列出公司名字和银行名字。从2018年国家对金融行业大整改以来,为了能够顺利通过备案,我们也跟着政府的脚步一步一步走向合规。 好了,大致就是因为要通过备案,必须把这个需求实现,否则将不会通过。 需求内容就是,当客户端有关资金交易的时候,会通过加密数据以及秘钥的方式把第三方银行的页面(充值、...

比较喜欢、笑的博客专栏 1586

华为硬件逻辑岗笔试题(一)

积少成多,集腋成裘,坚持!!! 目录 1. 进制转换 2. 状态机和编码方式 3. 存储器的分类 4. Verilog语法中的操作符 5. 对组合逻辑的认识 6. 对时序逻辑的认识 7. 竞争冒险的认识 8. 基本时序逻辑电路 9. 建立时间和保持时间 10. 同步时序电路 11. 组合逻辑和时序逻辑判断 13. 基本总线的理解 14.加法器 15.FPGA开发工具 1. 进制转换 1、十进制46.25对应的二进制表达式为( )。 A 101110.11...

qq_42235129的博客 1万+

解决在测试类中运行一次数据库新增方法却添加两条相同数据

为什么选择跳过测试呢,这是因为我们在执行某一方法时,内部先执行一次测试操作,再执行我们的方法,这就导致了数据库内容连续添加两条,跳过测试之后就只会执行一遍操作。解决方案为在设置中找到maven里面的Runner选项,把Skip Tests选项打对勾,这个意思是。我们运行testInsert方法,从下面控制台我们可以看到添加数据库成功。在20:36:54秒时添加了一条,在20:36:57秒时又添加一条。但是查看数据库时,发现一次添加了两行语句。可以看到这次只添加了一条,成功。我们修改后在执行一次。

cz980413的博客 907

Plant Simulation与ABB机器人虚实结合

Plant Simulation与ABB机器人虚实结合

santayana的专栏 1869

实现防止接口重复访问-Java-SpringBoot

在上面的代码中,我们假设从请求头中获取了userId,然后将其与接口路径拼接成Redis中的键,用于存储该用户上次访问该接口的时间。在preHandle方法中,首先从Redis中获取该用户上次访问该接口的时间,如果时间间隔小于5分钟,则返回错误信息;否则,将当前时间存储到Redis中,并放行该请求。在拦截器中可以从Redis中获取该用户上次访问该接口的时间,并计算与当前时间的时间间隔。在上面的代码中,我们假设需要限制访问时间间隔的接口都以“api”为前缀,因此只需要对以“api”开头的请求进行拦截即可。

qq_45822726的博客 2014

[Dify实战] 用 Dify 打造高效的新员工 7 天成长计划-带绑定资源

本文介绍了如何利用Dify平台为新员工制定智能成长计划,解决融入难、节奏乱、成长慢等问题。方案通过输入岗位信息自动生成7天结构化计划,包含每日任务和评估要点,并支持进度跟踪与报告输出。关键点在于设计清晰的Prompt模板,合理分配学习节奏,形成闭环反馈机制。该方案能显著提升新员工培养效率,实现人才培养的数字化升级。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 568

【用unity实现100个游戏之2】复刻实现经典2d平台跳跃游戏《蔚蓝 Celeste》(附工程源码)

蔚蓝》是一款备受好评的独立游戏,由游戏开发工作室开发并由发行。这款游戏于2016年首次发布,并在玩家和评论界收获了广泛赞誉。蔚蓝以其独特的艺术风格和引人入胜的故事而著称。游戏以深海为背景,玩家将扮演一个能够探索水下世界的神秘生物。游戏通过惊人的视觉表现和动态的海洋生态系统为玩家带来了令人难忘的游戏体验。无论是广阔的水下景观还是充满生机的海洋生物,蔚蓝都让玩家感受到深海的神秘和美丽。蔚蓝在玩法上注重探索和解谜。玩家可以在海底环境中自由游动,发现隐藏的地点和秘密,与各种生物互动,并解决环境谜题以继续前进。

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 4166

java防止重复提交的方法

这样,即使用户多次提交表单,由于令牌已经被移除,后续的请求都会被拒绝。在处理完POST请求后,服务器不直接返回响应,而是返回一个重定向(REDIRECT)到GET请求的响应。这样,即使用户刷新页面,也只会重复发起GET请求,而不会重复提交表单。在服务器端可以设置一定的限制,例如限制同一个用户在一定时间内只能提交一次表单。在提交成功后,可以禁用提交按钮,防止用户多次点击。在前端页面中,可以通过禁用提交按钮或者在提交时添加遮罩层来防止用户重复提交。在后端代码中,可以通过生成唯一的token来防止重复提交。

花生日记 7604

Dify 第 7 篇 Dify 应用介绍 + 聊天助手&Agent 应用关键点说明

本文介绍了Dify应用的核心概念与使用方法。Dify是基于大模型构建的实际场景应用,提供API、WebApp和易用界面。包含5种应用类型:聊天助手、文本生成、Agent、对话流和工作流。创建方式有模板创建、空白应用和DSL文件三种。重点讲解了聊天助手和Agent应用的关键功能:聊天助手支持提示词优化、知识库引用、文件处理等;Agent应用则强调推理能力、工具调用和任务自动化。文章还详细介绍了应用的调试、发布和管理工具,为开发者提供了全面的应用构建指南。

甘蓝的专栏 1882

java 防重_java接口防重提交如何处理

1.什么是接口防重?在一定的时间内请求同一接口,同一参数。由于请求是健康请求,会执行正常的业务逻辑,从而产生大量的废数据。2.处理方法第一种:前台在请求接口的时候,传递一个唯一值,然后在对应接口判断该唯一值,在一定的时间内是否被消费过第二种:采用Spring AOP理念,实现请求的切割,在请求执行到某个方法或某层时候,开始拦截进行,获取该请求的参数,用户信息,请求地址,存入redis中并放置过期时...

weixin_35323550的博客 398

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

java接口防重提交如何处理

在一定的时间内多次请求同一接口,同一参数。由于请求是健康请求,会执行正常的业务逻辑,从而产生大量的废数据。

不定时分享最优质的网络技术与教程,满满的干货。 1058

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

java接口的重复提交_接口重复提交解决方案

/*** 防止重复提交的注解*/@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AvoidRepeatSubmit {long lockTime() default 1000;}import com.mushi.anno.AvoidRepeatSubmit;import com.mu...

weixin_35878683的博客 1302

redis实现接口防重复提交

redis 接口防重 技术点:redis/aop 说明: 简易版本实现防止重复提交,适用范围为所有接口适用,采用注解方式,在需要防重的接口上使用注解,可以设置防重时效。 场景: 在系统中,经常会有一些接口会莫名其妙的被调用两次,可能在幂等接口中不会存在太大的问题,但是非幂等接口的处理就会导致出现脏数据,甚至影响系统的正确性。 选型参考: 在常见的防重处理分为多种,粗分为前端处理,后端处理 前端处...

sky_demo的博客 2508

java自定义注解防重提交

Annotation(注解) 从JDK 1.5开始, Java增加了对元数据(MetaData)的⽀持,也就 是 Annotation(注解)。注解其实就是代码⾥的特殊标记,它⽤于替代配置⽂件 常⻅的很多 @Override、@Deprecated等什么是元注解 注解的注解,⽐如当我们需要⾃定义注解时 会需要⼀些元注解(meta-annotation),如@Target和 @Retentionjava内置4种元注解@Target 表示该注解⽤于什么地⽅。

weixin_45063957的博客 1516

如何处理HTTP接口中的防重复提交

点击上方IT牧场,选择置顶或者星标技术干货每日送达关于防重复提交由于本人从事电商开发工作,项目中面对C端用户或多或少都会接触到提交保存或者修改的请求,例如创建订单,物流包裹签收...

ITMuch的专栏 1644

写了个 Spring Boot 接口防护的轮子:防重提交 + 限流,已发到 Maven Central

Guardian 是一个轻量级 Spring Boot API 请求层防护框架,提供防重复提交和接口限流两大能力。支持注解和 YAML 双模式配置,内置滑动窗口与令牌桶两种限流算法,Redis 和本地缓存一键切换,核心组件全部可插拔替换。引一个 Starter 即可使用,无需复杂配置。

2604_95253941的博客 963

Java实现防重复提交

欢迎访问我的个人博客:www.ifueen.com 防重复提交的重要性? 在业务开发中,为什么我们要去想办法解决重复提交这一问题发生?网上的概念很多:导致表单重复提交,造成数据重复,增加服务器负载,严重甚至会造成服务器宕机,那么为什么会造成这种现象?前台操作的抖动,快速操作,网络通信或者后端响应慢,都会增加后端重复处理的概率,就拿我亲身经历来说,因为业务逻辑,需要进行一个"关注"操作,但是写好业务之后在测试时连续点击几下,重复地进行关注和取消关注操作,因为操作过于频繁,而服务器走过来的响应速度没有那么快地进

f2315895270的博客 8734

Spring自定义注解防重提交方案(参数形式&Token令牌)

防重提交通常在需要防止用户重复提交表单或执行某些敏感操作时使用,以确保系统的数据一致性和安全性,本文章集结了通用场景下防重提交(参数形式&Token令牌),采用Java的特性(注解和AOP),配合Redis进行实现,使用方便有效。

猫步轻移,以学求知。余于此方寸之地,如猫观鼠,细察技艺所得,思维所悟。灵思如猫之警觉,日积月累,终成智海; 1475

SpringBoot接口防抖(防重复提交)

在Web系统中,表单提交是一个非常常见的功能,如果不加控制,容易因为用户的误操作或网络延迟导致同一请求被发送多次,进而生成重复的数据记录。从测试的结果上看,防抖是做到了,但是随着缓存消失、锁失效,还是可以发起同样的请求,所以要真正做到接口幂等性,还需要业务代码的判断、设置数据库表的UK索引等操作。其次,两次请求提交的参数比对,不一定要全部参数,选择标识性强的参数即可;Redisson的核心思路就是抢锁,当一次请求抢到锁之后,对锁加一个过期时间,在这个时间段内重复的请求是无法获得这个锁,也不难理解。

奔走的蚂蚁的博客 1996
上一篇: NIO与BIO的区别、NIO的运行原理和并发使用场景
下一篇: 你想不到的知识之Java单例模式
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值