当前位置: 首页 > news >正文

使用Redis的SETNX命令实现分布式锁

什么是分布式锁

分布式锁是一种用于在分布式系统中控制多个节点对共享资源进行访问的机制。在分布式系统中,由于多个节点可能同时访问和修改同一个资源,因此需要一种方法来确保在任意时刻只有一个节点能够对资源进行操作,以避免数据不一致或冲突。分布式锁就是用来实现这种互斥访问的工具。

为什么 Redis 的 SETNX 可以实现分布式锁

Redis 的 SETNX 命令(即 SET if Not eXists)可以用来实现分布式锁,原因如下:

  1. 原子性SETNX 是一个原子操作,这意味着在同一时间只有一个客户端能够成功设置键值对。如果键已经存在,SETNX 将不会执行任何操作。这种原子性确保了锁的获取和释放是线程安全的。

  2. 唯一性SETNX 确保了锁的唯一性。只有第一个尝试设置键的客户端能够成功,其他客户端在尝试设置相同的键时会失败。这模拟了锁的“获取”和“释放”行为。

  3. 过期时间:通过结合 EXPIRE 命令或使用 SET 命令的 EX 选项,可以为锁设置一个过期时间。这防止了锁被永久占用,即使客户端在持有锁期间崩溃或未能正确释放锁。

  4. 分布式环境:Redis 是一个分布式内存数据库,可以在多个节点之间共享数据。因此,使用 Redis 实现的锁可以在分布式系统中的多个节点之间共享,从而实现分布式锁。

  5. 高性能:Redis 是一个高性能的数据库,能够处理大量的并发请求。这使得 Redis 非常适合作为分布式锁的实现基础。

准备工作

创建一个Spring Boot项目,并引入相关依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application.yml文件中配置 Redis 连接信息:

server:port: 8080
spring:redis:host: xxx.xxx.xxx.xxxport: 6379password: xxxxxx

具体实现

创建分布式锁工具类

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
public class DistributedLock {private final StringRedisTemplate redisTemplate;// 通过构造函数注入 StringRedisTemplatepublic DistributedLock(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 尝试获取分布式锁** @param lockKey    锁的键* @param requestId  请求标识,用于区分不同的锁持有者* @param expireTime 锁的过期时间,单位为毫秒* @return 如果成功获取锁,返回 true;否则返回 false*/public boolean acquireLock(String lockKey, String requestId, long expireTime) {// 使用 setIfAbsent 方法尝试设置键值对,如果键不存在则设置成功并返回 true,否则返回 falseBoolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS);return result != null && result;}/*** 释放分布式锁** @param lockKey   锁的键* @param requestId 请求标识,用于确保只有锁的持有者才能释放锁* @return 如果成功释放锁,返回 true;否则返回 false*/public boolean releaseLock(String lockKey, String requestId) {// 获取当前锁的值String currentValue = redisTemplate.opsForValue().get(lockKey);// 检查当前锁的值是否等于请求标识,确保只有锁的持有者才能释放锁if (currentValue != null && currentValue.equals(requestId)) {// 删除锁键return redisTemplate.delete(lockKey);}return false;}
}

创建业务类用来测试

import com.wh.demo01.demos.web.utils.DistributedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Date;@Service
public class MyRedisService {private final DistributedLock distributedLock;// 通过构造函数注入 DistributedLock@Autowiredpublic MyRedisService(DistributedLock distributedLock) {this.distributedLock = distributedLock;}/*** 模拟需要同步执行的方法*/public void someMethod() {String lockKey = "myLockKey";String requestId = "uniqueRequestId";long expireTime = 10000; // 10 secondstry {// 尝试获取锁if (distributedLock.acquireLock(lockKey, requestId, expireTime)) {// 获取到锁,执行需要同步的操作System.out.println(new Date() + "获取锁成功");// 模拟业务操作Thread.sleep(5000);} else {System.out.println(new Date() + "获取锁失败");}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 确保锁在操作完成后被释放distributedLock.releaseLock(lockKey, requestId);}}
}

创建Controller

import com.wh.demo01.demos.web.service.MyRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/redis")
public class RedisController {@Autowiredprivate MyRedisService service;@GetMapping("/test")public void TestRedis(){service.someMethod();}
}

整个项目结构如下:
在这里插入图片描述
使用idea的复制配置功能将该服务复制一份,并指定端口为8081,模拟分布式服务:
在这里插入图片描述
使用接口调试工具分别向80808081端口发送请求:
在这里插入图片描述
在这里插入图片描述
结果如下:
在这里插入图片描述
在这里插入图片描述
可见在分布式锁的影响下,someMethod方法在10秒内只能被调用一次。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 科普文:微服务技术栈梳理
  • 详细讲解下 算法中的 堆栈
  • 提示工程的技术与策略分类
  • 贪心算法(2024/7/16)
  • 从0到1搭建数据中台(4):neo4j初识及安装使用
  • Golang 创建 Excel 文件
  • PlantUML-UML 绘图工具安装、Graphviz安装、本地使用/在线使用、语法、图示案例
  • 源码分析SpringCloud Gateway如何加载断言(predicates)与过滤器(filters)
  • Java毕业设计 基于SpringBoot的景区行李寄存管理系统
  • 【Django】网上蛋糕商城后台-类目管理
  • huawei USG6001v1学习---信息安全概念
  • 前端使用webSocket与后台建立连接并进行心跳监测机制
  • AWS基础知识
  • 专业PDF编辑工具:Acrobat Pro DC 2024.002.20933绿色版,提升你的工作效率!
  • WPF/C#:实现导航功能
  • 【翻译】babel对TC39装饰器草案的实现
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • axios 和 cookie 的那些事
  •  D - 粉碎叛乱F - 其他起义
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • JavaScript服务器推送技术之 WebSocket
  • Map集合、散列表、红黑树介绍
  • Mysql优化
  • Nacos系列:Nacos的Java SDK使用
  • STAR法则
  • vue总结
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 从零开始的无人驾驶 1
  • - 概述 - 《设计模式(极简c++版)》
  • 排序算法学习笔记
  • 自制字幕遮挡器
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • ​探讨元宇宙和VR虚拟现实之间的区别​
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • ${factoryList }后面有空格不影响
  • $L^p$ 调和函数恒为零
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (4)(4.6) Triducer
  • (C#)一个最简单的链表类
  • (C++17) std算法之执行策略 execution
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (二开)Flink 修改源码拓展 SQL 语法
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (四) 虚拟摄像头vivi体验
  • (四)js前端开发中设计模式之工厂方法模式
  • (一)基于IDEA的JAVA基础12
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .Family_物联网
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET DataGridView数据绑定说明