文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

抛出一个问题

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

正常的代码逻辑

1、注册controller



@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



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、加入分布式锁,问题依旧

分布式锁的实现方式



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值,作为防重提交的判断。不想过多解释了,直接上代码吧。



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则进入注册阶段
            stringRedisTemplate.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.info("{},注册成功",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防重的方式



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;
}

测试结果:

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

总结

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

到此这篇关于分布式锁实例教程之防止重复提交的文章就介绍到这了,更多相关分布式锁防止重复提交内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯