分布式锁框架Lock4j简单使用

最近项目中使用到了Lock4j的分布式锁组件,小编今天就带大家学习一下该框架,以及如何在我们项目中进行集成使用。

一、简介

Lock4j是一个分布式锁组件,它提供了多种不同的支持以满足不同性能和环境的需求;它基于Spring AOP,支持RedisTemplateRedissonZookeeper作为底层。

二、开源地址

https://gitee.com/baomidou/lock4j

Lock4j框架源码目录结构

关于具体实现源码分析,小编这里就不带大家介绍了~

三、特性

  • 简单易用,功能强大,扩展性强。

  • 支持redissionredisTemplatezookeeper。可混用,支持扩展。

四、Lock4j注解介绍

@Lock4j注解共有六个属性:

属性说明
name需要锁住的key名称
executorlock 执行器,可以使用该参数自定义设置
keys需要锁住的keys名称,可以是多个
expire锁过期时间,主要是用来防止死锁
acquireTimeout可以理解为排队等待时长,超过这个时长就退出排队,并排除获取锁超时异常
autoRelease是否自动释放锁,默认为true
public @interface Lock4j {

    /**
     * 用于多个方法锁同一把锁 可以理解为锁资源名称 为空则会使用 包名+类名+方法名
     *
     * @return 名称
     */
    String name() default "";

    /**
     * @return lock 执行器
     */
    Class<? extends LockExecutor> executor() default LockExecutor.class;

    /**
     * support SPEL expresion 锁的key = name + keys
     *
     * @return KEY
     */
    String[] keys() default "";

    /**
     * @return 过期时间 单位:毫秒
     * <pre>
     *     过期时间一定是要长于业务的执行时间. 未设置则为默认时间30秒 默认值:{@link Lock4jProperties#expire}
     * </pre>
     */
    long expire() default -1;

    /**
     * @return 获取锁超时时间 单位:毫秒
     * <pre>
     *     结合业务,建议该时间不宜设置过长,特别在并发高的情况下. 未设置则为默认时间3秒 默认值:{@link Lock4jProperties#acquireTimeout}
     * </pre>
     */
    long acquireTimeout() default -1;

    /**
     * 业务方法执行完后(方法内抛异常也算执行完)自动释放锁,如果为false,锁将不会自动释放直至到达过期时间才释放 {@link com.baomidou.lock.annotation.Lock4j#expire()}
     *
     * @return 是否自动释放锁
     */
    boolean autoRelease() default true;

}

五、简单使用

5.1、引入相关依赖

pom.xml

<!-- 引入Lock4j依赖 -->
<!-- 若使用redisTemplate作为分布式锁底层,则需要引入 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
    <version>2.2.4</version>
</dependency>
<!-- 若使用redisson作为分布式锁底层,则需要引入 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
    <version>2.2.4</version>
</dependency>

5.2、添加redis相关配置

application.yml

spring:
  redis:
    database: 1
    # Redis服务器地址 写你的ip
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password:
    # 连接池最大连接数(使用负值表示没有限制  类似于mysql的连接池
    jedis:
      pool:
        max-active: 200
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 表示连接池的链接拿完了 现在去申请需要等待的时间
        max-wait: -1
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池中的最小空闲连接
        min-idle: 0
    # 连接超时时间(毫秒) 去链接redis服务端
    timeout: 6000

5.3、简单使用

LockController.java

@RestController
@RequestMapping("/lock")
public class LockController {

    @Autowired
    private LockService lockService;

    /**
     * @param key
     * @return
     */
    @GetMapping
    @Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 6000)
    @SneakyThrows
    public R testLock(@RequestParam String key) {
        Thread.sleep(3000);
        return R.success(key);
    }
}

浏览器地址栏访问:http://localhost:8080/lock?key=xxkfz

{"code":200,"status":true,"message":"xxkfz","data":null}

我们在浏览器连续快速访问上述地址,模拟抢占不到锁得场景:

抢占不到锁的情况默认将会抛出:com.baomidou.lock.exception.LockFailureException: request failed,please retry it.异常。

如下图所示:

默认的锁获取失败策略为 com.baomidou.lock.DefaultLockFailureStrategy.

我们也可以自己定义抢占锁失败执行策略:

我们只需要创建一个类实现LockFailureStrategy接口重写onLockFailure方法即可。

CustomLockFailureStrategy.java

@Component
@Slf4j
public class CustomLockFailureStrategy implements LockFailureStrategy {

    /**
     * 锁失败事件
     *
     * @param key
     * @param method
     * @param arguments
     */
    @Override
    public void onLockFailure(String key, Method method, Object[] arguments) {
        log.error("抢占锁失败:{}==={}", key, method.getName());
    }
}

此时,抢占锁失败将会输出以下日志:

2023-12-03T15:13:33.455+08:00 ERROR 18952 --- [nio-8080-exec-3] c.x.s.config.CustomLockFailureStrategy   : 抢占锁失败:lock4j:com.xxkfz.simplememory.controller.LockControllertestLock#xxkfz===testLock
2023-12-03T15:13:38.468+08:00 ERROR 18952 --- [nio-8080-exec-4] c.x.s.config.CustomLockFailureStrategy   : 抢占锁失败:lock4j:com.xxkfz.simplememory.controller.LockControllertestLock#xxkfz===testLock

六、高级使用

6.1、自定义执行器

创建类CustomRedissonLockExecutor.java继承抽象类AbstractLockExecutor,通过源码可以得知,AbstractLockExecutor抽象类实现了LockExecutor接口,我们重写acquirereleaseLock方法即可。

CustomRedissonLockExecutor.java

@Component
public class CustomRedissonLockExecutor extends AbstractLockExecutor {

    /**
     * 加锁
     *
     * @param lockKey        锁标识
     * @param lockValue      锁值
     * @param expire         锁有效时间
     * @param acquireTimeout 获取锁超时时间
     * @return 锁信息
     */
    @Override
    public Object acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        return null;
    }

    /**
     * 解锁
     *
     * <pre>
     * 为何解锁需要校验lockValue
     * 客户端A加锁,一段时间之后客户端A解锁,在执行releaseLock之前,锁突然过期了。
     * 此时客户端B尝试加锁成功,然后客户端A再执行releaseLock方法,则将客户端B的锁给解除了。
     * </pre>
     *
     * @param key          加锁key
     * @param value        加锁value
     * @param lockInstance 锁实例
     * @return 是否释放成功
     */
    @Override
    public boolean releaseLock(String key, String value, Object lockInstance) {
        return false;
    }
}

6.2、自定义锁key生成器

默认的锁key生成器为 com.baomidou.lock.DefaultLockKeyBuilder ,我们创建类CustomKeyBuilder继承DefaultLockKeyBuilder重写buildKey方法,自己实现key生成逻辑即可。

CustomKeyBuilder.java

@Component
@Slf4j
public class CustomKeyBuilder extends DefaultLockKeyBuilder {


    public CustomKeyBuilder(BeanFactory beanFactory) {
        super(beanFactory);
    }

    @Override
    public String buildKey(MethodInvocation invocation, String[] definitionKeys) {
        return super.buildKey(invocation, definitionKeys);
    }
}

6.3、手动上锁解锁

LockService.java

@Service
public class LockService {
    
    @Autowired
    private LockTemplate lockTemplate;

   public void lock(String key) {
        // 获取锁
        LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
        if (Objects.isNull(lockInfo)) {
            throw new RuntimeException("业务处理中,请稍后再试......");
        }
        // 获取锁成功,处理业务
        try {
            // 处理业务逻辑 
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }
    }
}

分布式锁框架Lock4j简单使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/214371.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL Server数据库数据文件的迁移

SQL Server数据库数据文件的迁移 如何将一台电脑中的SQL Server数据库数据文件迁移到另一台电脑上&#xff1f; 一、首先查看数据库文件保存在电脑中的位置&#xff1b; 如下图所示&#xff1a;右键-》属性-》数据库设置&#xff1b;可以找到数据库文件保存位置&#xff1b; …

【Node.js】Node.js环境下载与安装教程(Windows系统)

前言 Node.js是一个基于Chrome V8引擎的JavaScript运行时环境&#xff0c;可以让你使用JavaScript进行服务器端编程。本教程将向你展示如何在Windows系统上下载和安装Node.js环境。 下载 首先&#xff0c;你需要下载Node.js环境。 打开Node.js官方网站&#xff1a;https://no…

Linux系列-1 Linux启动流程——init与systemd进程

背景&#xff1a; 最近对所有项目完成了一个切换&#xff0c;服务管理方式由&#xff1a; init-> systemd。对相关知识进行总结一下。 1.启动流程 服务器的整体启动流程如下图所示&#xff1a; POST&#xff1a; 计算机通电后进行POST( Power-On Self-Test )加电自检&am…

力扣每日一题day26[42. 接雨水]

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] …

MQ - KAFKA 基础篇

##1、KAFKA的核心组件/API Producer API&#xff0c;它允许应用程序向一个或多个 topics 上发送消息记录 Consumer API&#xff0c;允许应用程序订阅一个或多个 topics 并处理为其生成的记录流 Streams API&#xff0c;它允许应用程序作为流处理器&#xff0c;从一个或多个主…

github问题解决(持续更新中)

1、ssh: connect to host github.com port 22: Connection refused 从.ssh文件夹中新建文件名为config&#xff0c;内容为&#xff1a; Host github.com Hostname ssh.github.com Port 4432、解决 git 多用户提交切换问题 使用系统命令ssh创建rsa公私秘钥 C:\Users\fyp01&g…

使用Redis构建简易社交网站(1)-创建用户与动态界面

目的 本文目的&#xff1a;实现简易社交网站中创建新用户和创建新动态功能。&#xff08;完整代码附在文章末尾&#xff09; 相关知识 本文将教会你掌握&#xff1a;1.redis基本命令&#xff0c;2.python基本命令。 redis基本命令 hget&#xff1a;从哈希中获取指定域的值…

vitepress的使用

创建项目并启动项目 // 1.创建项目,直接在空项目下安装vitepress(npm/yarn等都可以,这个可以看官网,官网给了好几种安装方式) yarn add -D vitepress // 2.初始化配置项目(npm/官网也给了多种包管理工具的安装方式) npx vitepress init // 初始化命令执行完会遇到以下几个问题…

Python---函数递归---练习:猴子吃桃问题(本文以递归算法 解法为主)

相关链接&#xff1a;Python---函数递归---练习&#xff1a;斐波那契数列&#xff08;本文以递归算法为主&#xff09;-CSDN博客 案例&#xff1a;猴子吃桃问题 猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。…

Web前端监控的方案

Web前端监控的方案 前端监控是一个非常重要的话题&#xff0c;对于业务的发展意义重大&#xff0c;就像遍布在城市各处的探头&#xff0c;实时监测整座城市的运行状况&#xff0c;保证系统的稳定、高效运行。 前端监控的意义 前端监控&#xff0c;对于业务和团队的重要性&am…

Kafka 的起源和背景

Apache Kafka 是一个分布式流处理平台&#xff0c;被广泛用于构建实时数据流应用程序和大数据处理系统。本文将深入探讨 Kafka 的起源、设计原则以及它在大数据领域中的重要作用。 大数据和实时数据处理背景 在大数据时代&#xff0c;处理海量数据和实时数据成为了一项关键挑…

C++学习之路(十七)C++ 用Qt5实现一个工具箱(增加托盘图标并且增加显示和退出菜单)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《为屏幕颜色提取功能增加一个点击复制的功能》功能。今天我们增加一个比较正式点的功能&#xff0c;就是增加托盘图标并且增加显示和退出菜单&#xff08;越来越像回事了吧 &#x1f601; &#xff09;。下面我们就来…

Python---函数递归---练习:使用递归求N的阶乘(如n=100)(本文以递归算法 解法为主)

相关链接&#xff1a;Python---函数递归---练习&#xff1a;斐波那契数列&#xff08;本文以递归算法为主&#xff09;-CSDN博客 Python---if选择判断结构、嵌套结构&#xff08;if elif else&#xff09;_python多重if嵌套-CSDN博客 案例&#xff1a;使用递归求N的阶乘&…

RabbitMQ架构是什么样的

publisher 生产者&#xff0c;发送消息的一方。 consumer 消费者&#xff0c;消费消息的一方。 queue 队列&#xff0c;存储消息。生产者投递的消息会暂存在消息队列中&#xff0c;等待消费者处理。 exchange 交换机&#xff0c;负责消息路由&#xff0c;生产者发送的消息由交换…

ORA-00257: archiver error. Connect internal only, until freed……

今天给客户测 试问题&#xff0c;让客户把数据发过来了。解压缩后一看&#xff0c;他们还是用的oracle 815版本的(他们exp导出时&#xff0c;带了导出日志&#xff0c;从导出日志中看出来是oracle 815版本的)&#xff0c;不过没有关系&#xff0c;低版本的exp是可以用高版本的i…

C语言扫雷小游戏

以下是一个简单的C语言扫雷小游戏的示例代码&#xff1a; #include <stdio.h>#include <stdlib.h>#include <time.h>#define BOARD_SIZE 10#define NUM_MINES 10int main() { int board[BOARD_SIZE][BOARD_SIZE]; int num_flags, num_clicks; int …

vmware虚拟机17 安装macos14过程及问题处理亲测

前期准备 1、可引导可虚拟机安装的macOS Sonoma 14 ISO镜像安装文件 我找到得地址&#xff0c;下载自行解决啦 2、VMware虚拟机应用软件 官网下载就好&#xff0c;搜个码搞定 3、解锁工具macOS Unlocker 开始安装&#xff1a; 1、打开VMware软件&#xff0c;新建一个系统…

Sakila数据库和World数据库

Sakila数据库和World数据库 安装MySQL8.2的时候多出两个样例数据库 Sakila数据库和World数据库 Sakila数据库是一个关于DVD租赁的样例数据库&#xff0c;用于展示MySQL的各种功能和特性。Sakila数据库中包含了多个表&#xff0c;包括电影、演员、客户、租赁记录等&#xff0c;可…

AR助推制造业智能转型:实时远程协作与可视化引领生产创新

制造商面临着多方面的变革&#xff0c;技术的兴起催生了工业物联网&#xff08;IIoT&#xff09;&#xff0c;改变了现代工厂的外貌、系统和流程。同时&#xff0c;全球竞争压力和不断变化的员工队伍要求采用新的员工培训方法&#xff0c;并重新审视工人在工厂中的角色。尽管如…

GPT实现开放式世界游戏实践【生化危机】

最近开始研究如何基于GPT构建一个游戏引擎&#xff0c;于是先从简单的文字游戏开始探索。 从最简单的选择机制、故事机制&#xff0c;完善成一个包括天气、事件、技能、属性、伙伴、建造系统的-生化危机版文字游戏-。 我唯一的体验是&#xff1a;AI游戏&#xff0c;大有可为! …