惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
Simon Willison's Weblog
Simon Willison's Weblog
K
Kaspersky official blog
P
Palo Alto Networks Blog
Google DeepMind News
Google DeepMind News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
AI
AI
G
GRAHAM CLULEY
O
OpenAI News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
L
LINUX DO - 热门话题
S
Schneier on Security
P
Privacy International News Feed
L
Lohrmann on Cybersecurity
SecWiki News
SecWiki News
C
Cybersecurity and Infrastructure Security Agency CISA
T
Threatpost
C
Cyber Attacks, Cyber Crime and Cyber Security
A
Arctic Wolf
C
Cisco Blogs
V2EX - 技术
V2EX - 技术
有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
Latest news
Latest news
人人都是产品经理
人人都是产品经理
Schneier on Security
Schneier on Security
Last Week in AI
Last Week in AI
Webroot Blog
Webroot Blog
美团技术团队
N
News and Events Feed by Topic
大猫的无限游戏
大猫的无限游戏
Security Archives - TechRepublic
Security Archives - TechRepublic
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog
Project Zero
Project Zero
博客园_首页
Cloudbric
Cloudbric
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
罗磊的独立博客
Hacker News: Ask HN
Hacker News: Ask HN
The Last Watchdog
The Last Watchdog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tenable Blog
Scott Helme
Scott Helme

博客园 - hu晓峰

记winform程序异常排查 记一次wpf 背景图的坑点 【Unity踩坑】Unity项目管理员权限问题(Unity is running as administrator ) 依赖注入 微服务聚合查询 libmodbus编译为64位动态库 一文读懂Modbus协议:工业设备的“普通话“通信指南 Mysql union与union all有什么区别? 理解Systemd服务重启策略:on-failure vs always Redis分布式锁正确的实现方法 C# 解决串口通讯中,返回数据不完整 字典Dictionary.Add不是把新的元素插入到字典最后面 c# Avalonia 架构开发跨平台应用 ‌索引基数 MySQL InnoDB损坏修复:使用innodb_force_recovery 整数取低字节 C#汉字-区位码相互转化类 avalonia在linux下运行出现Default font family name can't be null or empty问题的解决 ICMP timestamp请求响应漏洞CVE-1999-0524解决方法 详解mysql的for update ASP.NET Core中如何对不同类型的用户进行区别限流
使用Redis的SETNX命令实现分布式锁
hu晓峰 · 2024-09-23 · via 博客园 - hu晓峰

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

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

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

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

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

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

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

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1
2
3
4
在application.yml文件中配置 Redis 连接信息:

server:
port: 8080
spring:
redis:
host: xxx.xxx.xxx.xxx
port: 6379
password: xxxxxx
1
2
3
4
5
6
7
具体实现
创建分布式锁工具类

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;

// 通过构造函数注入 StringRedisTemplate
public 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,否则返回 false
Boolean 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
创建业务类用来测试

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
@Autowired
public MyRedisService(DistributedLock distributedLock) {
this.distributedLock = distributedLock;
}

/**
* 模拟需要同步执行的方法
*/
public void someMethod() {
String lockKey = "myLockKey";
String requestId = "uniqueRequestId";
long expireTime = 10000; // 10 seconds

try {
// 尝试获取锁
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);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
创建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 {

@Autowired
private MyRedisService service;

@GetMapping("/test")
public void TestRedis(){
service.someMethod();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
整个项目结构如下:

使用idea的复制配置功能将该服务复制一份,并指定端口为8081,模拟分布式服务:

使用接口调试工具分别向8080和8081端口发送请求:


结果如下:


可见在分布式锁的影响下,someMethod方法在10秒内只能被调用一次。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_39354140/article/details/140514514