• 方法幂等性后端解决方案设计


    问题描述:

    在实际开发中,经常会因为接口响应时间过长,导致用户多次点击(连续)提交按钮。因此会创建多条相同的数据,如此就造成了数据重复。即我们常说的方法幂等性问题(就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用)

    思路一:前段解决,通过点击按钮后制灰控制按钮不可点击。
    思路二:后端解决,通过锁定参数+计时的方式,控制相同参数调用接口的次数。
    sequenceDiagram participant w as web页面 participant c as controller(后端:接收器) participant s as service(后端:业务组件) participant r as 缓存(数据库redis) loop 多次提交数据user123 w->>c: 提交数据到后台 end c->>r: 查询缓存user123 r->>c: 返回缓存结果 alt user数据已被缓存 c->>w: 告诉前段数据已提交请等待 else 未查询到user123数据 c->>r: 缓存请求数据user123 c->>s: 处理业务 end
    涉及技术:AOP注解+redis
    步骤一:定义注解 PowerReq
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PowerReq {
        /**
         * 锁住方法中的第几个参数(-1全部参数)
         *
         * @return
         */
        int lockIndexParam() default -1;
    
        /**
         * 锁的时间
         *
         * @return
         */
        int leaseTime() default 10;
    }
    
    步骤二:定义注解解析器
    @Aspect
    @Component
    public class PowerReqAspect {
    
        @Resource
        private RedisTemplate redisTemplate;
    
        /**
         * 环绕通知:灵活自由的在目标方法中切入代码
         */
        @Around("@annotation(powerReq)")
        public Object around(ProceedingJoinPoint joinPoint, PowerReq powerReq) throws Throwable {
            // 获取待锁定的参数index
            int lockIndexParam = powerReq.lockIndexParam();
            // 获取方法传入参数
            Object[] params = joinPoint.getArgs();
            String i = params[lockIndexParam - 1].toString();
            ValueOperations valueOperations = redisTemplate.opsForValue();
            Object o = valueOperations.get(String.valueOf(i));
            if (Objects.isNull(o)) {
                valueOperations.set(String.valueOf(i), String.valueOf(i), powerReq.leaseTime(), TimeUnit.SECONDS);
                return joinPoint.proceed();
            }
            throw new Exception("数据已提交请等待");
    
    
        }
    }
    
    
    使用方式
    @PowerReq(lockIndexParam = 1)
    @RequestMapping("/lock")
    public Boolean hello1(@RequestBody String id, Map<String, String> param) {
      redissonService.tt(param.get("id"));
      logger.info("业务已完成={}", param);
      return Boolean.TRUE;
    }
    

    项目地址:https://gitee.com/liujinliang_gitee/think-in-springboot/tree/master/think-in-redis
  • 相关阅读:
    为什么不要用VSCODE来写Makefile
    JavaFX第三弹
    javaFX文件和文件夹选择器
    写了一个vsftpd的GUI
    在java中调用shell命令和执行shell脚本
    正交投影与斯密特正交化的好处
    Linux下安装软件
    C++中的仿函数
    C++中重载操作符[ ]
    使用斐波那契查找
  • 原文地址:https://www.cnblogs.com/jinliang374003909/p/15257215.html
Copyright © 2020-2023  润新知