使用SpringAOP+Redis实现接口处理幂等

news2024/11/25 23:34:31

文章目录

  • 一、思路分析
  • 二、代码实战
    • 1、搭建Springboot+AOP+Redis环境
    • 2、自定义注解
    • 3、切面类
    • 4、测试一下吧

一、思路分析

在调用后台接口时,由于用户多次点击或者说第三方重试,可能会导致幂等问题。

解决方案无非就是上一次请求没有处理完,第二次请求不会处理,或者直接提示请求频繁,让用户等待。

我们基于SpringAOP(或者拦截器)来实现接口的幂等处理,多次请求时,提示用户不要重复请求,并缓存处理结果,将处理后的结果快速返回。

流程图如下:

在这里插入图片描述

二、代码实战

1、搭建Springboot+AOP+Redis环境

2、自定义注解

该注解标注在Controller层,可以根据项目需要进行参数调整,比如说可以实现按指定字段判断幂等、实现接口的限流、指定幂等的判断条件等等。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 实现幂等的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {

}

3、切面类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 幂等切面
 */
@Aspect
@Component
public class IdempotentAspect {

    private final StringRedisTemplate redisTemplate;

    private final String Status = "status";
    private final String Begin = "begin";
    private final String End = "end";
    private final String Data = "data";

    @Autowired
    public IdempotentAspect(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint point, Idempotent idempotent) throws Throwable {
        String identifier = generateIdentifier(point);

        // 直接设置状态为begin,设置成功,说明该请求已处理
        Boolean beginStatus = redisTemplate.opsForHash().putIfAbsent(identifier, Status, Begin);
        if(!beginStatus) {
            // 先取数据,再取状态,防止这期间数据过期
            Object dataObject = redisTemplate.opsForHash().get(identifier, Data);
            Object status = redisTemplate.opsForHash().get(identifier, Status);
            if (Begin.equals(status)) {
                // 请求在处理,抛异常退出
                throw new RuntimeException("请求处理中,不要重复请求");
            }

            if (End.equals(status)) {
                // 如果处理结束,直接将结果返回
                return JsonAide.fromJson(dataObject.toString(), getMethod(point).getReturnType());
            }
        }
        // 5分钟过期
        redisTemplate.expire(identifier, 5, TimeUnit.MINUTES);

        // 设置成功,执行流程
        Object proceed = point.proceed();
        // 将请求状态设为结束,并且缓存返回值
        // TODO 请求结束之后,可以将key删掉,在业务中进行判断是否重复请求
        redisTemplate.opsForHash().put(identifier, Status, End);
        redisTemplate.opsForHash().put(identifier, Data, JsonAide.toJson(proceed));

        return proceed;
    }

    /**
     * 获取方法
     */
    private Method getMethod(ProceedingJoinPoint point) {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        return methodSignature .getMethod();
    }

    /**
     * 获取方法签名
     */
    private String generateIdentifier(ProceedingJoinPoint point) {
        // 获取方法参数和相关信息
        Method method = getMethod(point);
        Object[] args = point.getArgs();
        // 根据方法名和参数生成唯一标识 TODO 可以替换为使用统一流水号
        return method.getName() + ":" + Stream.of(args).filter(Objects::nonNull).map(Object::toString).collect(Collectors.joining());
    }
}

4、测试一下吧

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping("/test")
    @Idempotent
    public Object test(@RequestBody Map<String, String> req) throws InterruptedException {
        System.out.println("请求进来了");
        // 休眠10秒 ,模拟业务处理时间
        Thread.sleep(10000);
        System.out.println("请求处理结束了");
        return req;
    }
}

我们发现,同一个请求未处理完成,会抛异常,此时我们捕获这个异常即可。(或者使用拦截器实现)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Tipas结合内网穿透在Ubuntu上搭建高效问题解答平台网站

文章目录 前言2.Tipask网站搭建2.1 Tipask网站下载和安装2.2 Tipask网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3 Cpolar稳定隧道&#xff08;本地设置&#xff09; 4. 公网访问测试5. 结语 前…

第八章 :如何基于Spring Boot +Mybatis 快速开发 Restful API

第八章 :如何基于Spring Boot +Mybatis 快速开发 Restful API 前言 本章知识重点:主要讲解开发人员如何利用【MybatisPlus+EasyCode插件 】快速开发Restful API ,利用节约的时间学习,养成一种正向循环的技术之道,最后达到终身学习成长! 案例基于SpringBoot 2.3.2.RELEASE…

Outlook邮件视图设置怎么修复

故障现象 Outlook邮箱显示不对 故障截图 故障原因 邮箱视图设置不对 解决方案 1、在Outlook上方工具栏找到视图按钮&#xff0c;以此选择视图→视图设置→列&#xff0c;打开选择的列 2、在视图→邮件预览里面&#xff0c;选择1行&#xff0c;在阅读格式选择靠右&#xff…

站长必读:如何巧妙应对网站攻击与提升速度

亲爱的站长们&#xff0c;您是否曾为网站被攻击而烦恼&#xff0c;或者一直想让您的网站更快速地响应用户&#xff1f;别担心&#xff0c;本文将为您揭示一项重要而有效的解决方案——CDN&#xff08;内容分发网络&#xff09;。让我们一起探讨如何从站长的角度出发&#xff0c…

【深度学习实验】网络优化与正则化(三):随机梯度下降的改进——Adam算法详解(Adam≈梯度方向优化Momentum+自适应学习率RMSprop)

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入必要的库1. 随机梯度下降SGD算法a. PyTorch中的SGD优化器b. 使用SGD优化器的前馈神经网络 2.随机梯度下降的改进方法a. 学习率调整b. 梯度估计修正 3. 梯度估计修正&#xff1a;动量法Momen…

Vue使用高德地图实现点击获取经纬度以及搜索功能

1. 首先在高德开放平台申请key值 2. 然后会在这个地方显示 3. 在VScode里面安装地图 yarn add amap/amap-jsapi-loader --save 4. 准备一个容器 <div id"maps"></div> <style scoped>#maps {width: 100%;height: 100%;position: relative;z-index…

2011年12月13日 Go生态洞察:从零到Go,在谷歌首页上的24小时飞跃

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

哈希竞猜游戏开发源码部署方案

随着互联网技术的发展&#xff0c;越来越多的人开始关注网络安全问题&#xff0c;而哈希算法作为一种重要的加密技术&#xff0c;在网络安全领域得到了广泛应用。其中&#xff0c;哈希竞猜游戏作为一种新型的网络安全挑战赛&#xff0c;也受到了越来越多人的关注。本文将介绍哈…

Go语言安装教程

【Go系列-1】-Go安装教程 环境提前准备 安装的时候可以选择自己的目录进行环境管理 E:\Z_Enviroment\Go创建文件夹&#xff1a; E:\Z_Enviroment\Go E:\Z_Enviroment\GoWorks E:\Z_Enviroment\GoWorks\bin E:\Z_Enviroment\GoWorks\pkg E:\Z_Enviroment\GoWorks\src环境变量…

成都瀚网科技有限公司抖音带货的正规

成都瀚网科技有限公司&#xff0c;一家在科技领域有着深厚积累的公司&#xff0c;近年来也开始涉足电子商务领域&#xff0c;特别是在抖音等短视频平台上进行带货活动。在这个充满机遇与挑战的时代&#xff0c;该公司以其独特的商业模式和运营策略&#xff0c;正在赢得消费者的…

【实例分割】用自己数据集复现经典论文YOLACT

YOLACT&#xff1a;You Only Look At CoefficienTs &#x1f3c6;论文下载&#xff1a;paper &#x1f3c6;代码下载&#xff1a;code &#x1f3c6;论文详解&#xff1a;YOLACT 目录 &#x1f342;&#x1f342;1.安装环境 &#x1f342;&#x1f342;2.数据准备 &…

代码随想录算法训练营第23期day49| 123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

目录 一、&#xff08;leetcode 123&#xff09;买卖股票的最佳时机III 二、&#xff08;leetcode 188&#xff09;买卖股票的最佳时机IV 一、&#xff08;leetcode 123&#xff09;买卖股票的最佳时机III 力扣题目链接 增加了两次的限制&#xff0c;相应的就是需要考虑的状…

Cesium 展示——改变点与线的关联关系后可实现对点或线的单独操作

文章目录 需求分析1. 实现区域选中状态(更改前)2. 循环遍历实体判断该区域内的实体(更改前)1. 将每一条线和线所对应的两个点进行关联(更改后的逻辑)2. 将每一个点和所对应的两条线进行关联(更改后的逻辑)3. 在新增点后修改【线、点】【点、线】间的关联关系(更改后的…

scDrug:从scRNA-seq到药物反应预测

scRNA-seq技术允许在转录组水平上对数千个细胞进行测量。scRNA-seq正在成为研究肿瘤微环境中细胞成分及其相互作用的重要工具。scRNA-seq也被用于揭示肿瘤微环境模式与临床结果之间的关联&#xff0c;并在复杂组织中剖析药物治疗的细胞特异性效应。scRNA-seq的最新进展推动了疾…

SAP 52策略测试简介

我们在前面测试了50策略按单生产,创建完计划独立需求后,通过主数据中的独立集中的字段控制下层物料是否能通过计划订单转成生产订单和采购订单。 52策略其实和50策略非常的相似。52策略就是按库存生产,创建完计划独立需求后的结果和50策略是一样的。 1、我们先看下50策略和…

【避雷选刊】Springer旗下2/3区,2个月录用!发文量激增,还能投吗?

计算机类 • 好刊解读 前段时间小编分析过目前科睿唯安数据库仍有8本期刊处于On Hold状态&#xff0c;其中包括4本SCIE、4本ESCI期刊&#xff08;&#x1f449;详情可见&#xff1a;避雷&#xff01;又有2本期刊被标记“On Hold”&#xff01;含中科院2区&#xff08;TOP&…

Vue 的h()

在你的示例中&#xff0c;h(div, { id: foo }, hello) 使用的是 Vue.js 中的虚拟DOM(hyperscript)的写法&#xff0c;这种写法用于创建虚拟节点。让我来详细解释一下&#xff1a; h 是一个用于创建虚拟节点的函数&#xff0c;通常是由 Vue.js 或其他类似的库提供的。这个函数通…

PostGIS学习教程二:PostGIS安装和创建空间数据库

一、安装PostgreSQL 在安装PostGIS前首先必须安装PostgreSQL&#xff0c;然后在安装好的Stack Builder中选择安装PostGIS组件。 PostgreSQL安装文件下载地址是https://www.enterprisedb.com/downloads/postgres-postgresql-downloads 这里使用的PostgreSQL版本是9.6。 双击…

人工智能与发电玻璃:未来能源技术的融合

人工智能与发电玻璃&#xff1a;未来能源技术的融合 摘要&#xff1a;本文探讨人工智能与发电玻璃这两项技术的结合&#xff0c;共同推动能源领域的创新。本文将介绍发电玻璃工作原理及应用、人工智能在发电玻璃的应用领域以及共同为可持续能源发展做出贡献。 一、引言 随着科…

新加坡建筑设备公司【Ten-League】申请3230万美元纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于新加坡的重型建筑设备和工程咨询服务公司Ten-League International Holdings Limited&#xff08;简称&#xff1a;Ten-League&#xff09;近期已向美国证券交易委员会&#xff08;SEC&am…