幂等性介绍
同一个方法,在相同的参数和相同的路径,请求多次,只会保证一次成功。
相信大家在日常做开发的时候,或多或少都会遇到这类的问题,比如说消息重复消费,表单重复提交等。
其实解决这类问题的通用思路就是设置全局id,下面介绍具体几种解决幂等的方案,这个涉及到具体业务,所以需要具体情况具体分析,这里只是提供一个大概的解决方案。
- 前端进行校验
点击提交按钮以后,将按钮改为禁用状态,但是这种不够严谨,使用接口调用工具的时候就能避开这种。
- 使用
JVM内置锁
比如
synchronize、reentrantLock锁,在处理业务之前,先加锁,判断这个业务是否被处理过,如果是的,就不执行业务并且释放锁,如果不是的,就执行业务代码,但是这种有一定的开销。
- 使用
Token的方式,将Token存储到Redis中
比如现在新增接口需要进行幂等处理,那么在调用新增之前,先获取
token(将token存储到Redis中),在新增的时候带上这个token进来,如果能够在Redis中查询到token的存在,说明还没有执行业务方法,这个时候就执行业务代码,并且将Redis中的token删除,如果查询不到,那么就证明已经被处理过了,现在就是重复处理了。
/**
* 在对需要进行幂等处理的接口之前,前端需要先获取一下token,也就是执行getToken
*/
@GetMapping("/insert")
public void insert(@RequestParam("username") String username, @RequestParam("token") String token) {
if (!checkToken(token)) {
// 这里直接抛异常或者返回出去
System.out.println("token不能为空或该操作存在重复!");
return;
}
try {
// TODO: 2021/1/22 正常处理业务
insert();
stringRedisTemplate.delete(token);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 在执行幂等之前,发放token
*/
public String getToken() {
String token = UUID.randomUUID().toString();
stringRedisTemplate.opsForValue().set(token, token, 60 * 3, TimeUnit.SECONDS);
return token;
}
/**
* 调用需要幂等的方法的时候,先进行判断
*/
public Boolean checkToken(String token) {
if (StringUtils.isEmpty(token)) {
return Boolean.FALSE;
}
if (!stringRedisTemplate.hasKey(token)) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
- 结合数据库,加上唯一性约束
比如按照某一个字段建立唯一性约束,在数据库层面加锁,或者在表中添加
token字段(给他加上约束),然后每次处理之前都先查询一下,是否存在,不存在再操作
总结:具体需要根据业务场景来选择,如果是比较敏感的业务,可能需要将以上几种情况结合起来使用,才能最终保证