问题背景
最近在学习 Redis 分布式锁和 Redisson 的 MultiLock 机制时,遇到了一个很现实的问题:
很多分布式场景需要多个 Redis 实例来模拟,例如多个 Redis 节点分别保存锁信息,从而提高分布式锁的可靠性。
但是我并不想开很多台VM虚拟机,不仅吃我电脑性能,还特别麻烦😑
不过我发现docker很适合解决该问题,正好就学习一下Docker并记录一下
我们可以在同一台机器上,通过 Docker 快速启动多个 Redis 容器,每个容器都像是一个独立的 Redis 服务。这样既能节省资源,也方便随时创建、停止和删除。
为什么需要多个 Redis 实例?
对于普通的 redis 分布式锁,一般是一个节点执行类似下面的命令:
set lock:order user1 NX EX 30
大概意思是,设置一个key 为 lock:order , value 为 user1 的锁,NX为不存在则设置, EX为设置过期时间,这里设置是30s
这种方式在 单redis 节点下可以工作,但是如果redis节点挂了;或者主从复制没有完成同步就发生了故障,就会出现锁安全问题。
Redisson的MutiLock思路是:
同时向多个redis实例加锁,只有多个锁都成功时,才认为最终枷锁成功
例如有三个redis
redis1
redis2
redis3
加锁时候要同时向它三加锁:
redis1 加锁成功
redis2 加锁成功
redis3 加锁成功
只要其中一个失败,就认为整体加锁失败
为了有多个redis环境让我来模拟该情景,也是让我发现了docker这个神奇的东东
Docker安装与使用
安装
咱们win11电脑普遍都有WSL,也就是Linux子系统,是Ubuntu的
所以文吗可以直接在win系统上安装Docker Desktop , 链接:https://www.docker.com/products/docker-desktop/
然后安装时选择Use WSL 2 instead of Hyper-V即可
这样Docker Desktop 就会借助 WSL 2 来运行 Linux 容器
当然也可以安装在你的Linux虚拟机上,具体可以到Docker官网查看如何安装和配置
我使用的是WSL 2,就围绕其往下展开了
使用
安装好打开WSL,输入docker -version来查看和验证是否安装成功
然后在运行测试容器
docker run hello-world
如果输出:
Hello from Docker!
说明Docker安装完成且可正常使用了
创建多个redis
获取redis镜像
使用命令获取redis镜像image
docker pull redis
这默认是获取 latest 版本,也就是最新版,如果需要指定版本可以在后面添加版本号,例如docker pull redis:7.2
使用docker images可以看到你本地安装的image
创建和运行容器
通过以下命令可以快速创建和运行redis容器
docker run -d --name redis-1 -p 6379:6379 redis
我们可以使用 docker ps -a 来查看所有容器是否启动,或者使用docker ps只查看已启动的容器。
停止容器使用docker stop 容器名/容器ID, 容器ID可以使用 docker ps -a 来查看
移除容器使用docker rm 容器名/容器ID
使用下面命令,唤醒redis-cli,进行PING PONG 一下看是否真的redis服务可用
docker exec -it redis-1 redis-cli
进入后输入:
ping
如果返回:
PONG
说明 Redis 容器正常运行。
但是我们要启动多个redis容器每次都得一个一个写,比如
docker run -d --name redis-1 -p 6379:6379 redis
docker run -d --name redis-2 -p 6380:6379 redis
docker run -d --name redis-3 -p 6381:6379 redis
当需要特别多容器的时候,启动麻烦,停止移除也麻烦
这时候可以创建一个 compose.yaml 或者 compose.yal 文件去一键启动。
compose服务的使用
我们可以创建一个专门放compose的目录
mkdir docker-compose
cd docker-compose
然后创建文件
touch redis-compose.yaml
编写redis-compose.yaml
vim redis-compose
在redis-compos.yaml里写
services:
redis1:
image: redis
container_name: redis1
ports:
- "6379:6379"
redis2:
image: redis
container_name: redis2
ports:
- "6380:6379"
redis3:
image: redis
container_name: redis3
ports:
- "6381:6379"
保存后,在当前目录下启动该compose文件
docker compose -f redis-compose.yaml up -d
如果目录下只有一个compose文件则不用 -f 去指定对应文件, -d 是让其后台运行,不阻塞当前命令行窗口
启动成功如图
查看 Compose 服务
docker compose -f redis-compose.yaml ps
停止和移除compose
docker compose -f redis-compose.yaml down
Java 中如何连接多个 Redis?
在 Redisson 中,可以分别创建多个 RLock,然后组合成 RedissonMultiLock。
示例代码:
Config config1 = new Config();
config1.useSingleServer()
.setAddress("redis://127.0.0.1:6379");
RedissonClient redissonClient1 = Redisson.create(config1);
Config config2 = new Config();
config2.useSingleServer()
.setAddress("redis://127.0.0.1:6380");
RedissonClient redissonClient2 = Redisson.create(config2);
Config config3 = new Config();
config3.useSingleServer()
.setAddress("redis://127.0.0.1:6381");
RedissonClient redissonClient3 = Redisson.create(config3);
分别获取三个锁:
RLock lock1 = redissonClient1.getLock("order-lock");
RLock lock2 = redissonClient2.getLock("order-lock");
RLock lock3 = redissonClient3.getLock("order-lock");
组合成 MultiLock:
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3);
加锁示例:
boolean isLock = multiLock.tryLock();
try {
if (!isLock) {
System.out.println("获取锁失败");
return;
}
System.out.println("获取锁成功,执行业务逻辑");
// 模拟业务逻辑
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (isLock) {
multiLock.unlock();
System.out.println("释放锁成功");
}
}
这样就可以在本地模拟多个 Redis 节点下的分布式锁效果。