问题背景

最近在学习 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-install

这样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 是让其后台运行,不阻塞当前命令行窗口
启动成功如图
start-compose

查看 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 节点下的分布式锁效果。