redisTemplate代替jedis实现分布式锁

springBoot 同时被 3 个专栏收录
18 篇文章 0 订阅
67 篇文章 0 订阅
42 篇文章 0 订阅

引言:

springBoot集成redis之后,再使用之前的jedis,会加上诸多配置和注解,很烦恼,因此,自己研究了两天,使用redisTemplate实现分布式锁。

1.锁工具类

/**
 * 分布式事务锁工具类
 *
 * @author leimin
 * @description
 * @time: 2019/09/20
 **/
@Component
@Slf4j
public class DistributeLockUtil {

    /**
     * 引入redisTemplate
     */
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 超时时间,8s
     */
    private static final long HANDLE_MSECS = 8000L;
    /**
     * 重复获取锁的时间间隔 50毫秒
     */
    private static final long TRY_INTERVAL = 50L;
    /**
     * 重复获取锁的超时时间 10s
     */
    private static final long TIME_OUT_MSECS = 10000L;

    /**
     * 获取锁
     *
     * @param lockName 锁的名称
     * @return 释放锁的时间的时间戳
     * @throws InterruptedException 中断异常
     */
    public Long getLock(String lockName) {

        long waitTime = System.currentTimeMillis() + TIME_OUT_MSECS;
        long lockTimeout;
        Object obj = redisTemplate.opsForValue().get(lockName);
        long expire = obj == null ? 0L : (Long) obj;

        // 时间段内,循环获取锁
        while (System.currentTimeMillis() < waitTime) {
            lockTimeout = System.currentTimeMillis() + HANDLE_MSECS + 1L;

            // 获取到锁后,设置过期时间
            if (redisTemplate.opsForValue().setIfAbsent(lockName, lockTimeout)) {
                redisTemplate.expire(lockName, HANDLE_MSECS, TimeUnit.MILLISECONDS);
                return lockTimeout;

                // 发现死锁,重置锁的过期时间
            } else if (expire < System.currentTimeMillis()) {
                redisTemplate.opsForValue().set(lockName, lockTimeout, HANDLE_MSECS, TimeUnit.MILLISECONDS);

                // 重置后,操作时间小于获取时间,允许等待操作时间
                // 操作时间大于获取时间,肯定获取不到,直接结束;
                if (HANDLE_MSECS > TIME_OUT_MSECS) {
                    return 0L;
                }
            }

            try {
                Thread.sleep(TRY_INTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return 0L;
    }

    /**
     * 释放锁的情况:
     *
     * 1.业务执行过程中,发生异常必须释放锁;(调用释放方法)
     * 2.业务执行完成,必须释放锁;(调用释放方法)
     * 3.发生死锁,必须释放锁;(超时设置)
     * 4.异步处理导致主程序过早释放锁;(取消finally,所有代码跑完才释放锁)
     */

    /**
     * 同步释放锁
     *
     * @param lockName    锁的名称
     * @param lockTimeout 超时时间
     */
    public void releaseLock(String lockName, long lockTimeout) {

        // 获取锁的释放时间, 如果锁的释放时间和定义的释放时间一致,那么释放锁
        Object lockTime = redisTemplate.opsForValue().get(lockName);
        long currentLockTime = lockTime == null ? 0L : (long) lockTime;

        if (lockTimeout != 0L && currentLockTime == lockTimeout) {
            redisTemplate.opsForValue().getOperations().delete(lockName);
        }
    }

    /**
     * 异步释放锁
     *
     * @param lockName 锁的名称
     */
    public void releaseLock(String lockName) {

        redisTemplate.opsForValue().getOperations().delete(lockName);
    }
}

2.使用锁

我的业务流程是异步的,因此,释放锁,必须在异步操作的最后

2.1 主业务

    /**
     * 表单数据,新增审批
     *
     * @param formId     表单id
     * @param grantId    操作员id
     * @param formDataId 表单中数据id
     * @param tenantId   企业id
     * @return 分装结果对象
     * @author leimin
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BaseVO formCreateApproval(Long formId, Long grantId, Long formDataId, Long tenantId) {

        long lockTimeOut = 0L;
        try {
            lockTimeOut = distributeLockUtil.getLock(FormConstants.CREATE_APPROVAL_LOCK + tenantId + formDataId);
        } catch (Exception e) {
            return new BaseVO(BaseVO.CONFLICT_CODE, "对不起,目前系统繁忙,请稍后重试");
        }
        if (lockTimeOut == 0) {
            return new BaseVO(BaseVO.CONFLICT_CODE, "对不起,目前系统繁忙,请稍后重试");
        }

        try {
            FormData formData = formDao.getByFormDataId(formDataId, tenantId);
            if (!auth.get(FormDataState.APPROVAL_CREATE.toString(), grantId.equals(formData.getCreator()), formData.getStatus())
                    || !formData.getEditor().equals(grantId)) {
                return new BaseVO(BaseVO.UNAUTHORIZED_CODE, "无权限", null);
            }

            //通过权限审批,修改fromData中的数据,approval_create;插入formDataApproval对象,approving;
            setFormDataStatusAndIndex(formData, FormDataState.APPROVAL_CREATE);
            FormDataApproval formDataApproval = createFormDataApproval(formData, FormDataApprovalState.APPROVING);
            formDao.updateFormDataStatus(formData);
            formApprovalDao.upsertFormDataApproval(formDataApproval);

            //日志记录准备数据 jsonObject.toJSONString()
            loggerUtil.saveLog(tenantId, grantId, formData, FormConstants.CREATE);

            //调用工作流中提交审批接口
            return audit.audit(formData, formId, grantId, formDataId, tenantId, FormConstants.CREATE, formDataApproval.getId());
        } catch (Exception e) {
            distributeLockUtil.releaseLock(FormConstants.CREATE_APPROVAL_LOCK + tenantId + formDataId, lockTimeOut);
            return new BaseVO(BaseVO.OTHER_CODE, "操作失败!");
        }
    }

2.2异步业务

    /**
     * 审批完成后,回调接口,修改源数据
     *
     * @param tenantId 企业id
     * @param formId   表单id
     * @param dataId   数据id
     * @param result   审核结果
     * @param type     审核类型
     * @return 返回的结果
     * @author niulibing
     */
    @Override
    public BaseVO callback(Long tenantId, Long formId, Long dataId, String result, String type) {

        try {
            //根据dataId查询formDataApproval表的数据,查询状态为审核中的状态的数据
            FormDataApproval formDataApproval = formApprovalDao.findOneByDataId(tenantId, dataId);

            if (formDataApproval == null) {
                return new BaseVO(BaseVO.OTHER_CODE, "不存在该条记录");
            }

            //审核类型:create
            switch (type) {

                //审核类型:create
                case FormConstants.CREATE:
                    return createApprovalCallBack(tenantId, formId, dataId, result, formDataApproval);

                //审核类型:edit
                case FormConstants.EDIT:
                    return editApprovalCallBack(tenantId, formId, dataId, result, formDataApproval);

                //审核类型:delete
                case FormConstants.DELETE:
                    return deleteApprovalCallBack(tenantId, formId, dataId, result, formDataApproval);
                default:
                    return new BaseVO(BaseVO.OTHER_CODE, "审核方式不存在,仅支持create、edit、delete,请确认后提交。");
            }
        } finally {
            switch (type) {
                case FormConstants.CREATE:
                    distributeLockUtil.releaseLock(FormConstants.CREATE_APPROVAL_LOCK + tenantId + dataId);
                    break;
                case FormConstants.EDIT:
                    distributeLockUtil.releaseLock(FormConstants.EDIT_APPROVAL_LOCK + tenantId + dataId);
                    break;
                default:

            }
        }

    }

redisTemplate相关方法参考:https://www.cnblogs.com/yanan7890/p/6617305.html

  • 1
    点赞
  • 0
    评论
  • 8
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值