精选文章 防止重复提交

防止重复提交

作者:牛大~ 时间: 2020-07-24 03:47:57
牛大~ 2020-07-24 03:47:57

常规版

此版本解决了 HashMap 无限增长的问题,它使用数组加下标计数器(reqCacheCounter)的方式,实现了固定数组的循环存储。

当数组存储到最后一位时,将数组的存储下标设置 0,再从头开始存储数据,实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Arrays;
 
@RequestMapping("/user")
@RestController
public class UserController {
 
    private static String[] reqCache = new String[100]; // 请求 ID 存储集合
    private static Integer reqCacheCounter = 0; // 请求计数器(指示 ID 存储的位置)
 
    @RequestMapping("/add")
    public String addUser(String id) {
        // 非空判断(忽略)...
        synchronized (this.getClass()) {
            // 重复请求判断
            if (Arrays.asList(reqCache).contains(id)) {
                // 重复请求
                System.out.println("请勿重复提交!!!" + id);
                return "执行失败";
            }
            // 记录请求 ID
            if (reqCacheCounter >= reqCache.length) reqCacheCounter = 0; // 重置计数器
            reqCache[reqCacheCounter] = id; // 将 ID 保存到缓存
            reqCacheCounter++; // 下标往后移一位
        }
        // 业务代码...
        System.out.println("添加用户ID:" + id);
        return "执行成功!";
    }
}

扩展版——双重检测锁(DCL)

上一种实现方法将判断和添加业务,都放入 synchronized 中进行加锁操作,这样显然性能不是很高,于是我们可以使用单例中著名的 DCL(Double Checked Locking,双重检测锁)来优化代码的执行效率,实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Arrays;
 
@RequestMapping("/user")
@RestController
public class UserController {
 
    private static String[] reqCache = new String[100]; // 请求 ID 存储集合
    private static Integer reqCacheCounter = 0; // 请求计数器(指示 ID 存储的位置)
 
    @RequestMapping("/add")
    public String addUser(String id) {
        // 非空判断(忽略)...
        synchronized (this.getClass()) {
            // 重复请求判断
            if (Arrays.asList(reqCache).contains(id)) {
                // 重复请求
                System.out.println("请勿重复提交!!!" + id);
                return "执行失败";
            }
            // 记录请求 ID
            if (reqCacheCounter >= reqCache.length) reqCacheCounter = 0; // 重置计数器
            reqCache[reqCacheCounter] = id; // 将 ID 保存到缓存
            reqCacheCounter++; // 下标往后移一位
        }
        // 业务代码...
        System.out.println("添加用户ID:" + id);
        return "执行成功!";
    }
}

一、完善版

Apache 为我们提供了一个 commons-collections 的框架,里面有一个非常好用的数据结构 LRUMap 可以保存指定数量的固定的数据,并且它会按照 LRU 算法,帮你清除最不常用的数据。

LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的数据淘汰算法,选择最近最久未使用的数据予以淘汰。

 


  org.apache.commons
  commons-collections4
  4.4

实现代码:

import org.apache.commons.collections4.map.LRUMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RequestMapping("/user")
@RestController
public class UserController {
 
    // 最大容量 100 个,根据 LRU 算法淘汰数据的 Map 集合
    private LRUMap reqCache = new LRUMap<>(100);
 
    @RequestMapping("/add")
    public String addUser(String id) {
        // 非空判断(忽略)...
        synchronized (this.getClass()) {
            // 重复请求判断
            if (reqCache.containsKey(id)) {
                // 重复请求
                System.out.println("请勿重复提交!!!" + id);
                return "执行失败";
            }
            // 存储请求 ID
            reqCache.put(id, 1);
        }
        // 业务代码...
        System.out.println("添加用户ID:" + id);
        return "执行成功!";
    }
}

2.最终版——封装

以上都是方法级别的实现方案,然而在实际的业务中,我们可能有很多的方法都需要防重,那么接下来我们就来封装一个公共的方法,以供所有类使用:

import org.apache.commons.collections4.map.LRUMap;
 
/**
 * 幂等性判断
 */
public class IdempotentUtils {
 
    // 根据 LRU(Least Recently Used,最近最少使用)算法淘汰数据的 Map 集合,最大容量 100 个
    private static LRUMap reqCache = new LRUMap<>(100);
 
    /**
     * 幂等性判断
     * @return
     */
    public static boolean judge(String id, Object lockClass) {
        synchronized (lockClass) {
            // 重复请求判断
            if (reqCache.containsKey(id)) {
                // 重复请求
                System.out.println("请勿重复提交!!!" + id);
                return false;
            }
            // 非重复请求,存储请求 ID
            reqCache.put(id, 1);
        }
        return true;
    }
}

调用代码如下:

import com.example.idempote.util.IdempotentUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RequestMapping("/user")
@RestController
public class UserController4 {
    @RequestMapping("/add")
    public String addUser(String id) {
        // 非空判断(忽略)...
        // -------------- 幂等性调用(开始) --------------
        if (!IdempotentUtils.judge(id, this.getClass())) {
            return "执行失败";
        }
        // -------------- 幂等性调用(结束) --------------
        // 业务代码...
        System.out.println("添加用户ID:" + id);
        return "执行成功!";
    }
}

 

 

 

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:移远NB-IOT模块BC26资料

下一篇:scrapy的extract() 、extract_first()方法,get() 、getall() 方法

您可能感兴趣

  • IntellIJ IDEA2020新功能

    一、java 1、Java 14支持:记录和模式匹配 IntelliJ IDEA 2020.1添加了对Java 14及其新功能的支持。IDE不仅添加了对Records的完整代码洞察支持,而且还使您能够快速创建新记录并生成其构造函数和组件,并警告存在的错误。您还将发现对instanceof运算符的模式匹配的支持,包括新的检查和快速修复,该快速修复通过用新的简洁模式变量替换它们来快速简化冗长的i...

  • 等保测评--管理人员安全测评

    信息安全等级保护,是对信息和信息载体按照重要性等级分级别进行保护的一种工作,在中国、美国等很多国家都存在的一种信息安全领域的工作。在中国,信息安全等级保护广义上为涉及到该工作的标准、产品、系统、信息等均依据等级保护思想的安全工作;狭义上一般指信息系统安全等级保护。 安全管理人员(HRS) 人员录用 L3-HRS1-01 L3-HRS1-02 L3-HRS1-03 人员离岗 L3-HRS1-0...

  • 解放双手、提高效率,如何优雅的做一名IDC运维!

    在IDC行业中,有一群从业者很特殊,你上班时他在上班,你休息时他在加班,你休假时他还在值班。这个人就是IDC运维工程师,听听他们自己怎么说。 我的职业是一名IDC运维工程师,守护机房的工兵,日常工作主要是维护机房的IT设备、网络系统的稳定,还有维护客户和公司的和谐关系,简称"维稳"人员。 今年是我在运维部门第七个年头了,我每天的工作看起来"轻松",实际很繁重。 经常来回奔波,出入各个机房是我...

  • Java异常面试题(2020最新版)

    文章目录 Java异常架构与异常关键字 Java异常简介 Java异常架构 1. Throwable 2. Error(错误) 3. Exception(异常) 运行时异常 编译时异常 4. 受检异常与非受检异常 受检异常 非受检异常 Java异常关键字 Java异常处理 声明异常 抛出异常 捕获异常 如何选择异常类型 常见异常处理方式 直接抛出异常 封装异常再抛出 捕获异常 自定义异常 t...

  • 大型架构及配置技术ansible(一)之ansible基础,ad-hoc,批量配置管理,ansible七种武器,JSON简介,YAML简介

    一、ansible基础 6台虚拟机(2cpu,1.5G以上内存,10G以上硬盘,1块网卡) 主机名 IP地址 角色 ansible40 192.168.4.40/24 管理主机 web41 192.168.4.41/24 托管主机 web42 192.168.4.42/24 托管主机 db43 192.168.4.43/24 托管主机 db44 192.168.4.44/24 托管主机 ca...

  • 前端实习生面试题分析(一)

    最近面试拿了很多公司的实习offer,只要是面试的都通过了。 接下来就分析下面试题,也能给自己一个提升吧,以便后续的面试更轻车熟路些,题目没什么顺序,想起什么写什么,还有我面试过程中的一些小套路。 估计要写好多,每天写几道题,而且有些经验性的东西也不容易写出来 这篇写的都是类似一些概念性的理论东西。 1.JS防抖和节流 先说为什么要做防抖和节流,针对一些会频繁触发的事件,像scroll、re...

  • JDK与IDE的安装与配置

    JDK与IDE的安装与配置 JDK和IDE去对应的官网下载,版本可以选最新的也可以凭自己喜好进行选择 1.安装JDK: (1)准备好官网下载jdk,选择安装目标路径即可。 (2)安装完成后,配置环境变量,使其生效 注:以win10为例 我的电脑,选择高级系统设置里的环境变量 系统变量中的Path,点击编辑 添加你电脑的java安装目录下的bin目录路径,默认路径为:C:\Program Fi...

  • 【STM32F429开发板用户手册】第38章 STM32F429的FMC总线应用之是32路高速IO扩展

    最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255 第38章 STM32F429的FMC总线应用之是32路高速IO扩展 本章教程为大家讲解利用STM32429的FMC总线扩展出32路高速IO,且使用简单,实际项目中也比较有实用价值。 目录 第38章 STM32F429的FMC总线应用之是32路高速IO扩展 38.1 初...

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。