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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
N
News and Events Feed by Topic
S
Secure Thoughts
Vercel News
Vercel News
S
Security @ Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - 聂微东
WordPress大学
WordPress大学
Google Online Security Blog
Google Online Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google DeepMind News
Google DeepMind News
PCI Perspectives
PCI Perspectives
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
Blog — PlanetScale
Blog — PlanetScale
Apple Machine Learning Research
Apple Machine Learning Research
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threat Research - Cisco Blogs
博客园 - 司徒正美
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
P
Proofpoint News Feed
大猫的无限游戏
大猫的无限游戏
V
V2EX
I
Intezer
H
Hacker News: Front Page
www.infosecurity-magazine.com
www.infosecurity-magazine.com
L
Lohrmann on Cybersecurity
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
博客园 - 叶小钗
The Cloudflare Blog
月光博客
月光博客
W
WeLiveSecurity
T
Tenable Blog
P
Proofpoint News Feed
aimingoo的专栏
aimingoo的专栏
Help Net Security
Help Net Security
L
LangChain Blog
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
美团技术团队
B
Blog

博客园 - 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