redis分布式锁

2023-09-22 10:29:25

用于用户重复注册, 点击过快,有可能会注册相同的手机号问题。
在这里插入图片描述
给用户手机号枷锁一分钟时间,判断相同的手机号。
判断下面这块代码执行时间是否超过一分钟时间,
不论超没超过 都会释放锁,
下个同样的手机号再次注册,
都得等到代码执行完毕后 (或者是一分钟后)才能进行注册,
防止有两个相同的手机号,两个线程,查询数据库都没存在,而注册了两次,(需要一个线程代码执行完毕后才会增加)

在这里插入图片描述
业务代码

 /**
     * 商户注册
     *
     * @param req
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public BaseMerchantInfoResp merRegister(MerRegisterReq req) {
        log.info("BaseMerchantInfoServiceImpl.merRegister; 商户注册开始; req:{}", JSON.toJSONString(req));
        Optional<RLock> opLock = Optional.empty();
        try {
            opLock = redisLockUtils.tryLock(req.getPhone(), BasicConstants.CACHE_TIME_1, TimeUnit.MINUTES);
            opLock.orElseThrow(() -> new BusinessException(BasicResultCode.OPERATE_FREQUENTLY));
            //1. 判断用户是否存在
            BaseMerchantInfo merchantInfo = baseMerchantInfoMapper.selectByPhone(EncryptUtils.encryptPhoneIdCard(req.getPhone()));
            if (merchantInfo != null) {
                throw new BusinessException(BaseMerchantResultCode.MERCHANT_EXISTS);
            }
            //2. 创建账号
            MerRegisterDTO merRegisterDTO = BeanUtil.copyBean(req, MerRegisterDTO.class);
            PlatformAccountDTO accountDTO = platformAccountExport.merRegister(merRegisterDTO);
            //3. 创建商户
            BaseMerchantInfo baseMerchantInfo = this.buildMerchant(accountDTO);
            //4. 入库
            baseMerchantInfoMapper.insert(baseMerchantInfo);
            log.info("BaseMerchantInfoServiceImpl.merRegister ; 商户创建成功, info:{}", JSON.toJSONString(baseMerchantInfo));
            return BeanUtil.copyBean(baseMerchantInfo, BaseMerchantInfoResp.class);
        } catch (BusinessException bus) {
            throw bus;
        } catch (Exception exception) {
        	//需要抛出异常,否则事务不会生效
           throw exception;
            log.info("BaseMerchantInfoServiceImpl.merRegister");
        } finally {
            redisLockUtils.unLock(opLock);
        }

        return null;
    }

工具类

import com.first.pet.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 描述:
 * redis分布式锁工具类
 * 统一提供两种加锁方法
 * - 阻塞锁,会阻塞到获取锁
 * - 非阻塞锁,未获取到锁会直接返回
 * 解锁请直接调用lock.unLock方法
 *
 * @author zhaofeng
 * @date 2023-08-29
 */
@Component
@Slf4j
public class RedisLockUtils {

    /**
     * redisson客户端
     */
    @Resource
    private RedissonClient redissonClient;

    /**
     * 阻塞获取锁
     * 加锁成功返回optional容器,容器中为Lock对象,加锁失败返回空容器
     *
     * @param key
     * @param time
     * @param timeUnit
     * @return
     */
    public Optional<RLock> lock(String key, long time, TimeUnit timeUnit) {
        RLock lock = redissonClient.getLock(key);
        try {
            lock.lock(time, timeUnit);
            log.info("加锁成功,key:{},time:{},timeUnit:{}", key, time, timeUnit);
            return Optional.of(lock);
        } catch (Exception exception) {
            log.info("加锁失败,key:{}", key, exception);
            return Optional.empty();
        }
    }

    /**
     * 获取锁,非阻塞
     * 加锁成功返回optional容器,容器中为Lock对象,加锁失败返回空容器
     *
     * @param key
     * @param time
     * @param timeUnit
     * @return Optional
     */
    public Optional<RLock> tryLock(String key, long time, TimeUnit timeUnit) {
        RLock lock = redissonClient.getLock(key);
        try {
            boolean lockResult = lock.tryLock(time, timeUnit);
            log.info("加锁结束, key:{},time:{},timeUnit:{},加锁结果:{}", key, time, timeUnit, lockResult);
            return lockResult ? Optional.of(lock) : Optional.empty();
        } catch (Exception exception) {
            log.info("加锁失败,key:{}", key, exception);
            return Optional.empty();
        }
    }

    /**
     * 解锁
     * optional不为空时执行解锁操作
     *
     * @return
     */
    public void unLock(Optional<RLock> optional) throws BusinessException {
        optional.ifPresent(Lock::unlock);
    }
}

依赖

   <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.6</version>
        </dependency>

这辈子坚持与不坚持都不可怕,怕的是独自走在坚持的道路上。。

更多推荐

不断探索创新 促进中国信息技术发展——南京宏控科技有限公司董事长应富忠

应富忠,男,现任南京宏控科技有限公司董事长、电子系统工程高级工程师(技术五级)、自动化系统注册工程师,先后被评为“研究所级青年突击手”、“研究所级先进工作者”、“研究所级优秀共产党员”、“南京市级考级优秀”等荣誉称号,持有江苏省国防工办系统可靠性设计资质证书。南京市专家组成员,2003-2016年。江苏省科技计划项目评

CESM模型教程

详情点击公众号技术科研吧链接:地球系统模式(CESM)教程前言目前通用地球系统模式(CommunityEarthSystemModel,CESM)在研究地球的过去、现在和未来的气候状况中具有越来越普遍的应用。CESM由美国NCAR于2010年07月推出以来,一直受到气候学界的密切关注。近年升级的CESM2.0在大气、陆

数据治理-数据仓库环境

数据仓库环境包括一系列组织起来以满足企业需求的架构组件,从源系统流动到数据暂存区,数据可以在这里被清晰,当数据集成并存储在数据仓库或操作数据存储中时,可以对其进行补充丰富。在数据仓库中,可以通过数据集市或数据立方体访问数据,生成各种各样的报表。其中包括:源系统;(CRM\账务系统或者DaaS服务、网页内容和任何大数据计

Spark

ApacheSpark是一种快速、通用、可扩展的大数据处理引擎,旨在处理大规模数据集并进行高效的数据分析。与HadoopMapReduce相比,Spark具有更高的性能和更丰富的功能,可以处理更复杂的数据处理任务。以下是ApacheSpark的一些基本概念:SparkCore:这是Spark的基本引擎,提供了分布式任务

计算机网络的分层体系结构

为什么要分层面对一个复杂系统时,将其划分成小的,功能独立的模块或子系统,能更好的解决问题付诸应用。有哪些好处各层之间是独立的灵活性好结构上可以分开易于实现和维护有利于功能复用能促进标准化工作计算机网络体系结构经历的阶段计算机网络分层体系结构的发展经历了以下主要阶段:早期的分层概念(1970s):分层体系结构的思想最早出

2023.9.11 关于传输层协议 UDP和TCP 详解

目录UDP协议TCP协议TCP十大核心机制确认应答超时重传连接管理(三次握手四次挥手)滑动窗口流量控制拥塞控制延时应答捎带应答面向字节流粘包问题TCP中的异常处理经典面试题对比TCP和UDP如何使用UDP实现可靠传输UDP协议源端口:指发送方的端口号目的端口:指接收方的端口号注意:端口号都是用两个字节来表示的,也就是1

前端开发学习指南

这篇文章很长,但的确是一篇非常干的干货,讲诉了HTML、JavaScript、CSS、jQuery使用的一些规范与建议,前端的同学可以认真阅读此文,并比较自己平时的一些习惯,看是否有改进的地方……HTML咋地了,DOCTYPE?不定义DOCTYPE是一种可以被判死刑的罪行。以前你可能用的是下面的DOCTYPE,不过你要

提前放电避雷针防雷综合应用方案

放电避雷针是一种利用电离空气提前放电的避雷装置,可以有效地保护建筑物、设备和人员免受雷电的危害。放电避雷针有多种类型,根据其放电机理和结构特点,可以分为以下几类:地凯科技预放电避雷针:这种避雷针利用雷云产生的空间电场强度,预先使周围的空气电离,空气离子在空间电场的作用下加速接近雷云,从而使迎面先导大大提前与雷云的下行先

Raft协议

前言最近在系统的实战springcloud,在学习nacos过程,我们知道nacos的底层用到了Raft协议。在Raft协议之前是一个paxos的协议,但是这个协议有点复杂,于是Stanford的两位教授决定设计一种较简单的一致性算法。参考自Raft动态演示网站我们一起来学习一下Raft协议。Raft前置概念term:

【CSS3】CSS3 3D 转换 ④ ( 3D 旋转 rotate3d | rotate3d 语法 | rotate3d 自定义轴旋转 | 元素旋转方向 - 左手准则 | 代码示例 )

文章目录一、3D旋转rotate3d1、rotate3d语法2、rotate3d自定义轴旋转3、元素旋转方向-左手准则二、代码示例1、代码示例2、执行结果一、3D旋转rotate3d3D旋转指的是在三维空间坐标系中,绕X轴,Y轴,Z轴进行旋转,同时还可以绕自定义轴进行旋转;2D旋转只能以某个点为中心进行旋转,3D旋转可

eSIM简介

翻译自eSIMeSIMeSIM被称为许多不同的名字。有人说嵌入式SIM,有人说电子SIM和其他的说eSIM。在行业标准最终确定并在该地区广泛采用之前,还会有其他名称出现。无论我们称之为什么,此SIM的基本思想是将SIM(UICC)作为用户设备(例如,M2M终端,移动电话等)的不可移除/固定组件分发。设备制造商对SIM卡

热文推荐