Redis 分布式锁 @Klock 注解详解及使用教程

文章目录

  • 一、作用
  • 二、参数
  • 三、锁的流程
  • 四、SpringBoot 集成
      • 1. pom 依赖
      • 2. yaml 配置
      • 3. 使用方式
  • 五、变量级锁和方法级锁
  • 六、常见应用场景
      • 1. 页面重复提交
      • 2. 定时任务
      • 3. 核心业务
  • 七、锁的粒度与锁的时间


一、作用

注解 @klock 是基于 Redis 的分布式锁,作用在分布式事务中避免并发冲突。只作用于方法上。

二、参数

  1. name:锁的名称。锁唯一标识。

  2. keys:加锁 id。加锁对象的唯一标识。

  3. lockType:锁类型。默认可重入锁。

     LockType.Reentrant 可重入锁。默认。LockType.Fair 公平锁。LockType.Reentrant 读锁。LockType.Reentrant 写锁。
    
  4. waitTime:获取锁最长等待时间。单位秒。

  5. leaseTime:获取到锁后,自动释放锁的时间。

  6. lockTimeoutStrategy:获取锁超时的处理策略。默认 NO_OPERATION 继续执行业务逻辑,不做任何处理。

     NO_OPERATION:不做处理,不加锁继续执行业务逻辑。(默认)FAIL_FAST:快速失败,直接抛出获取锁超时异常。KEEP_ACQUIRE:阻塞等待,一直阻塞,直到获得锁,在太多的尝试后,仍会报错。一般下一次获取锁的间隔时间初始为0.1秒、总共尝试次数为10次,下一次获取锁的时间间隔会成倍增长。很可能引起死锁。
    
  7. customLockTimeoutStrategy:自定义加锁超时的处理策略。String 类型,当前类中的方法名。会覆盖 lockTimeoutStrategy 的处理策略。

     自定义处理方法,方法参数必须与加锁的方法参数一致。
    
  8. releaseTimeoutStrategy:占有锁超时的处理策略。默认 NO_OPERATION 继续执行业务逻辑,不做任何处理。

     NO_OPERATION:不做处理,不加锁继续执行业务逻辑。(默认)FAIL_FAST:快速失败,直接抛出获取锁超时异常。
    
  9. customReleaseTimeoutStrategy:自定义释放锁时已超时的处理策略。String 类型,当前类中的方法名。会覆盖 releaseTimeoutStrategy 的处理策略。

     自定义处理方法,方法参数必须与加锁的方法参数一致。
    

三、锁的流程

在这里插入图片描述

四、SpringBoot 集成

1. pom 依赖

<!--klock-->
<dependency><groupId>cn.keking</groupId><artifactId>spring-boot-klock-starter</artifactId><version>1.4-RELEASE</version>
</dependency>

2. yaml 配置

  1. 单节点配置
spring:klock:#单节点地址address: 192.168.0.193:6379#密码,没有密码就不要配置password#password:#获取锁最长阻塞时间(默认:60,单位:秒)wait-time: 20#已获取锁后自动释放时间(默认:60,单位:秒)lease-time: 20
  1. 集群配置
spring:klock:#密码#password:#获取锁最长阻塞时间(默认:60,单位:秒)wait-time: 20#已获取锁后自动释放时间(默认:60,单位:秒)lease-time: 20#集群配置cluster-server:node-addresses: 192.168.0.111:6379,192.168.0.112:6379,192.168.0.113:6379,192.168.0.101:6379,192.168.0.102:6379,192.168.0.103:6379,192.168.0.114:6379,192.168.0.104:6379

3. 使用方式

/*** 根据id查询** @param id 文章id* @return 文章*/
@Override
@Klock(name = "article-hist-lock", // 锁名称keys = "#id", // 加锁idlockType = LockType.Fair, // 锁类型;公平锁waitTime = 10, // 获取锁最长等待时间:10sleaseTime = 300, // 获得锁后,自动释放锁的时间:300slockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST, // 获取锁超时的处理策略:直接返回失败releaseTimeoutStrategy = ReleaseTimeoutStrategy.FAIL_FAST) // 占有锁超时的处理策略:直接返回失败
public CmsArticleVo selectCmsArticleVoByIdForApi(Long id) {// 设置查询文章的内容类型String contentType = CmsConstants.CONTENT_TYPE_ARTICLE;CmsArticleVo cmsArticleVo = cmsArticleMapper.selectCmsArticleVoById(id, contentType);if (cmsArticleVo != null) {// 文章点击量+1CmsArticle cmsArticle = new CmsArticle();cmsArticle.setId(cmsArticleVo.getId());cmsArticle.setHist(cmsArticleVo.getHist() + 1);cmsArticleMapper.updateById(cmsArticle);}return cmsArticleVo;
}

五、变量级锁和方法级锁

VariableAndMethodLockService

package com.alian.redisklock.service;import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.klock.annotation.Klock;
import org.springframework.boot.autoconfigure.klock.model.LockTimeoutStrategy;
import org.springframework.stereotype.Service;@Slf4j
@Service
public class VariableAndMethodLockService {/*** 变量级加锁* @param userId*/@Klock(keys = "#userId", lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST)public void variableLock(String userId) {log.info("变量级加锁收到的消息:{}", userId);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.info("变量级加锁业务处理完:{}", userId);}/*** 方法级加锁* @param userId*/@Klockpublic void methodLock(String userId) {log.info("方法级加锁收到的消息:{}", userId);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.info("方法级加锁业务处理完:{}", userId);}
}

使用5个线程来进行并发验证:

TestLockTypeService

package com.alian.redisklock.service;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import java.util.concurrent.CountDownLatch;@Slf4j
@Service
public class TestLockTypeService {private final CountDownLatch countDownLatch = new CountDownLatch(1);@Autowiredprivate VariableAndMethodLockService vmService;@PostConstructpublic void testVariableAndMethodLock() {for (int i = 0; i < 5; i++) {int finalI = i;new Thread(() -> {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}//变量级加锁vmService.variableLock("10001_" + finalI);//方法级加锁
//                vmService.methodLock("10002_"+ finalI);}, "Thread" + i).start();}countDownLatch.countDown();}}

变量级加锁结果

2023-09-23 17:42:15 432 [Thread0] INFO :变量级加锁收到的消息:10001_0
2023-09-23 17:42:15 432 [Thread1] INFO :变量级加锁收到的消息:10001_1
2023-09-23 17:42:15 432 [Thread4] INFO :变量级加锁收到的消息:10001_4
2023-09-23 17:42:15 432 [Thread2] INFO :变量级加锁收到的消息:10001_2
2023-09-23 17:42:15 432 [Thread3] INFO :变量级加锁收到的消息:10001_3
2023-09-23 17:42:16 439 [Thread3] INFO :变量级加锁业务处理完:10001_3
2023-09-23 17:42:16 439 [Thread2] INFO :变量级加锁业务处理完:10001_2
2023-09-23 17:42:16 439 [Thread0] INFO :变量级加锁业务处理完:10001_0
2023-09-23 17:42:16 439 [Thread4] INFO :变量级加锁业务处理完:10001_4
2023-09-23 17:42:16 439 [Thread1] INFO :变量级加锁业务处理完:10001_1

方法级加锁结果

2023-09-23 17:43:15 779 [Thread3] INFO :方法级加锁收到的消息:10002_3
2023-09-23 17:43:16 787 [Thread3] INFO :方法级加锁业务处理完:10002_3
2023-09-23 17:43:16 797 [Thread2] INFO :方法级加锁收到的消息:10002_2
2023-09-23 17:43:17 800 [Thread2] INFO :方法级加锁业务处理完:10002_2
2023-09-23 17:43:17 819 [Thread0] INFO :方法级加锁收到的消息:10002_0
2023-09-23 17:43:18 833 [Thread0] INFO :方法级加锁业务处理完:10002_0
2023-09-23 17:43:18 844 [Thread1] INFO :方法级加锁收到的消息:10002_1
2023-09-23 17:43:19 847 [Thread1] INFO :方法级加锁业务处理完:10002_1
2023-09-23 17:43:19 863 [Thread4] INFO :方法级加锁收到的消息:10002_4
2023-09-23 17:43:20 864 [Thread4] INFO :方法级加锁业务处理完:10002_4

结论:

  • 变量级锁,针对的是一个变量,变量不同,获取的锁就不存在先后顺序,都可以获取到自己的锁。
  • 方法级锁,针对的是方法,哪怕请求值不一样,也只能一个线程获取到锁,直到释放后,下一个线程才能获取。
  • 变量级锁的效率更适合高并发场景,而方法级锁可能引起阻塞。

六、常见应用场景

1. 页面重复提交

最常见的就比如手机端录入信息到后台,比如注册之类的等等,用户端可能因为各种原因可能会点击多次,导致后台可能会出现多笔记录的情况,这个时候很简单,用到我们的锁,假设,我们是注册用户,手机号是唯一的。

@Klock(keys = "#phone", lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST)
@RequestMapping("register")
public void register(String phone,String nickName) {log.info("注册账户收到的信息:{},{}", phone,nickName);try {//模拟业务过程Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}log.info("注册账户业务处理完");
}

这个时候,如果是点击了两次,第一次业务进入获取到锁进行处理,第二过来了也是一个等待,要么第一次处理完成,第二次业务判断已注册,要么第二次直接超时了。

2. 定时任务

工作中定时任务使用还是蛮多的,但是也会有很多问题,当遇到分布式服务时,一个服务部署多台,定时任务就可能会同时运行,这种情况怎么处理呢?有些人可能会给两个服务的配置改成不一样,比如定时任务的时间修改,一个正常执行,一个在不可能的时间执行,还有人直接给服务设置一个标志位,只有某个标志位的能执行。好吧,在分布式环境,并且服务不是很多的情况下,也许还能勉强维护,那么如果是容器下呢?所以分布式锁的方案就更加重要了。

@Scheduled(cron = "0 0 2 * * ?")
public void dataCollector(){//开始做任务String dataTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));createDataFile(dataTime);//结束任务
}@Klock(keys = "#dataTime", lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST)
public void createDataFile(String dataTime) {log.info("开始生成对账文件:{}", dataTime);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}log.info("对账文件生成完成:");
}

这是一个示例,实际中,createDataFile方法是另外一个service中的方法,这样不管是单机,分布式多机,还是在容器中都只有一个定时任务在执行,而不会导致重复数据问题。

3. 核心业务

其实这个场景是用的最多的,比如商品库存的扣减,因为我们不能超卖啊。实际工作中,需要根据自己的业务定义特定意义的key就可以了。

@Klock(keys = "#goodId", lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST)
public void deductCommodityInventory(String goodId,int num) {log.info("商品【{}】扣减库存:{}", goodId,num);//扣减库存操作//dosomething()log.info("商品扣减库存完成");
}

七、锁的粒度与锁的时间

锁的粒度一定要把握好,不能过小或者过大。能使用粒度小的锁,就尽量使用。比如尽量使用粒度更小的变量级锁,而不是方法级锁。尽量使用对象的唯一性字段作为锁的 key,而不是对象。

锁的时间控制一定要合理,不能太长也不能太短,根据相应的需求来合理设置以达到较好的性能。包括等待时间、释放时间、超时策略的时间响应问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CCF CSP认证 历年题目自练Day29

题目一 试题编号&#xff1a; 202112-1 试题名称&#xff1a; 序列查询 时间限制&#xff1a; 300ms 内存限制&#xff1a; 512.0MB 样例1输入 3 10 2 5 8 样例1输出 15 样例2输入 9 10 1 2 3 4 5 6 7 8 9 样例2输出 45 题目分析&#xff08;个人理解&#xff09; 还是…

Chrome Extensions v3 迁移清单

一、前置问题 1.1为什么需要迁移 v3&#xff1f; Chrome 计划完全停止 v2 版本维护&#xff0c;后续 v2 版本将无法上架谷歌插件商店&#xff0c;除此之外&#xff0c;未来新版本 Chrome 对于 v2 版本插件的限制会越来越大&#xff0c;比如安全性限制 iframe 嵌套只能通过沙盒…

云原生Kubernetes:Rancher管理k8s集群

目录 一、理论 1.Rancher 2.Rancher 安装及配置 二、实验 1.Rancher 安装及配置 三、问题 1. Rancher 部署监控系统报错 四、总结 一、理论 1.Rancher (1) 概念 Rancher 简介 Rancher 是一个开源的企业级多集群 Kubernetes 管理平台&#xff0c;实现了 Kubernetes …

利用MobaXterm连接服务器的全程配置

一、服务器上的操作 1.1 保证openssh的安装 openssh安装命令如下 apt-get update apt install openssh-server1.2 保证SSH服务没有在相应端口上侦听连接 1确保本地 SSH 服务正在运行 可以尝试使用以下命令检查 SSH 服务的状态&#xff08;在大多数 Linux 系统上&#xff0…

分布式存储系统Ceph应用详解

Ceph的应用 一、Ceph 存储池(Pool)1.1 Ceph存储池的基本概念1.2 原理1.3 一个Pool资源池应该包含多少PG数&#xff1f;1.4 Ceph 存储池相关管理命令1.4.1 创建1.4.2 查看1.4.3 修改1.4.4 删除 二、 CephFS文件系统MDS接口三、创建CephFS文件系统MDS接口3.1 服务端操作Step1 在管…

【frp实现内网穿透踩坑到成功篇】

【frp实现内网穿透踩坑到成功篇】 背景&需求配置服务器端配置客户端总结 背景&需求 白嫖了一个tencent入门级服务器 ∗ 1 *1 ∗1&#xff0c;学校实验室内网服务器 ∗ 1 *1 ∗1&#xff0c;需要访问内网的服务器。一顿搜寻资料后确定大致的路子&#xff1a; 第一步是…

虹科分享 | 独特的FRER机制:TSN如何确保网络的可靠性?

1.IEEE802.1 CB协议 Frame Replication and Elimination for Reliability(FRER)是IEEE 802.1CB协议的一个重要特性&#xff0c;旨在增强以太网网络的可靠性。FRER利用帧复制和消除技术提供冗余保护和从连接故障中快速恢复。 FRER-IEEE 802.1CB协议的应用场景&#xff1a; 高…

【HttpRunner】接口自动化测试框架

简介 2018年python开发者大会上&#xff0c;了解到HttpRuuner开源自动化测试框架&#xff0c;采用YAML/JSON格式管理用例&#xff0c;能录制和转换生成用例功能&#xff0c;充分做到用例与测试代码分离&#xff0c;相比excel维护测试场景数据更加简洁。在此&#xff0c;利用业…

阿里云韩国服务器测试IP地址及公网带宽收费价格表

阿里云服务器韩国&#xff08;首尔&#xff09;地域公网带宽价格表&#xff0c;1M带宽价格是23.0元/月&#xff0c;按使用流量1GB价格是0.8元&#xff0c;阿里云韩国服务器测试IP地址&#xff1a;149.129.12.20&#xff0c;阿里云百科aliyunbaike.com来详细说下阿里云韩国服务器…

火伞云Web应用防火墙的特点与优势

在前文中&#xff0c;我们已经介绍了Web应用防火墙&#xff08;WAF&#xff09;的基本原理和重要性。接下来&#xff0c;我们将深入探讨火伞云Web应用防火墙的特点与优势&#xff0c;了解它如何为企业提供更为完善和专业的网络安全保障。 一、强大的防御能力 火伞云Web应用防火…

SVN报错fail to run the WC Db work queue associated with,清理失败,乱码的解决方式

替换掉 wc.db 文件即可 SVN报错fail to run the WC Db work queue associated with&#xff0c;清理失败&#xff0c;乱码的解决方式_svn failed to run the wc db-CSDN博客

再谈Java泛型

一.类型参数的约束 我们可以对泛型传进来的参数做一些约束&#xff0c;比如说 用extends表明传进来的参数类型必须是必须是某个类型的子类型或者本身 当然也可以用接口约束&#xff0c;也是用extends表明传进来的参数类型必须实现某个接口。用&连接&#xff0c;注意class…

python+深度学习+opencv实现植物识别算法系统 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…

论文阅读/写作扫盲

第一节&#xff1a;期刊科普 JCR分区和中科院分区是用于对期刊进行分类和评估的两种常见方法。它们的存在是为了帮助学术界和研究人员更好地了解期刊的学术质量、影响力和地位。 JCR分区&#xff08;Journal Citation Reports&#xff09;&#xff1a;JCR分区是由Clarivate Ana…

虹科方案 | AR助力仓储物流突破困境:规模化运营与成本节约

文章来源&#xff1a;虹科数字化AR 点击阅读原文&#xff1a;https://mp.weixin.qq.com/s/xis_I5orLb6RjgSokEhEOA 虹科方案一览 HongKe DigitalizationAR 当今的客户体验要求企业在人员、流程和产品之间实现全面的连接。为了提升整个组织的效率并提高盈利能力&#xff0c;物流…

【2023】redis-stream配合spring的data-redis详细使用(包括广播和组接收)

目录 一、简介1、介绍2、对比 二、整合spring的data-redis实现1、使用依赖2、配置类2.1、配置RedisTemplate bean2.2、异常类 3、实体类3.1、User3.2、Book 4、发送消息4.1、RedisStreamUtil工具类4.2、通过延时队列线程池模拟发送消息4.3、通过http主动发送消息 5、&#x1f3…

UWB承启定位基站

UWB承启定位基站 随着我们使用UWB做超高精度的定位项目越来越多&#xff0c;我们发现之前的定位基站完全站在二维或三维的角度去设计还是存在对应的缺陷&#xff0c;这个时候需要在很短的距离内安装多一个基站&#xff0c;对于用户来说&#xff0c;会觉得设备变多了&#xff0…

多目标鳟海鞘算法(Multi-objective Salp Swarm Algorithm,MSSA)求解微电网优化MATLAB

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、多目标鳟海鞘算法MSSA 多…

ABB机器人关于重定位移动讲解

关于机器人如何重定位移动&#xff0c;首先来看一下示教器上的重定位移动是在哪。 从图中所示的坐标位置和操纵杆方向得知&#xff0c;重定位的本质是绕X、Y、Z轴的旋转。那么实现跟摇杆一样的操作&#xff0c;就可以通过改变当前位置的欧拉角来实现&#xff0c;参考Rapid指令…

《Unity Shader 入门精要》笔记07

透明效果 为什么渲染顺序很重要Unity Shader的渲染顺序透明度测试透明度混合开启深度写入的半透明效果ShaderLab 的混合命令混合等式和参数混合操作常见的混合类型 双面渲染的透明效果透明度测试的双面渲染透明度混合的双面渲染 Unity中通常使用两种方法来实现透明效果&#xf…