首页 游戏 软件 资讯 排行榜 专题
首页
科技数码
Spring Boot防重实战:Redis缓存+哈希锁方案性能测评

Spring Boot防重实战:Redis缓存+哈希锁方案性能测评

热心网友
24
转载
2025-10-30

本文将介绍一种基于“哈希 + 缓存”双重防护机制的接口防重复提交方案,无需前端配合,不依赖额外 Token,仅通过请求特征动态生成哈希签名即可快速识别重复请求。我们将采用 Spring Boot + AOP + Redis/Caffeine 架构实现这一机制,整体方案轻量高效,具备实战级复用价值。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

在高并发业务场景中,接口被重复点击或短时间内多次提交请求,是常见却极具破坏性的隐患。例如在电商系统中,用户多次点击“提交订单”按钮可能导致重复订单生成;支付接口被异常触发可能造成重复扣费;表单接口因网络波动被重复提交,极易产生脏数据。

这类问题虽看似小概率事件,但在真实生产环境中往往引发严重后果。为规避此类“重复提交”带来的混乱,我们需要在服务端层面构建一个高可靠的防重机制。

本方案通过构建全局唯一哈希值,结合动态缓存实现高效防重。当检测到相同哈希值在有效期内重复出现时,系统将自动拦截并返回提示,从源头杜绝数据异常。

防重原理与方案选型

什么是防重复提交

防重复提交(Prevent Duplicate Request)指防止用户在短时间内对同一接口重复触发操作,从而避免数据重复创建、状态异常或业务逻辑错误。

典型场景包括:

下单接口防止同一用户创建两笔相同订单;表单提交避免页面卡顿或多次点击产生重复记录;支付操作防止短时间内重复支付。

常见实现方式

本方案采用第三种方式,通过 URL + 请求方法 + 请求参数构建全局唯一哈希值,并将其存储在缓存中。当系统检测到相同哈希值在有效期内再次出现时,即判定为重复请求。

系统架构与流程设计

目录结构如下

/src 
├── /main
│   └── /java/com/icoderoad/duplicate
│       ├── annotation/PreventDuplicate.java
│       ├── aspect/PreventDuplicateAspect.java
│       ├── storage/DuplicateStorage.java
│       ├── storage/impl/RedisStorage.java
│       ├── storage/impl/CaffeineStorage.java
│       └── util/RequestParameterUtils.java
└── /resources
    └── application.yml

防重复机制核心流程如下:

请求进入控制层;AOP 拦截目标方法;提取 URL、请求方法、参数信息;计算 SHA-256 哈希值作为 Key;查询缓存是否存在该 Key;存在则拒绝请求,不存在则执行方法并写入缓存。

核心实现代码

自定义注解 @PreventDuplicate
package com.icoderoad.duplicate.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交注解 
 * 可应用在 Controller 层接口上 
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PreventDuplicate {
    /** 防重复提交时间(单位:秒) */
    int expire() default 3;
    
    /** 时间单位,默认秒 */
    TimeUnit timeUnit() default TimeUnit.SECONDS;
    
    /** 可指定参与生成哈希的主要字段 */
    String[] field() default {};
    
    /** 提示信息 */
    String message() default "请勿重复提交!";
}
AOP 拦截器 PreventDuplicateAspect
package com.icoderoad.duplicate.aspect;

import cn.hutool.crypto.digest.DigestUtil;
import com.icoderoad.duplicate.annotation.PreventDuplicate;
import com.icoderoad.duplicate.storage.DuplicateStorage;
import com.icoderoad.duplicate.storage.DuplicateStorageFactory;
import com.icoderoad.duplicate.util.RequestParameterUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
@RequiredArgsConstructor
public class PreventDuplicateAspect {
    private final HttpServletRequest request;
    private final DuplicateStorageFactory storageFactory;
    
    @Around("@annotation(preventDuplicate)")
    public Object handle(ProceedingJoinPoint joinPoint, PreventDuplicate preventDuplicate) throws Throwable {
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String params = RequestParameterUtils.getAllParamsAsString(joinPoint, preventDuplicate.field());
        
        // 拼接唯一签名源
        String signSource = method + ":" + uri + ":" + params;
        long start = System.currentTimeMillis();
        String key = DigestUtil.sha256Hex(signSource);
        long end = System.currentTimeMillis();
        System.out.println("生成哈希耗时:" + (end - start) + "ms");
        
        DuplicateStorage storage = storageFactory.getStorage();
        if (storage.exists(key)) {
            throw new RuntimeException(preventDuplicate.message());
        }
        storage.put(key, preventDuplicate.expire(), preventDuplicate.timeUnit());
        return joinPoint.proceed();
    }
}
控制层示例
package com.icoderoad.duplicate.controller;

import com.icoderoad.duplicate.annotation.PreventDuplicate;
import com.icoderoad.duplicate.model.ArticleDTO;
import com.icoderoad.duplicate.model.UserInfo;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/hello")
    @PreventDuplicate
    public String hello(String name, String age, String address) {
        return "防重复测试:" + name + " " + age + " " + address;
    }
    
    @PostMapping("/saveUserInfo")
    @PreventDuplicate(expire = 5)
    public String saveUserInfo(@RequestBody UserInfo userInfo) {
        System.out.println(userInfo);
        return "请求时间:" + DateTime.now() + " 保存成功";
    }
    
    @PostMapping("/saveContent")
    @PreventDuplicate(expire = 10)
    public String saveContent(@RequestBody ArticleDTO articleDTO) {
        System.out.println(articleDTO);
        return "请求时间:" + DateTime.now() + " 内容保存成功";
    }
}

测试效果:当短时间内重复发送相同参数请求时,系统将直接返回"请勿重复提交!"异常提示。

性能验证

为验证哈希计算的性能表现,我们模拟生成一篇 3 万字文章内容并重复请求测试。测试数据表明:

首次生成哈希值耗时约9ms(JVM 预热阶段);多次请求后平均耗时降至0ms;即使请求参数极大,对接口性能几乎无影响。

结论:SHA-256 哈希算法在防重场景中既具备唯一性又满足高性能要求,完全可支撑高并发接口的防重复需求。

总结与实践建议

通过本方案,我们实现了一个无侵入、通用性强、性能优越的防重复提交机制。核心优势体现在:

使用AOP 切面拦截请求,避免侵入业务逻辑;基于请求路径 + 方法 + 参数哈希生成唯一标识;通过Redis / Caffeine 缓存实现分布式与本地防重双模式;支持灵活配置提交间隔与关键字段粒度。

该方案不仅适用于表单、下单、支付等关键接口,还可扩展至异步任务提交、API 幂等控制等更广泛场景。

未来还可进一步优化:

加入异步清理机制;为Key 结构添加命名空间前缀;结合分布式锁提升集群环境下的安全性。

一句话总结:

防重不是“锦上添花”的优化,而是“防止灾难”的必要保护。用哈希 + 缓存双重保险,为你的接口上好“安全带”!

来源:https://www.51cto.com/article/828280.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

LangChain之外,为何还需关注Spring AI?
AI
LangChain之外,为何还需关注Spring AI?

如果面试官问你这个问题,你可以这样总结:LangChain功能繁多、反应迅速,是探索 AI 前沿的最佳工具,而 Spring AI 更像是一套工业级的生产线。对于企业而言,我们不仅需要调用大模型,更

热心网友
01.07
Spring Boot + sshd-sftp:SSH 命令与文件传输实践
科技数码
Spring Boot + sshd-sftp:SSH 命令与文件传输实践

在现代分布式系统中,服务器间的远程操作与文件传输是常见需求。SSH作为一种安全的网络协议,为远程登录和文件传输提供了可靠保障。 前言在现代分布式系统中,服务器间的远程操作与文件传输是常见需求。SSH

热心网友
12.15
Spring Boot + Pcap4j 实现网络流量抓包与实时分析
科技数码
Spring Boot + Pcap4j 实现网络流量抓包与实时分析

在当今数字化时代,网络流量如同信息社会的血液,承载着海量的数据交互。对网络流量进行有效的抓包与实时分析,是保障网络安全、优化网络性能的关键环节。无论是及时发现潜在的网络攻击,还是排查网络拥塞等问题,

热心网友
12.15
震惊!SpringBoot 接口耗时监控还能这么玩,简单到离谱!
科技数码
震惊!SpringBoot 接口耗时监控还能这么玩,简单到离谱!

随着业务规模的增长,我们还可以在此基础上拓展更多维度,比如统计 QPS、采集请求来源、结合用户信息等,形成完整的 接口性能监控体系。这不仅能帮助我们快速定位问题,更能为系统优化提供强有力的数据支撑。

热心网友
12.15
在 SpringBoot 项目中如何动态切换数据源、数据库?(可直接CV)
科技数码
在 SpringBoot 项目中如何动态切换数据源、数据库?(可直接CV)

如果服务器搭建的是一主多从多个mysql数据源,主服务器用来读。从服务器用来写。此时你在代码层面用注解指定了一个增删改方法到从数据源,但是碰巧此时从数据源失效了,那么就会自动的切换到其它服务器。 前

热心网友
12.15

最新APP

火柴人传奇
火柴人传奇
动作冒险 04-01
街球艺术
街球艺术
体育竞技 04-01
飞行员模拟
飞行员模拟
休闲益智 04-01
史莱姆农场
史莱姆农场
休闲益智 04-01
绝区零
绝区零
角色扮演 04-01

热门推荐

OPPO A6k 手机上市:天玑 6300 + LCD 直屏 + 7000mAh 电池,定价 1999 元起
科技数码
OPPO A6k 手机上市:天玑 6300 + LCD 直屏 + 7000mAh 电池,定价 1999 元起

OPPO A6k手机重磅发布:天玑6300处理器、高清LCD直屏、7000mAh超大电池,售价仅1999元起 OPPO旗下广受欢迎的A系列再添实力新机。近日,备受期待的OPPO A6k正式上市发售。这款新品搭载了备受好评的天玑6300八核处理器,并配备了一块容量高达7000mAh的耐用长寿电池,成为

热心网友
04.06
《红色沙漠》熔化锁链的火焰任务攻略-支线任务完成方法详解
游戏攻略
《红色沙漠》熔化锁链的火焰任务攻略-支线任务完成方法详解

速览 在《红色沙漠》的广阔世界中,数量丰富的支线任务与主线剧情共同构筑了沉浸式的冒险体验。其中,“熔化锁链的火焰”任务作为瑟金斯家族剧情线的关键环节,其触发机制与主线进程紧密相连。任务并非随时可用,玩家需将主线故事推进到特定阶段后,任务才会自动添加至任务日志。本篇攻略将为你详解此支线任务的接取条件与

热心网友
04.06
《异种航员2》运动机制详解-战术移动与时间单位消耗
游戏攻略
《异种航员2》运动机制详解-战术移动与时间单位消耗

《异种航员2》运动机制深度解析 在《异种航员2》(Xenonauts 2)的策略战斗中,对“时间单位”(TU)的高效运用是取胜的核心。每个士兵的移动、射击乃至战术配合,都依赖于玩家对TU的精确规划。操作上手简单:选中单位后,直接使用鼠标左键点击目的地方格,系统便会清晰显示移动所需消耗的时间单位,帮助

热心网友
04.06
《异种航员2》封面机制详解-掩体闪避效果介绍
游戏攻略
《异种航员2》封面机制详解-掩体闪避效果介绍

速览 在《异种航员2》(Xenonauts 2)的战局中,掌握“战术规避”与精通“火力输出”同等关键。游戏全新设计的掩体系统,是提升你作战小队生存几率的战略性核心。简言之,战场上绝大多数可见的物体都能转化为你的战术屏障。无论是散落的木箱、残缺的矮墙,还是茂密的灌木丛与坚实的建筑物,巧妙地利用它们,就

热心网友
04.06
《红色沙漠》超凡建造物任务攻略-任务流程详解
游戏攻略
《红色沙漠》超凡建造物任务攻略-任务流程详解

速览 在开放世界大作《红色沙漠》中,庞大的支线任务系统为玩家提供了丰富的探索体验。其中,“超凡建造物”任务是阿方索家族势力任务线中的重要一环。要成功接取此任务,玩家必须首先完成其前置任务【枪械名门】。在此之后,任务的下一步关键操作是前往游戏中标注的特定建筑地点进行互动调查——这本质上是一个用于快速移

热心网友
04.06