问题描述:
在实际开发中,经常会因为接口响应时间过长,导致用户多次点击(连续)提交按钮。因此会创建多条相同的数据,如此就造成了数据重复。即我们常说的方法幂等性问题(就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用)
思路一:前段解决,通过点击按钮后制灰控制按钮不可点击。
思路二:后端解决,通过锁定参数+计时的方式,控制相同参数调用接口的次数。
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;
}