当前位置: 首页 > news >正文

spring boot 实战之分布式锁

在 Spring Boot 项目中集成分布式锁,可结合 Redis 和 Redisson 框架,实现高性能、高可用的分布式锁服务。下面从环境搭建到实战案例,一步步带你实现 Spring Boot 分布式锁。

一、环境准备:引入依赖

pom.xml中添加 Redisson 和 Redis 客户端依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.20.0</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • redisson-spring-boot-starter:Redisson 的 Spring Boot 自动配置启动器。
  • spring-boot-starter-data-redis:Spring Data Redis 集成包。

二、配置 Redisson 客户端

application.yml中配置 Redis 连接信息:

spring:redis:host: localhostport: 6379password: 123456  # 若无密码可省略timeout: 3000ms   # 连接超时时间

创建 Redisson 配置类(可选,使用默认配置时可省略):

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {Config config = new Config();// 单节点模式(生产环境建议用集群模式)config.useSingleServer().setAddress("redis://" + "${spring.redis.host}:${spring.redis.port}").setPassword("${spring.redis.password}").setConnectTimeout((int) "${spring.redis.timeout}".replace("ms", ""));return Redisson.create(config);}
}

三、分布式锁实战:秒杀场景

假设我们要实现一个商品秒杀功能,关键是保证库存扣减的原子性,避免超卖。

1. 自定义注解:简化锁使用

创建@DistributedLock注解,用于标记需要加锁的方法:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {String value() default "defaultLock"; // 锁的key前缀long waitTime() default 10;           // 等待锁的时间(秒)long leaseTime() default 30;          // 锁的持有时间(秒)
}
2. AOP 切面:实现锁逻辑

创建切面类,拦截带有@DistributedLock注解的方法,自动加锁和解锁:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;@Aspect
@Component
public class DistributedLockAspect {private final RedissonClient redissonClient;public DistributedLockAspect(RedissonClient redissonClient) {this.redissonClient = redissonClient;}@Around("@annotation(com.example.annotation.DistributedLock)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();DistributedLock lockAnnotation = method.getAnnotation(DistributedLock.class);// 生成锁的key(类名+方法名+自定义前缀)String className = joinPoint.getTarget().getClass().getSimpleName();String methodName = method.getName();String lockKey = lockAnnotation.value() + ":" + className + ":" + methodName;RLock lock = redissonClient.getLock(lockKey);boolean isLocked = false;try {// 尝试获取锁(等待时间+持有时间)isLocked = lock.tryLock(lockAnnotation.waitTime(), lockAnnotation.leaseTime(), java.util.concurrent.TimeUnit.SECONDS);if (isLocked) {// 获取锁成功,执行方法return joinPoint.proceed();} else {// 获取锁失败,抛出异常或返回失败结果throw new RuntimeException("获取锁失败,请稍后重试");}} finally {// 释放锁(仅在当前线程持有锁时释放)if (isLocked && lock.isHeldByCurrentThread()) {lock.unlock();}}}
}
3. 业务服务:扣减库存

创建商品服务,使用@DistributedLock注解保护库存扣减逻辑:

import com.example.annotation.DistributedLock;
import org.springframework.stereotype.Service;@Service
public class ProductService {// 使用分布式锁保护库存扣减操作@DistributedLock(value = "product:stock", waitTime = 5, leaseTime = 20)public boolean deductStock(String productId, int quantity) {// 模拟从数据库查询库存int stock = getStockFromDb(productId);if (stock >= quantity) {// 扣减库存boolean success = updateStockInDb(productId, stock - quantity);return success;}return false;}private int getStockFromDb(String productId) {// 实际应查询数据库或缓存return 100; }private boolean updateStockInDb(String productId, int newStock) {// 实际应更新数据库return true;}
}
4. 控制器:暴露秒杀接口
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class SeckillController {private final ProductService productService;public SeckillController(ProductService productService) {this.productService = productService;}@PostMapping("/seckill/{productId}/{quantity}")public String seckill(@PathVariable String productId, @PathVariable int quantity) {boolean success = productService.deductStock(productId, quantity);return success ? "秒杀成功" : "库存不足,秒杀失败";}
}

四、高级用法:读写锁与公平锁

Redisson 支持多种锁类型,满足不同场景需求。

1. 读写锁(ReadWriteLock)

适合读多写少的场景,允许多个读操作并行,但写操作会互斥:

import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;@Service
public class CacheService {private final RedissonClient redissonClient;public CacheService(RedissonClient redissonClient) {this.redissonClient = redissonClient;}// 读锁:允许多个线程同时读public String getCache(String key) {RReadWriteLock rwLock = redissonClient.getReadWriteLock("cache:" + key);rwLock.readLock().lock();try {// 从缓存或数据库读取数据return "cacheValue";} finally {rwLock.readLock().unlock();}}// 写锁:互斥,同一时间只能有一个线程写public void updateCache(String key, String value) {RReadWriteLock rwLock = redissonClient.getReadWriteLock("cache:" + key);rwLock.writeLock().lock();try {// 更新缓存或数据库} finally {rwLock.writeLock().unlock();}}
}
2. 公平锁(Fair Lock)

按请求顺序获取锁,避免某些线程长期饥饿:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;@Service
public class OrderService {private final RedissonClient redissonClient;public OrderService(RedissonClient redissonClient) {this.redissonClient = redissonClient;}public String createOrder() {// 获取公平锁(加参数true)RLock fairLock = redissonClient.getFairLock("order:create");fairLock.lock();try {// 生成唯一订单号return generateOrderId();} finally {fairLock.unlock();}}private String generateOrderId() {// 生成订单号的逻辑return "ORD" + System.currentTimeMillis();}
}

五、监控与异常处理

1. 自定义异常
public class LockAcquisitionException extends RuntimeException {public LockAcquisitionException(String message) {super(message);}
}
2. 全局异常处理器
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(LockAcquisitionException.class)public String handleLockException(LockAcquisitionException e) {return "系统繁忙,请稍后重试";}
}

六、生产环境注意事项

  1. 集群模式配置:生产环境建议使用 Redis 集群,提高可用性:
spring:redis:cluster:nodes:- 192.168.1.1:6379- 192.168.1.2:6379- 192.168.1.3:6379

锁超时设置:根据业务耗时合理设置waitTimeleaseTime,避免锁持有过久或提前释放。

  1. 监控与告警:监控 Redis 性能和锁竞争情况,对频繁获取锁失败的场景告警。

  2. 降级策略:高并发场景下,若锁获取失败可降级为排队或返回 “稍后重试”,避免服务雪崩。

总结

在 Spring Boot 中集成 Redis 分布式锁,通过 Redisson 框架可以轻松实现:

  1. 依赖引入:添加 Redisson 和 Redis Starter 依赖。
  2. 配置客户端:连接 Redis 服务器(单节点或集群)。
  3. 自定义注解与 AOP:简化锁的使用,避免代码重复。
  4. 选择合适的锁类型:普通锁、读写锁、公平锁等。
  5. 异常处理与监控:保证系统稳定性。

通过这种方式,你可以在分布式系统中安全、高效地实现资源互斥访问,避免数据竞争问题。

http://www.xdnf.cn/news/1145161.html

相关文章:

  • linux 的list_for_each_entry
  • 数字化转型:概念性名词浅谈(第三十一讲)
  • 怎么判断一个对象是不是vue的实例
  • STM32-CAN
  • 根据用户id自动切换表查询
  • STM32 RTOS 开发基础:从任务管理到同步机制的全面解析
  • Git 团队协作完全指南:从基础到高级应用
  • Docker面试题
  • 饿了么app 抓包 hook
  • HTTP 性能优化:五条建议
  • 控制鼠标和键盘
  • uniapp微信小程序 实现swiper与按钮实现上下联动
  • SymAgent(神经符号自学习Agent)
  • 光伏财务管理:在阳光与资本的精密计算中前行
  • MyBatis缓存实战指南:一级与二级缓存的深度解析与性能优化
  • 用线性代数推导码分多址(CDMA)
  • vscode 一直连不上远程,网络是通的,ssh 也能直接登录远程
  • 【Linux】Linux异步IO-io_uring
  • 【Unity】IEnumeratorCoroutine
  • Ubuntu系统下交叉编译Android的X265库
  • Leetcode 04 java
  • cartorgapher的编译与运行
  • 网工知识——vlan技术
  • Linux操作系统之线程:分页式存储管理
  • 记录DataGrip 2025.1.3破解失败后,无法重启问题修复
  • 从“代码工坊“到“思维引擎“:Claude Code如何重塑编程权力结构
  • 习题4.1 输出3个人的顺序
  • 一文了解CDA
  • 优先算法——专题九:链表
  • 25数据库三级备考自整理笔记