ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

redis分布式锁实现

2022-04-22 12:33:02  阅读:153  来源: 互联网

标签:return String 实现 lock redis private lockKey redisTemplate 分布式


最近项目中使用到了redis实现的分布式锁,自定义的分布式锁支持自旋和可冲入等,是一个不错的实践,这里记录下

/**
 * @description: redis分布式锁
 * @author: cc.wang
 * @createDate: 2022-04-22 12:13
 * @version: 1.0
 */
public class RedisLock {
    private static Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private RedisTemplate<String, Object> redisTemplate;

    private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 100;

    /**
     * Lock key path.
     */
    private String lockKey;

    /**
     * 锁超时时间,防止线程在入锁以后,无限的执行等待
     */
    private int expireMsecs = 3000;

    /**
     * 锁等待时间,防止线程饥饿
     */
    private int timeoutMsecs = 100;

    private volatile boolean locked = false;

    private String requestClientId = "";
    protected static final String UNLOCK_LUA;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }

    /**
     * Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
     *
     * @param lockKey lock key (ex. account:1, ...)
     */
    public RedisLock(RedisTemplate<String, Object> redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey + "_lock";
    }

    /**
     * Detailed constructor with default lock expiration of 60000 msecs.
     */
    public RedisLock(RedisTemplate<String, Object> redisTemplate, String lockKey, int timeoutMsecs) {
        this(redisTemplate, lockKey);
        this.timeoutMsecs = timeoutMsecs;
    }

    /**
     * Detailed constructor.
     */
    public RedisLock(RedisTemplate<String, Object> redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs) {
        this(redisTemplate, lockKey, timeoutMsecs);
        this.expireMsecs = expireMsecs;
    }

    /**
     * @return lock key
     */
    public String getLockKey() {
        return this.lockKey;
    }

    private String get(final String key) {
        try {
            RedisCallback<String> callback = (connection) -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.get(key);
            };
            String result = redisTemplate.execute(callback);
            return result;
        } catch (Exception e) {
            logger.error("获取锁内容异常", e);
        }
        return "";
    }

    private boolean setNX(final String key, final String value) {
        try {
            RedisCallback<String> callback = (connection) -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.set(lockKey, value, "NX", "PX", this.expireMsecs);
            };
            String result = redisTemplate.execute(callback);
            logger.debug("locked result: {}", result);
            return StringUtils.isNotBlank(result);
        } catch (Exception e) {
            logger.error("加锁失败", e);
        }
        return false;
    }

    /**
     * @return true if lock is acquired, false acquire timeouted
     * @throws InterruptedException in case of thread interruption
     */
    public synchronized boolean lock() throws InterruptedException {
        int timeout = timeoutMsecs;
        if (StringUtils.isBlank(this.requestClientId)) {
            requestClientId = UUID.randomUUID().toString();
            logger.debug("redis lock requestClientId: {}", requestClientId);
        }
        //支持自旋
        while (timeout >= 0) {
            boolean isLocked = this.setNX(this.lockKey, this.requestClientId);
            if (isLocked) {
                this.locked = isLocked;
                return true;
            }

            if (this.locked) {
                //支持重入
                String value = this.get(this.lockKey);
                logger.debug("locked requestClientId: {}", value);
                if (StringUtils.equalsIgnoreCase(this.requestClientId, value)) {
                    return true;
                }
            }

            timeout -= DEFAULT_ACQUIRY_RESOLUTION_MILLIS;
            if (timeout > 0) {
                doSleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
            }

        }
        return false;
    }

    /**
     * 休眠一段时间
     *
     * @param milliseconds
     * @throws InterruptedException
     */
    private void doSleep(long milliseconds) throws InterruptedException {
        Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
    }

    /**
     * Acqurired lock release.
     */
    public synchronized boolean unlock() {
        if (!this.locked) {
            //未取得锁时直接返回
            return false;
        }

        try {
            List<String> keys = Lists.newArrayList(this.lockKey);
            List<String> args = Lists.newArrayList(this.requestClientId);

            // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
            // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
            RedisCallback<Long> callback = (connection) -> {
                Object nativeConnection = connection.getNativeConnection();
                // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行

                if (nativeConnection instanceof JedisCluster) { // 集群模式
                    return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
                } else if (nativeConnection instanceof Jedis) { // 单机模式
                    return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
                }
                return 0L;
            };
            Long result = redisTemplate.execute(callback);

            boolean reply = result != null && result > 0;
            if (reply) {
                this.locked = false;
            }
            return reply;
        } catch (Exception e) {
            logger.error("release lock occured an exception", e);
        }
        return false;
    }
}

image

标签:return,String,实现,lock,redis,private,lockKey,redisTemplate,分布式
来源: https://www.cnblogs.com/lovelywcc/p/16178357.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有