SpringBoot Redis 实现分布式锁

1. 配置和依赖

# build.gralde 添加依赖
  implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.1.5.RELEASE'
# application.xml 添加 redis 端口配置
spring:
  redis:
    cluster:
      nodes:
        - xxxx:6379
        - yyyy:6379

2. Redis 配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration
public class RedisConfig {

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }

}

3. 加锁和解锁的实现

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedisService {

    private static final String REDIS_UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 获取分布式锁
     *
     * @param key
     * @param token
     * @param expireInSeconds 锁超时时间
     * @return
     */
    public boolean tryLock(String key, String token, long expireInSeconds) {
        Boolean res = redisTemplate.opsForValue().setIfAbsent(key, token, expireInSeconds, TimeUnit.SECONDS);
        return Objects.equals(res, true);
    }

    /**
     * 分布式锁unlock,使用lua脚本保证事务
     *
     * @param key
     * @param token lock时的token值,只有token一致才能解锁
     * @return
     */
    public void unlock(String key, String token) {
        try {
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(REDIS_UNLOCK_SCRIPT, Long.class);
            Long res = redisTemplate.execute(redisScript, Collections.singletonList(key), token);
            if (!Objects.equals(res, 1L)) {
                log.warn("redis unlock wrong:key=[{}],token=[{}],res=[{}]", key, token, res);
            }
        } catch (Exception e) {
            log.error("redis unlock error:key=[{}],token=[{}]", key, token, e);
        }
    }

加锁添加等待时间

    public boolean tryAcquireLock(String key, String token, long lockTimeout, long acquireTimeout) {
        try {
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                Boolean res = redisTemplate.opsForValue().setIfAbsent(key, token, lockTimeout, TimeUnit.MILLISECONDS);
                if (Boolean.TRUE.equals(res)) {
                    return true;
                }
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    log.error("thread sleep error", e);
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
            log.error("try acquire lock error, ", e);
        }
        return false;
    }
posted @ 2022-10-20 16:20  风小雅  阅读(796)  评论(0编辑  收藏  举报