首页 > 数据库 >基于Redission的分布式锁

基于Redission的分布式锁

时间:2023-08-23 17:47:25浏览次数:43  
标签:基于 加锁 return timeOut default Redission lockAction key 分布式

分布式锁的设计共分为3步

  1. 定义注解
  2. 对注解进行扫描
  3. 使用注解

加锁核心逻辑为
RLock rLock = redissonClient.getLock(key);
//是否加锁成功
boolean isLock = rLock.tryLock(timeOut, expireTime, timeUnit);

1.定义注解 LockAction

package com.jwds.app.compont.cache.annotation;

import com.jwds.app.compont.cache.service.lock.AppLockService;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * 同步锁注解
 *
 * @author by liang.zhang
 * @Description
 * @Date 2021/9/7
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface LockAction {

  /**
   * 锁的资源,key。支持spring El表达式
   * 例如:#user.id
   */
  String value() default "'default'";

  /**
   * 等待时间 在等待时间内将会一直等待
   * 默认-1  不进行等待,获取失败直接返回结果
   * @return
   */
  long timeOut() default AppLockService.TIME_OUT;

  /**
   * 获取锁的等待时间单位 超过这个时间就获取失败
   *
   * @return
   */
  TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

  /**
   * 过期时间 默认30分钟,过期后自动删除key  -1永远不过期
   *
   * @return
   */
  long expireTime() default AppLockService.EXPIRE_TIME;

  /**
   * 获取锁失败时的重试次数 默认为0
   * 当获取锁失败时,会进行锁的重新获取
   */
  int retryTimes() default AppLockService.RETRY_TIMES;

  /**
   * 获取锁失败时的重试的间隔时间 单位毫秒
   */
  long sleepMills() default AppLockService.RETRY_SLEEP_MILLIS;

  /**
   * 加锁失败的回调方法 参数为方法的入参
   * 回调进入该方法,
   * @return
   */
  String fallbackMethod() default "";

  /**
   * 未拿到锁 提示消息
   *
   * @return
   */
  String errorMsg() default "操作过于频繁,请稍后再试!";
}

2. 对注解进行切面处理

//主要注意加锁失败后的回调方法

@Around("lockPoint()")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {

    MethodSignature signature = (MethodSignature) pjp.getSignature();
    Method method = signature.getMethod();
    LockAction lockAction = method.getAnnotation(LockAction.class);
    String key = lockAction.value();
    Object[] args = pjp.getArgs();
    //系统编码+key 组成唯一key
    //NameSpace命名空间
    key = "RedisLock" + AppConstant.COLON + parse(key, method, args);

    //重试次数
    int retryTimes = lockAction.retryTimes();
    boolean lock = lockService
        .lock(key, lockAction.timeOut(), lockAction.expireTime(), lockAction.timeUnit(), retryTimes,
            lockAction.sleepMills());
    //加锁失败抛出异常 如果存在回调方法则不抛出异常,由业务自己去处理
    if (!lock) {
      //回调方法存在的时候,调用
      if (null != lockAction.fallbackMethod() && !lockAction.fallbackMethod().trim().equals("")) {
        log.error("get lock failed : {}进入失败回调方法", key);
        //获取方法所在的类(controller)
        Class<?> beanType = signature.getDeclaringType();
        Object object = beanType.newInstance();

        //参数转变
        Class<?>[] params = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
          Object o = args[i];
          params[i] = o.getClass();
        }
        try {
          //回调失败方法
          Method infoMethod = beanType.getMethod(lockAction.fallbackMethod(), params);
          if (infoMethod==null){
            throw new RuntimeException("回调方法:"+lockAction.fallbackMethod()+"不存在或为private的");
          }
          infoMethod.invoke(object, args);
        } catch (Exception e) {
          log.error("调用加锁失败回调方法{}异常", lockAction.fallbackMethod(), e);
          throw new LockException(lockAction.errorMsg());
        }
      } else {
        log.error("get lock failed : {}没有失败回调方法", key);
        throw new LockException(lockAction.errorMsg());
      }
    }

 //具体的锁代码
 @Override
  public boolean lock(String key, long timeOut, long expireTime, TimeUnit timeUnit, int retryTimes,
      long retrySleepMillis) {
    //增加系统编码
    key = appCacheProperties.getSysCode() + AppConstant.COLON + key;

    boolean result = setRedis(key, timeOut, expireTime, timeUnit);
    // 如果获取锁失败,按照传入的重试次数进行重试
    while ((!result) && retryTimes-- > 0) {
      try {
        log.debug("lock failed, retrying..." + retryTimes);
        Thread.sleep(retrySleepMillis);
      } catch (InterruptedException e) {
        return false;
      }
      result = setRedis(key, timeOut, expireTime, timeUnit);
    }
    return result;
  }
  
 //加锁的核心代码
  /**
   * @param key
   * @param timeOut    超时时间
   * @param expireTime 过期时间
   * @param timeUnit   单位
   * @return
   */
  private boolean setRedis(String key, long timeOut, long expireTime, TimeUnit timeUnit) {
    RLock rLock = redissonClient.getLock(key);
    log.debug("原始传入上锁时间:" + timeOut);
    //是否加锁成功
    boolean isLock = false;
    try {
      //尝试加锁
      isLock = rLock.tryLock(timeOut, expireTime, timeUnit);
    } catch (Exception e) {
      isLock = false;
      log.error("===>{}操作Redis失败:{}", key, e.getMessage());
      throw new RuntimeException(e.getMessage());
    }
    log.debug("取得加锁状态:" + isLock + ",最终超时间:" + timeOut);
    return isLock;
  }

3.注解使用

其中value 支持EL表达式
@LockAction(value = "#reqDto.queryCondition.id", fallbackMethod = "fallbackMethod")

也可以手动调用代码 进行加锁和解锁操作
AppLockService.lock();

标签:基于,加锁,return,timeOut,default,Redission,lockAction,key,分布式
From: https://www.cnblogs.com/hopeway-shaon/p/17652348.html

相关文章

  • 基于mysql的异步事件框架的设计&实现
    背景       事件驱动模型编程是程序设计中经常会用到的方法技巧,本质上是为了解耦事件的发布者和订阅者,实现组件之间的松耦合,提高应用程序的扩展性;另外,在一些业务场景中,顺序、阻塞式的执行任务会遇到一些比较耗时的中间步骤,但是往往我们不希望整个流程都停下来等待这些中间......
  • 基于机器视觉工具箱的车辆检测计数算法matlab仿真
    1.算法理论概述1.1、研究背景      随着城市化进程的加速和汽车保有量的增加,交通拥堵和交通事故等交通问题日益突出,如何对城市交通进行有效管理和调控成为了城市交通管理的重要任务。车辆检测计数是交通管理中的一个重要问题,它可以用于交通状况的监测、交通流量的统计以......
  • m基于FPGA的高斯白噪声信道模拟系统verilog实现,包含testbench,可以配置不同的SNR和频
    1.算法仿真效果vivado2019.2仿真结果如下:SNR=0db,无频偏SNR=5db,无频偏SNR=25db,无频偏SNR=45db,带频偏2.算法涉及理论知识概要高斯白噪声信道在通信系统中具有重要意义,模拟此类信道有助于评估系统性能。本文提出的FPGA实现系统可以灵活地模拟不同信道条件,为通信系统的设计......
  • 真香!基于 Prometheus 的持久化存储,全是知识点
    Prometheus将基于告警规则生成的告警存储为时间序列,不会将Alertmanager的告警信息持久化存储,那么针对历史告警的检索、统计等需求就无法实现。因此需要一种持久化机制用于存储历史告警信息,本文主要探究基于alertmanager告警的开源持久化方案。1.告警触发机制基于主机层面内存......
  • m基于FPGA的高斯白噪声信道模拟系统verilog实现,包含testbench,可以配置不同的SNR和频
    1.算法仿真效果vivado2019.2仿真结果如下:   SNR=0db,无频偏   SNR=5db,无频偏   SNR=25db,无频偏   SNR=45db,带频偏   2.算法涉及理论知识概要       高斯白噪声信道在通信系统中具有重要意义,模拟此类信道有助于评估系统性能。本......
  • GIS开发与应用(PostgreSQL空间数据库各种查询语句范例以及SQL语句查询空间关系)_postgre
    实验二PG空间数据库应用实验目的:实验准备实验内容及要求实验过程及步骤:1、创建空间数据库nyc,在nyc空间数据库中创建geometries表,对表中插入Point、Linestring、Polygon、PolygonWithHole、collection等几何要素。2、查看geometries表中的几何图形的元数据。使用`ST_G......
  • 基于 Vercel & TiDB Serverless 的 chatbot
    作者:shiyuhang0#前言TiDBServerless去年就有和Vercel的集成了,同时还有一个bookstoretemplate方便大家体验。但个人感觉bookstore不够炫酷,借2023TiDBhackthon的机会,我搞了个maskchatbot,你可以在maskchatbot上选定角色,基于此生成prompt来更好的使用ChatGPT......
  • 分布式可视化 DAG 任务调度系统 Taier 的整体流程分析
    Taier作为袋鼠云的开源项目之一,是一个分布式可视化的DAG任务调度系统。旨在降低ETL开发成本,提高大数据平台稳定性,让大数据开发人员可以在Taier直接进行业务逻辑的开发,而不用关心任务错综复杂的依赖关系与底层的大数据平台的架构实现,将工作的重心更多地聚焦在业务之中。本文......
  • 基于ssm的动漫推荐平台系统设计-计算机毕业设计源码+LW文档
    摘要随着信息技术的发展,基于web模式的管理系统逐渐普及,网上查找信息是目前广受欢迎的模式。基于ssm的动漫推荐平台系统可以适应现代化快节奏的生活方式,满足各类人群足不出户的在线查找动漫,利用基于ssm的动漫推荐平台系统可以获取动漫的信息,并可以和其他用户进行交流,提高了动漫的......
  • 基于Springboot的个人网站的设计与实现-计算机毕业设计源码+LW文档
    一、设计(论文)选题的依据1.研究背景与意义现在越来越多的人关注网站的自动化设计与开发,什么是个人网站呢?它的出现和运营究竟承载这怎样的信息?这并不是每个人都清楚的很多人无法准确的理解个人网站的优势和作用,我对网站的认识还处于相当低的程度中所以在正文开始前我想先阐述自己对......