Last active 5 months ago

Spring boot 中 Redis 实现简易分布式锁

Revision 81c0287675f081e65d69f820b4dab0a7a199721e

Demo.java Raw
1DistributedLockParam lockParam = null;
2try {
3 lockParam = new DistributedLockParam("test:lock:key");
4 boolean lockOk = distributedLock.lock(lockParam);
5 if (!lockOk) {
6 return new Result().error("系统繁忙,请稍候重试");
7 }
8 // 执行业务逻辑
9} finally {
10 // 最后强制解锁
11 if (lockParam != null) {
12 distributedLock.unlock(lockParam);
13 }
14}
DistributedLock.java Raw
1/**
2 * 分布式锁
3 */
4public interface DistributedLock {
5
6 /**
7 * 加锁
8 *
9 * @param param 加锁参数
10 * @return 加锁是否成功
11 */
12 boolean lock(DistributedLockParam param);
13
14 /**
15 * 解锁
16 *
17 * @param param 加锁参数
18 * @return 解锁是否成功
19 */
20 boolean unlock(DistributedLockParam param);
21}
RedisDistributedLockImpl.jav Raw
1import lombok.RequiredArgsConstructor;
2import org.apache.commons.lang3.StringUtils;
3import org.slf4j.Logger;
4import org.slf4j.LoggerFactory;
5import org.springframework.data.redis.core.RedisTemplate;
6import org.springframework.data.redis.core.script.DefaultRedisScript;
7import org.springframework.data.redis.core.script.RedisScript;
8import org.springframework.stereotype.Service;
9
10import java.util.Collections;
11import java.util.concurrent.TimeUnit;
12
13/**
14 * Redis Lock
15 **/
16@Service
17@RequiredArgsConstructor
18public class RedisDistributedLockImpl implements DistributedLock {
19
20 private static final Logger log = LoggerFactory.getLogger(RedisDistributedLockImpl.class);
21
22 private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
23 private static final Long UNLOCK_SUCCESS = 1L;
24
25 private final RedisTemplate<String, String> redisTemplate;
26
27 @Override
28 @SuppressWarnings({"BusyWait", "java:S2142"})
29 public boolean lock(DistributedLockParam param) {
30 final long tryLockEndTime = param.getTryLockTime() + System.currentTimeMillis();
31 while (true) {
32 //判断是否超过了,尝试获取锁的时间
33 if (System.currentTimeMillis() > tryLockEndTime) {
34 return false;
35 }
36
37 // 尝试获取锁
38 Boolean ok = redisTemplate.opsForValue().setIfAbsent(param.getKey(), param.getValue(), param.getHoldLockTime(), TimeUnit.MILLISECONDS);
39 if (Boolean.TRUE.equals(ok)) {
40 return true;
41 }
42
43 try {
44 //获得锁失败,休眠50毫秒再去尝试获得锁,避免一直请求redis,导致redis cpu飙升
45 Thread.sleep(50);
46 } catch (InterruptedException e) {
47 log.info(e.getMessage());
48 }
49 }
50 }
51
52 @Override
53 public boolean unlock(DistributedLockParam param) {
54 if (StringUtils.isBlank(param.getKey()) || StringUtils.isBlank(param.getValue())) {
55 return false;
56 }
57 RedisScript<Long> redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
58 Long result = redisTemplate.execute(redisScript, Collections.singletonList(param.getKey()), param.getValue());
59 return UNLOCK_SUCCESS.equals(result);
60 }
61}