目录
2.后端根据雪花算法生成唯一标识key,以雪花数为key存到redis。并返回key给前端。
5.请求到达后端,验证key,根据key去redis里查,如果得不到值,说明已处理过。否则尝试获取redisson锁,然后处理业务,并删掉redis里的值。
1.幂等性是什么?
在新增和修改功能的时候,常常需要用到幂等性。
所谓的幂等性就是,即使不小心多按了几次,仍然只执行一次。常用在下单、增量修改、插入数据的时候。
效果:
在同一时间狂点鼠标"新增",或者jmter压测并发多线程访问这个接口,也执行一次新增。
2.如何实现幂等性呢?
本次案例采用,token+redis+分布式锁:
流程如下所示:
1.新增管理员,出弹窗的同时,请求后台。
//获取唯一标识
beforeAdd() {
setTimeout(() => {
this.dialogAdd = true;
}, 500);
//请求后台拿唯一标识
(this.imageUrl = ""),
this.$axios.get("/api/pc-zdy-sys/admin/preAddAdmin").then((res) => {
if (res.data.code == 200) {
this.allRoleList = res.data.data.allRoleList; //系统里所有的角色
this.key = res.data.data.key; //唯一标识key
}
});
},
2.后端根据雪花算法生成唯一标识key,以雪花数为key存到redis。并返回key给前端。
public String preAddAdmin() {
//雪花id 为key存到redis 值可以无意义
Long snowflakeNextId = IdUtil.getSnowflakeNextId();
String key = String.valueOf(snowflakeNextId);
redisTemplate.opsForValue().set(key,"唯一标识");
//返回雪花id
return key;
}
3.前端保存后端传过来的key。
data() {
return {
key: "", //唯一标识key
};
},
4.前端输入完成信息,点击【保存】,携带key请求后端。
confirmAdd() {
//key带到后台去,请求接口确认新增
this.AddAdmin.url = this.imageUrl;
this.AddAdmin.key = this.key;
if(this.isAnyFieldEmpty){
this.$message({
message: "不能为空",
type: "danger",
});
return;
}
this.$axios
.post("/api/pc-zdy-sys/admin", this.AddAdmin)
.then((res) => {
if (res.data.code == 200) {
this.$message({
message: "恭喜你,新增成功",
type: "success",
});
this.dialogAdd = false;
this.queryAdmin()
}
})
.catch((error) => {
this.$message({
message: "新增失败",
type: "danger",
});
});
},
5.请求到达后端,验证key,根据key去redis里查,如果得不到值,说明已处理过。否则尝试获取redisson锁,然后处理业务,并删掉redis里的值。
public void addAdmin(UserDTO userDTO) {
//根据key找是否有token
String key = userDTO.getKey();
if(StringUtils.isBlank(key)||ObjectUtil.isEmpty(redisTemplate.opsForValue().get(key))){
//没有key或根据key找不到token直接抛异常
throw new RuntimeException("新增失败");
}
//拿锁去处理
RLock lock = redissonClient.getLock(key);
boolean b = lock.tryLock();
//如果拿锁失败
if(!b){
throw new RuntimeException("新增失败");
}
lock.lock();
try {
//admin加到用户表
User user = new User();
BeanUtils.copyProperties(userDTO,user);
user.setStatus(1);
userMapper.insert(user);
//将userDTO里角色list加到角色-用户 中间表去
//需要用户id 角色id集合
userRoleMapper.addRoleListToUserRole(user.getId(),userDTO.getRoleList());
//删掉缓存
redisTemplate.delete(key);
} catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}