完整的 Spring Boot + Redisson 分布式锁示例

下面为你提供一个完整的 Spring Boot + Redisson 分布式锁示例项目结构,包含:

  • Maven 依赖配置
  • Redisson 配置类(支持单机/集群)
  • 分布式锁工具封装
  • 控制器示例(模拟并发场景)
  • 测试方法建议

你可以直接复制到你的项目中使用。


📁 项目结构概览

复制代码
src/
├── main/
│   ├── java/
│   │   └── com.example.redissonlock/
│   │       ├── RedissonLockApplication.java
│   │       ├── config/
│   │       │   └── RedissonConfig.java
│   │       ├── service/
│   │       │   ├── OrderService.java
│   │       │   └── impl/
│   │       │       └── OrderServiceImpl.java
│   │       └── controller/
│   │           └── OrderController.java
│   └── resources/
│       ├── application.yml
│       └── application-dev.yml (可选)
└── test/
    └── java/
        └── com.example.redissonlock/
            └── RedissonLockTest.java

1️⃣ Maven 依赖(pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version> <!-- 支持 JDK 17+ -->
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>redisson-lock-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Redisson Starter (自动配置 RedissonClient) -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.25.2</version> <!-- 检查最新版:https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
        </dependency>

        <!-- Lombok (可选,简化日志/Getter/Setter) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

注意redisson-spring-boot-starter 已包含 spring-boot-starter-data-redisredisson 核心包。


2️⃣ 配置文件(application.yml

yaml 复制代码
spring:
  application:
    name: redisson-lock-demo

  # Redis 连接配置(单机模式)
  redis:
    host: localhost
    port: 6379
    database: 0
    timeout: 2000ms
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0

# Redisson 自定义配置(可选,如需调整看门狗等)
# 如果不配置,Redisson 会自动使用 spring.redis 下的连接信息

🔁 集群模式示例 (替换 spring.redis):

yaml 复制代码
spring:
  redis:
    cluster:
      nodes:
        - 192.168.1.10:7000
        - 192.168.1.10:7001
        - 192.168.1.10:7002
    password: your_password
    timeout: 2000ms

3️⃣ Redisson 配置类(可选,用于自定义)

⚠️ 如果只用单机 Redis,其实不需要写这个类redisson-spring-boot-starter 会自动创建 RedissonClient

但如果你需要自定义(如修改看门狗超时、启用 codec),可以添加:

java 复制代码
// src/main/java/com/example/redissonlock/config/RedissonConfig.java
package com.example.redissonlock.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://" + redisHost + ":" + redisPort)
              .setTimeout(3000)
              .setConnectionPoolSize(10);

        // 可选:调整看门狗超时(默认30秒)
        // config.setLockWatchdogTimeout(60000); // 60秒

        return Redisson.create(config);
    }
}

4️⃣ 业务服务层(带分布式锁)

java 复制代码
// src/main/java/com/example/redissonlock/service/OrderService.java
package com.example.redissonlock.service;

public interface OrderService {
    void createOrder(String userId, String orderId);
}
java 复制代码
// src/main/java/com/example/redissonlock/service/impl/OrderServiceImpl.java
package com.example.redissonlock.service.impl;

import com.example.redissonlock.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    private final RedissonClient redissonClient;

    public OrderServiceImpl(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public void createOrder(String userId, String orderId) {
        // 锁 key 建议包含业务唯一标识,避免全局锁
        String lockKey = "order:create:" + orderId;

        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 尝试加锁:最多等待 3 秒,持有锁 30 秒(看门狗会自动续期)
            boolean isLocked = lock.tryLock(3, 30, TimeUnit.SECONDS);
            if (!isLocked) {
                log.warn("获取分布式锁失败,orderId={}", orderId);
                throw new RuntimeException("系统繁忙,请稍后再试");
            }

            log.info("线程 {} 获取锁成功,开始处理订单 {}", Thread.currentThread().getName(), orderId);

            // 模拟业务耗时(即使超过30秒也不会丢锁!)
            Thread.sleep(5000);

            // TODO: 真实业务逻辑(如扣库存、写DB)
            log.info("订单 {} 创建成功", orderId);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("订单处理被中断", e);
            throw new RuntimeException(e);
        } finally {
            // 安全释放锁(Redisson 会校验线程ID)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("线程 {} 释放锁,orderId={}", Thread.currentThread().getName(), orderId);
            }
        }
    }
}

5️⃣ 控制器(用于测试)

java 复制代码
// src/main/java/com/example/redissonlock/controller/OrderController.java
package com.example.redissonlock.controller;

import com.example.redissonlock.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @GetMapping("/create-order")
    public String createOrder(@RequestParam String userId,
                              @RequestParam String orderId) {
        orderService.createOrder(userId, orderId);
        return "Order created: " + orderId;
    }
}

6️⃣ 启动类

java 复制代码
// src/main/java/com/example/redissonlock/RedissonLockApplication.java
package com.example.redissonlock;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RedissonLockApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedissonLockApplication.class, args);
    }
}

🧪 如何测试并发效果?

方法 1:使用 JMeter / Postman Runner

  • 发起多个并发请求到:
    GET http://localhost:8080/create-order?userId=U1&orderId=O1001
  • 观察日志:同一 orderId 的请求会串行执行

方法 2:单元测试(模拟多线程)

java 复制代码
// src/test/java/com/example/redissonlock/RedissonLockTest.java
@Test
void testConcurrentOrderCreation() throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(5);
    CountDownLatch latch = new CountDownLatch(5);

    for (int i = 0; i < 5; i++) {
        executor.submit(() -> {
            try {
                orderService.createOrder("U1", "O1001"); // 相同 orderId
            } finally {
                latch.countDown();
            }
        });
    }

    latch.await(); // 等待所有线程完成
    executor.shutdown();
}

✅ 关键优势总结

特性 说明
自动续期 Watchdog 默认每 10s 续期一次,不怕业务超时
安全释放 基于线程 ID 校验,不会误删其他线程的锁
可重入 同一线程可多次加锁
无死锁风险 即使应用 crash,锁也会在 watchdog 超时后释放
Spring Boot 集成简单 starter 自动配置,开箱即用

📌 注意事项

  1. 锁 key 设计 :务必带上业务唯一标识(如 order:{orderId}),避免锁粒度太大。
  2. 不要在锁内做远程调用:如 HTTP 请求、MQ 发送,可能因网络延迟导致锁持有时间过长。
  3. 监控锁状态 :可通过 Redis CLI 查看:KEYS *order*TTL your_lock_key
  4. 生产环境 Redis 高可用:建议使用 Redis Cluster 或 Sentinel。

相关推荐
风中凌乱1 小时前
kafka新版本集群的安装与部署
分布式·kafka
嘉伟桑1 小时前
Python调用电价API返回JSON示例:分时电价、现货电价和字段解析
后端
武子康1 小时前
Shippy:确定性工具、会话级 Sandbox 与 Live-Data Eval(4 类收敛 + 7 步实现方案 + 6 类评测指标)
前端·人工智能·后端
SLD_Allen2 小时前
大规模分布式AI训练基础设施
人工智能·分布式·模型训练
我叫黑大帅2 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
AskHarries2 小时前
文件上传系统
后端
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
富士康质检员张全蛋3 小时前
Kafka的操作-消费的详情
分布式·kafka
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端