分布式锁实例教程之防止重复提交

网友投稿 1161 2022-11-19

分布式锁实例教程之防止重复提交

分布式锁实例教程之防止重复提交

目录抛出一个问题正常的代码逻辑1、注册controller2、注册service3、加入分布式锁,问UhZopETw题依旧解决方法分布式锁+防重操作总结

抛出一个问题

需求:现在有一个常见的场景——用户注册,但是如果出现重复提交的情况,则会出现多条注册数据,因此这里如何做好防止重复提交这是我们需要解决的问题。

正常的代码逻辑

1、注册controller

/**

* 用户注册请求

* @param userDto

* @param bindingResult

* @return

*/

@RequestMapping(value=prefix+"/db/register",method = RequestMethod.POST,consumes = MediaType.APPLICATION_jsON_UTF8_VALUE)

public BaseResponse register(@RequestBody @Validated UserDto userDto, BindingResult bindingResult){

BaseResponse response=new BaseResponse(StatusCode.Success);

try {

log.debug("注册信息: {} ",userDto);

//注册之前,我们先判断是否已经注册了。(正常逻辑)

User user=userService.selectByUserName(userDto.getUserName());

if (user!=null){

return new BaseResponse(StatusCode.UserNameExist);

}

userService.register(userDto);

}catch (Exception e){

e.printStackTrace();

response=new BaseResponse(StatusCode.Fail);

}

return response;

}

在controller中判断用户是否已经注册,如果没有注册,则调用注册逻辑。

2、注册service

/**

* 用户注册——最普通的操作,没有任何加锁,没有任何防止重复提交

*

* @param userDto

* @return

* @throws Exception

*/

public int register(UserDto userDto) throws Exception {

int result = 0;

User user = new User();

BeanUtils.copyProperties(userDto, user);

result = userMapper.insertSelective(user);

return result;

}

简单的增加一个用户信息。

问题也很明显,这样毕竟会出现问题,并发的问题,也会出现重复注册的情况。测试结果也很明显

一堆重复注册的,但是加入分布式锁就好了么?

3、加入分布式锁,问题依旧

分布式锁的实现方式

/**

* 用户注册,基于redisson的分布式锁

*

* @param userDto

* @return

*/

public int registerLockRedisson(UserDto userDto) {

int result = 0;

RLock rLock = redissonLockComponent.acquireLock(userDto.getUserName());

try {

if (rLock != null) {

User user = new User();

BeanUtils.copyProperties(userDto, user);

user.setCreateTime(new Date());

userMapper.insertSelective(user);

}

} catch (Exception e) {

log.error("获取redisson分布式锁异常");

} finally {

if (rLock != null) {

redissonLockComponent.releaseLock(rLock);

}

}

return result;

}

加入分布式锁之后,再进行测试。

不好意思,依旧出现了重复注册的情况。何解?

问题分析,为了遵循单一职责,这里的读取数据(判断是否注册)与写入数据(用户注册)操作是分开的,分布式锁为了进一步细化,只是加在了写入数据阶段,并没有加在整个业务阶段,因此会出现数据重复提交的问题,解决方法有很多,最暴力的方法无非就是给数据库user表中的用户名字段加入唯一约束。但是这样随着业务规模扩大,数据库压力会越来越大。

解决方法

解决方法有几种,前面提到的给数据库增加唯一索引也是一种方法。但是为了减轻数据库的压力,这种操作可以直接在应用层处理。

分布式锁+防重操作

在分布式锁的基础上,加入redis存储key值,作为防重提交的判断。不想过多解释了,直接上代码吧。

/**

* 用户注册,redisson分布式锁,redis防止重复提交

*

* @param userDto

* @return

*/

public int registerLockAvoidDupPost(UserDto userDto) {

int result = 0;

RLock rLock = redissonLockComponent.acquireLock(userDto.getUserName());

try {

//redis中根据用户名存储作为key值

String key = lockKeyPrefix+userDto.getUserName();

if (!stringRedisTemplate.hasKey(key)) {//如果不存在key则进入注册阶段

stringRedisTehttp://mplate.opsForValue().set(key,UUID.randomUUID().toString(),10L,TimeUnit.SECONDS);

User user = new User();

BeanUtils.copyProperties(userDto, user);

user.setCreateTime(new Date());

userMapper.insertSelective(user);

log.infUhZopETwo("{},注册成功",userDto.getUserName());

}else{//如果存在,则提示不可重复提交

log.error("10秒内,请勿重复提交注册信息");

}

} catch (Exception e) {

log.error("获取redisson分布式锁异常");

} finally {

if (rLock != null) {

redissonLockComponent.releaseLock(rLock);

}

}

return result;

}

分布式锁的实现方式有多重,redis/redisson/zookeeper等,只需要在已经实现分布式锁的基础上引入防重提交的机制即可。

因此还有其他方式的实现,如下所示为zookeeper分布式锁+redis防重的方式

/**

* 用户注册,redisson分布式锁,redis防止重复提交

*

* @param userDto

* @return

*/

public int registerLockAvoidDupPost(UserDto userDto) {

int result = 0;

InterProcessMutex mutex=new InterProcessMutex(client,zkPrefix+userDto.getUserName()+"-lock");

try {

if (mutex.acquire(10L, TimeUnit.SECONDS)){

final String realKey=zkRedisKeyPrefix+userDto.getUserName();

if (!stringRedisTemplate.hasKey(realKey)){

stringRedisTemplate.opsForValue().set(realKey, UUID.randomUUID().toString());

User user=new User();

BeanUtils.copyProperties(userDto,user);

user.setCreateTime(new Date());

userMapper.insertSelective(user);

log.info("{},注册成功",userDto.getUserName());

}else{

log.error("10秒内,请勿重复提交注册信息");

}

}else{

throw new RuntimeException("获取zk分布式锁失败!");

}

}catch (Exception e){

e.printStackTrace();

throw e;

}finally {

mutex.release();

}

return result;

}

测试结果:

并不会出现重复注册情况了。

总结

防重提交不能全部交给数据库

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Prometheus remote_write InfluxDB,unable to parse authentication credentials,authorization failed
下一篇:关于 SAP UI5 getSAPLogonLanguage is not a function 的错误消息以及 API 版本的讨论
相关文章

 发表评论

暂时没有评论,来抢沙发吧~