跳到正文
Kale的数字花园
目录 文章
/3 分钟/3 阅读/0 评论

redis分布式锁工具类

import cn.hutool.core.util.StrUtil;
import com.wdtrip.common.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisLockService {

    private static final DefaultRedisScript<Long> REDIS_LOCK_SCRIPT;
    // 默认的过期时间:5分钟
    private static final Integer DEFAULT_EXPIRE = 5 * 60;
    // 获取锁的最大重试次数: 1次
    private static final Integer DEFAULT_MAX_COUNT = 1;
    // 默认睡眠时间。当获取次数大于 1 次时,如果第一次未获取到锁,则会睡眠, 默认睡眠200毫秒
    private static final Long DEFAULT_SLEEP_MILLIS = 200L;

    public static final String DEFAULT_ERROR_MSG = "can not get lock";

    static {
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setResultType(Long.class);
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("RedisLock.lua")));
        REDIS_LOCK_SCRIPT = script;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    public Result<?> exec(String key, Action action) {
        return exec(key, action, DEFAULT_EXPIRE);
    }

    public Result<?> exec(String key, Action action, Integer expire) {
        return exec(key, action, expire, DEFAULT_MAX_COUNT, DEFAULT_SLEEP_MILLIS);
    }

    /**
     * @param key
     * @param action   获取到锁后,需要执行的动作
     * @param expire   锁存活时间/秒
     * @param maxCount 获取锁的最大尝试次数
     * @param sleep    当 maxCount > 1 时,如果第一次未获取到锁,则睡眠指定时间之后,再重新获取锁
     * @return
     */
    public Result<?> exec(String key, Action action, Integer expire, Integer maxCount, Long sleep) {

        for (Integer count = 0; count < maxCount; count++) {
            // 如果获取锁抛出异常,则继续向外抛出
            boolean lock = getLock(key, expire);
            if (lock) {
                // 执行业务
                try {
                    return action.doAction();
                } catch (Exception e) {
                    log.error("RedisLock: 成功获取锁,执行动作异常", e);
                    return Result.error(-1, StrUtil.format("执行动作异常: {}", e.getMessage()));
                } finally {
                    releaseLock(key);
                }
            }
            if (maxCount > 1) {
                try {
                    TimeUnit.MILLISECONDS.sleep(sleep);
                } catch (InterruptedException e) {
                    return Result.error(-1, DEFAULT_ERROR_MSG);
                }
            }
        }
        return Result.error(-1, DEFAULT_ERROR_MSG);
    }

    /**
     * 获取锁
     *
     * @param key
     * @param expire
     * @return
     */
    private boolean getLock(String key, Integer expire) {
        Long res = redisTemplate.execute(REDIS_LOCK_SCRIPT, Arrays.asList(key), expire.toString());
        return res == 1;
    }


    private boolean releaseLock(String key) {
        return redisTemplate.delete(key);
    }

    public interface Action {
        Result<?> doAction();
    }
}
import cn.hutool.core.util.StrUtil;
import com.wdtrip.common.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedissonLockService {

    // 默认的过期时间:5分钟
    private static final Integer DEFAULT_EXPIRE = 5 * 60;
    // 获取锁的最大重试次数: 1次
    private static final Integer DEFAULT_MAX_COUNT = 1;
    // 默认睡眠时间。当获取次数大于 1 次时,如果第一次未获取到锁,则会睡眠, 默认睡眠200毫秒
    private static final Long DEFAULT_SLEEP_MILLIS = 200L;

    public static final String DEFAULT_ERROR_MSG = "try to get the lock fail";

    @Autowired
    private RedissonClient redissonClient;

    public Result<?> exec(String key, Boolean choose, Action action) {
        return exec(key, action, DEFAULT_EXPIRE, DEFAULT_MAX_COUNT, DEFAULT_SLEEP_MILLIS, choose);
    }

    /**
     * @param key
     * @param action   获取到锁后,需要执行的动作
     * @param expire   锁存活时间/秒
     * @param maxCount 获取锁的最大尝试次数
     * @param sleep    当 maxCount > 1 时,如果第一次未获取到锁,则睡眠指定时间之后,再重新获取锁
     * @param choose   true 手动释放锁
     * @return
     */
    public Result<?> exec(String key,
                          Action action,
                          Integer expire,
                          Integer maxCount,
                          Long sleep,
                          Boolean choose) {
        for (int count = 0; count < maxCount; count++) {
            try {
                RLock rLock = redissonClient.getLock(key);
                if (rLock.tryLock(0, expire, TimeUnit.SECONDS)) {
                    // 执行业务
                    try {
                        return action.doAction();
                    } catch (Exception e) {
                        log.error("RedisLock: 成功获取锁,执行动作异常", e);
                        return Result.error(-1, StrUtil.format("执行动作异常: {}", e.getMessage()));
                    } finally {
                        release(choose,rLock);
                    }
                }
                if (maxCount > 1) {
                    TimeUnit.MILLISECONDS.sleep(sleep);
                }
            } catch (InterruptedException e) {
                log.error("尝试获取分布式锁失败,key={}", key);
                return Result.error(-2, "尝试获取分布式锁失败");
            }
        }
        return Result.error(-1, DEFAULT_ERROR_MSG);
    }


    public void release(Boolean choose,RLock rLock){
        if (choose) {
            rLock.unlock();
        }
    }

    public interface Action {
        Result<?> doAction();
    }
}

评论需要登录。生产环境使用 GitHub 登录,本地可前往 登录页 使用开发登录。

还没有评论,来抢沙发。