Spring Boot分布式锁选型与Redisson集成
微服务架构下,多个服务实例并发操作共享资源是常见问题。Spring Boot生态中分布式锁实现有多种选择:基于数据库的悲观锁、ZooKeeper临时节点、Redis SETNX。Redisson在Redis基础上提供了可重入锁、公平锁、读写锁等完整实现,API简洁且性能优异,是Spring Boot项目的首选方案。
Maven依赖引入:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.34.0</version>
</dependency>
application.yml配置单节点Redis:
spring:
redis:
host: 10.0.0.100
port: 6379
password: ${REDIS_PASSWORD}
database: 0
redisson:
threads: 16
nettyThreads: 32
可重入分布式锁核心用法与防死锁设计
Redisson的RLock实现了java.util.concurrent.locks.Lock接口,使用方式与JUC锁一致:
@Service
public class OrderService {
@Autowired
private RedissonClient redissonClient;
public String createOrder(String productId, String userId) {
String lockKey = "order:lock:" + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// waitTime: 获取锁最大等待时间
// leaseTime: 锁持有时间(防死锁关键)
boolean acquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!acquired) {
throw new BusinessException("系统繁忙,请稍后重试");
}
// 业务逻辑:检查库存、扣减、创建订单
return doCreateOrder(productId, userId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("操作被中断");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
leaseTime设为10秒是防死锁的核心:即使持有锁的服务宕机,10秒后锁自动释放。Redisson通过后台看门狗(watchdog)线程续期,默认每10秒检查并续期30秒,业务执行完毕正常释放时看门狗停止。
API接口限流:令牌桶与滑动窗口双方案
高并发场景下限流比限锁更常见。Redisson提供两种限流器:
方案1:RRateLimiter令牌桶限流
@Component
public class RateLimitAspect {
@Autowired
private RedissonClient redissonClient;
public boolean tryAcquire(String key, long permits, long rate, long interval) {
RRateLimiter limiter = redissonClient.getRateLimiter(key);
limiter.trySetRate(RateType.OVERALL, rate, interval, RateIntervalUnit.SECONDS);
return limiter.tryAcquire(permits);
}
}
// 在Controller中使用
@GetMapping("/api/products")
public Result listProducts() {
if (!rateLimitAspect.tryAcquire("api:products", 1, 100, 1)) {
return Result.fail(429, "请求过于频繁");
}
return Result.ok(productService.list());
}
令牌桶允许突发流量,适合读多写少的查询接口。参数rate=100表示每秒100个令牌,超量请求直接拒绝。
方案2:RScript滑动窗口计数限流
滑动窗口比固定窗口更精确,无临界点突刺问题:
@Service
public class SlidingWindowLimiter {
@Autowired
private RedissonClient redissonClient;
private static final String LUA_SCRIPT =
"local key = KEYS[1] " +
"local now = tonumber(ARGV[1]) " +
"local window = tonumber(ARGV[2]) " +
"local limit = tonumber(ARGV[3]) " +
"redis.call('ZREMRANGEBYSCORE', key, 0, now - window) " +
"local count = redis.call('ZCARD', key) " +
"if count < limit then " +
" redis.call('ZADD', key, now, now .. ':' .. math.random()) " +
" redis.call('PEXPIRE', key, window) " +
" return 1 " +
"end " +
"return 0";
public boolean tryAcquire(String key, long limit, long windowMs) {
RScript script = redissonClient.getScript();
Long result = script.eval(
RScript.Mode.READ_WRITE,
LUA_SCRIPT,
RScript.ReturnType.INTEGER,
Collections.singletonList(key),
String.valueOf(System.currentTimeMillis()),
String.valueOf(windowMs),
String.valueOf(limit)
);
return result != null && result == 1;
}
}
滑动窗口利用Redis Sorted Set存储每个请求的时间戳,查询时移除窗口外的旧记录,统计窗口内请求数。Lua脚本保证原子性,避免并发计数不准。
服务治理:分布式锁监控与降级策略
生产环境需监控锁的获取失败率和持有时长。Redisson原生支持JMX指标暴露:
# application.yml
redisson:
jmxEnabled: true
关键监控指标:锁等待超时次数(tryLock失败计数)、锁持有时长P99(超过5秒需告警)、看门狗续期失败次数(Redis连接异常导致)。
降级策略:当Redis不可用时,分布式锁退化为本地锁,保证服务可用性但牺牲一致性:
@Service
public class ResilientLock {
@Autowired(required = false)
private RedissonClient redissonClient;
public Lock acquireLock(String key) {
if (redissonClient != null && isRedisHealthy()) {
return redissonClient.getLock(key); // 分布式锁
}
return new ReentrantLock(); // 降级为本地锁
}
private boolean isRedisHealthy() {
try {
return redissonClient.getNodesGroup().pingAll();
} catch (Exception e) {
return false;
}
}
}
降级方案在Redis短暂故障时维持服务运转,故障恢复后自动切回分布式锁。对于库存扣减等强一致性场景,降级时应直接拒绝请求而非使用本地锁。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-gao-bing-fa-she-ji-ji-yu-redisson-de-fen-bu-shi/