redis分布式锁的最佳实践

72 阅读3分钟

上篇文章提到了我解决bug的时候,尝试引入分布式锁解决问题,操作的时候发现我们的项目简单到没有redis。需要从头开始了。这里就简单记录一下吧

引入两个相关的包:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>

这里是一个分布式锁的实现类,可以根据不同的需求调用不同的方法。


package com.guava.mall.app.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisLockHandler {
    /**
     * 最大持有锁的时间(毫秒)
     */
    private final static long LOCK_EXPIRE = 30 * 1000L;

    /**
     * 尝试获取锁的时间间隔(毫秒)
     */
    private final static long LOCK_TRY_INTERVAL = 30L;

    /**
     * 获取锁最大等待时间( 毫秒 )
     */
    private final static long LOCK_TRY_TIMEOUT = 20 * 1000L;

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 尝试获取 分布式锁
     *
     * @param lockKey 锁名
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLock(String lockKey) {
        return getLock(lockKey, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, LOCK_TRY_INTERVAL);
    }

    /**
     * 尝试获取 分布式锁(不自动释放锁)
     *
     * @param lockKey 锁名
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLockNotAutoRelease(String lockKey) {
        return getLock(lockKey, LOCK_TRY_TIMEOUT, LOCK_TRY_INTERVAL, -1);
    }

    /**
     * 尝试获取 分布式锁
     *
     * @param lockKey 锁名
     * @param timeout 获取锁最大等待时间
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLock(String lockKey, long timeout) {
        return getLock(lockKey, timeout, LOCK_TRY_INTERVAL, LOCK_EXPIRE);
    }

    /**
     * 尝试获取 分布式锁(不自动释放锁)
     *
     * @param lockKey 锁名
     * @param timeout 获取锁最大等待时间
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLockNotAutoRelease(String lockKey, long timeout) {
        return getLock(lockKey, timeout, LOCK_TRY_INTERVAL, -1);
    }

    /**
     * 尝试获取 分布式锁
     *
     * @param lockKey     锁名
     * @param timeout     获取锁最大等待时间
     * @param tryInterval 获取锁尝试 时间间隔
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLock(String lockKey, long timeout, long tryInterval) {
        return getLock(lockKey, timeout, tryInterval, LOCK_EXPIRE);
    }

    /**
     * 尝试获取 分布式锁(不释放锁)
     *
     * @param lockKey     锁名
     * @param timeout     获取锁最大等待时间
     * @param tryInterval 获取锁尝试 时间间隔
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLockNotAutoRelease(String lockKey, long timeout, long tryInterval) {
        return getLock(lockKey, timeout, tryInterval, -1);
    }

    /**
     * 尝试获取 分布式锁
     *
     * @param lockKey        锁名
     * @param timeout        获取锁最大等待时间
     * @param tryInterval    获取锁尝试 时间间隔
     * @param lockExpireTime 锁最大持有时间
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean tryLock(String lockKey, long timeout, long tryInterval, long lockExpireTime) {
        return getLock(lockKey, timeout, tryInterval, lockExpireTime);
    }

    /**
     * 获取分布式锁
     *
     * @param lockKey        锁名
     * @param timeout        获取锁最大等待时间
     * @param tryInterval    获取锁尝试 时间间隔
     * @param lockExpireTime 锁最大持有时间
     * @return true 得到了锁 ,false 获取锁失败
     */
    public boolean getLock(String lockKey, long timeout, long tryInterval, long lockExpireTime) {
        try {
            if (StringUtils.isEmpty(lockKey)) {
                return false;
            }
            long startTime = System.currentTimeMillis();
            do {
                ValueOperations<String, String> ops = redisTemplate.opsForValue();
                
                boolean success = Boolean.TRUE.equals(lockExpireTime > 0 ? ops.setIfAbsent(lockKey, "lockValue", lockExpireTime, TimeUnit.MILLISECONDS) : ops.setIfAbsent(lockKey, "lockValue"));
                if (success) {
                    return true;
                }
                Thread.sleep(tryInterval);
            } while (System.currentTimeMillis() - startTime < timeout);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
            return false;
        }
        return false;
    }

    /**
     * 释放锁
     *
     * @param lockKey
     */
    public void unLock(String lockKey) {
        if (!StringUtils.isEmpty(lockKey)) {
            redisTemplate.delete(lockKey);
        }
    }
}

使用的时候就很简单了,将这个包引入,然后直接调用方法。

boolean lock = redisLockHandler.tryLock(CREATE_WAVE_LOCK_KEY, 0, 0, 10 * 1000L);

简单记录一下,这里本来是先set再expire的,肯定有问题,若是设置时间的时候挂了,岂不是死锁了。