All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lx.boot.lock.RedisLock Maven / Gradle / Ivy

Go to download

使用文档: https://a7fi97h1rc.feishu.cn/docx/X3LRdtLhkoXQ8hxgXDQc2CLOnEg?from=from_copylink

There is a newer version: 1.1
Show newest version
package com.lx.boot.lock;

import com.lx.annotation.Note;
import com.lx.boot.OS;
import com.lx.util.LX;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

@Slf4j
public class RedisLock {

    private StringRedisTemplate stringRedisTemplate;

    //redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("META-INF/lua/redis-limit.lua")));
    /**
     * 加/解锁的lua脚本
     */
    public static final String LOCK_LUA;
    public static final String UNLOCK_LUA;
    static {
        //判断key是否存在 判断值对不对 对+1
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call('exists',KEYS[1]) == 0 ");
        sb.append("then ");
        sb.append("    redis.call('hincrby' ,KEYS[1] ,ARGV[1] ,1) ");
        sb.append("    redis.call('expire' ,KEYS[1] ,ARGV[2]) ");
        sb.append("    return 1 ");
        sb.append("end ");
        sb.append("if redis.call('hexists',KEYS[1], ARGV[1]) == 1 ");
        sb.append("then ");
        sb.append("    redis.call('hincrby' ,KEYS[1] ,ARGV[1] ,1) ");
        sb.append("    redis.call('expire' ,KEYS[1] ,ARGV[2]) ");
        sb.append("    return 1 ");
        sb.append("end ");
        sb.append("return 0 ");
        LOCK_LUA = sb.toString();


        //判断值是否一致 次数减少  如果次数为0 则删除键
        sb = new StringBuilder();
        sb.append("if redis.call('hexists',KEYS[1], ARGV[1]) == 0 then ");
        sb.append("return 0 ");
        sb.append("end ");
        sb.append("if redis.call('hincrby' ,KEYS[1] ,ARGV[1] ,-1) > 0 then ");
        sb.append("    redis.call('expire' ,KEYS[1] ,ARGV[2]) ");
        sb.append("else ");
        sb.append("    redis.call('del' ,KEYS[1])");
        sb.append("end ");
        sb.append("return 1 ");
        UNLOCK_LUA = sb.toString();
    }

    /**
     * 锁标志对应的key
     */
    private String lockKey;
    /**
     * 锁对应的值
     */
    private String lockValue;
    /**
     * 锁的有效时间(s)
     */
    private int expireTime = 60;

    final Random random = new Random();
    public int getExpireTime() {
        return expireTime;
    }
    public void setExpireTime(int expireTime) {
        this.expireTime = expireTime<10?10:expireTime;
    }

    /**
     * 使用默认的锁过期时间和请求锁的超时时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     */
    RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.lockKey = lockKey + "_lock";
        this.lockValue = OS.getLogTraceId();
        if (this.lockValue == null){
            this.lockValue = LX.uuid32(10);
        }
    }

    /**
     * 使用默认的请求锁的超时时间,指定锁的过期时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key(Redis的Key)
     * @param expireTime    锁的过期时间(单位:秒)
     */
    RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireTime) {
        this(stringRedisTemplate, lockKey);
        setExpireTime(expireTime);
    }

    @Note("尝试获取锁 超时返回")
    public boolean tryLock(long timeout) {
        // 系统当前时间,毫秒
        long nowTime = System.currentTimeMillis();
        while ((System.currentTimeMillis() - nowTime) < timeout) {
            if (this.lock()) {
                // 上锁成功结束请求
                return true;
            }
            // 每次请求等待一段时间
            sleep(100, 50000);
        }
        return false;
    }

    @Note("尝试获取锁 立即返回")
    public boolean lock() {
        List keys = new ArrayList<>();
        keys.add(lockKey);
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(LOCK_LUA);
        redisScript.setResultType(Long.class);
        Long result = (Long) stringRedisTemplate.execute(redisScript, keys, lockValue , expireTime+"");
        if (result == 1){
            log.debug("分布式锁-加锁:"+lockKey+" "+stringRedisTemplate.opsForHash().get(lockKey,lockValue));
        }
        return result == 1;
    }

    @Note("以阻塞方式的获取锁")
    public boolean lockBlock() {
        while (true) {
            //不存在则添加 且设置过期时间(单位ms)
            if (lock()) {
                return true;
            }
            // 每次请求等待一段时间
            sleep(100, 50000);
        }
    }

    @Note("解锁")
    public boolean unlock() {
        List keys = new ArrayList<>();
        keys.add(lockKey);
        DefaultRedisScript redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(UNLOCK_LUA);
        redisScript.setResultType(Long.class);
        Long result = (Long) stringRedisTemplate.execute(redisScript, keys, lockValue , expireTime+"");
        if (result == 1) {
            log.debug("分布式锁-解锁:" + lockKey + " " + stringRedisTemplate.opsForHash().get(lockKey,lockValue));
        }
        return result == 1;
    }

    /**
     * 线程等待时间
     *
     * @param millis 毫秒
     * @param nanos  纳秒
     */
    private void sleep(long millis, int nanos) {
        try {
            Thread.sleep(millis, random.nextInt(nanos));
        } catch (InterruptedException e) {
            log.debug("获取分布式锁休眠被中断:", e);
            Thread.currentThread().interrupt();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy