发布于 4年前

Redis分布式锁实现类

package com.dami.util.redis;
import java.util.Collections;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import redis.clients.jedis.Jedis;

/**
 * Redis分布式锁实现类
 * @ClassName: RedisDistributedLock
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author Administrator
 * @date 2018-11-22 下午2:11:07
 */
public class RedisDistributedLock{

    private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);
    // 锁标志
    public static final String PREFIX = "lock_";

    // SET_IF_NOT_EXIST 标志,表示如果当key不存在时进行set操作,否则不处理
    private static final String NX = "NX";
    // 过期时间标志, EX 表示以秒作为单位
    private static final String EX = "EX";
    // 锁成功标志
    private static final String LOCK_SUCCESS = "OK";
    // 解锁成功标志
    private static final long DEL_SUCCESS = 1L;
    // 默认过期时间
    private static final int DEFAULT_EXPIRE_TIME = 180;
    // 默认重试次数
    private static final int DEFAULT_RETRY_TIME = 3;
    // 默认重试暂停时间
    private static final int DEFAULT_INTERNAL_TIME = 100;
    // 解锁时的 lua语言Script
    private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    // 过期时间(秒)
    private int expireSeconds;
    // 锁重试次数
    private int retryTimes;
    // 锁重试间隔
    private int internalTime;
    // 加锁者标志, 解锁时只有与加锁时的值一致才允许解锁
    private final String lockId;
    @Autowired
    private static RedisTemplate<String, Object> redisTemplate;

    public RedisDistributedLock() {
        this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
    }
    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate) {
        this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
    }

    public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
        this.redisTemplate = redisTemplate;
        this.expireSeconds = expireSeconds;
        this.retryTimes = retryTimes;
        this.internalTime = internalTime;
        this.lockId = UUID.randomUUID().toString();
    }
    /**
     * 获取redis 锁
     * @Title: lock
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @author Administrator
     * @date 2018-11-22 下午1:55:57
     * @param key
     * @return
     */
    public boolean lock(String key, final String lockId) {
        if (StringUtils.isBlank(key)) {
            return false;
        }

        final String fullKey = PREFIX + key;
        //logger.debug("尝试获取锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
        System.out.println("尝试获取锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
        for (int i = 0; i < retryTimes; i++) {
            //logger.debug("第 {} 次尝试获取锁", i + 1);
            System.out.println("第 {"+(i + 1)+"} 次尝试获取锁");

            String status = redisTemplate.execute(new RedisCallback<String>() {
                @Override
                public String doInRedis(RedisConnection connection) throws DataAccessException {
                    try {
                        Jedis jedis = (Jedis) connection.getNativeConnection();
                        return jedis.set(fullKey, lockId, NX, EX, expireSeconds);
                    } catch (Exception e) {
                        return "ERROR";
                    }
                }
            });
            if (LOCK_SUCCESS.equalsIgnoreCase(status)) {
                //logger.debug("获取锁成功, 锁Key:{}, 锁标识:{}", fullKey, lockId);
                System.out.println("获取锁成功, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
                return true;
            }
            try {
                Thread.sleep(internalTime);
            } catch (InterruptedException e) {
                break;
            }
        }
        //logger.debug("尝试获取锁 {} 次后失败, 锁Key:{}", retryTimes, fullKey);
        System.out.println("尝试获取锁 {"+retryTimes+"} 次后失败, 锁Key:{"+fullKey+"}" );
        return false;
    }
    /**
     * 释放redis锁
     * @Title: unlock
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @author Administrator
     * @date 2018-11-22 下午1:55:34
     * @param key
     * @return
     */
    public boolean unlock(String key, final String lockId) {
        if (StringUtils.isBlank(key)) {
            return false;
        }
        final String fullKey = PREFIX + key;
        //logger.debug("尝试解锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
        System.out.println("尝试解锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
        Object value = redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                try {
                    Jedis jedis = (Jedis) connection.getNativeConnection();
                    return jedis.eval(DEL_SCRIPT, Collections.singletonList(fullKey), Collections.singletonList(lockId));
                } catch (Exception e) {
                    return -1L;
                }
            }
        });
        boolean isUnlock = value!=null && value.equals(DEL_SUCCESS);
        if (isUnlock) {
            //logger.debug("解锁成功, 锁Key:{}", fullKey);
            System.out.println("解锁成功, 锁Key:{"+fullKey+"}");
        }
        return isUnlock;
    }
    /**
     * 
     * @Title: get
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @author Administrator
     * @date 2018-11-22 下午2:38:28
     * @param key
     * @return
     */
    public String get( String key) {
        String obj = null;
        final String fullKey = PREFIX + key;
        try {
            obj = redisTemplate.execute(new RedisCallback<String>() {
                @Override
                public String doInRedis(RedisConnection connection) throws DataAccessException {
                    Jedis jedis = (Jedis) connection.getNativeConnection();
                    return jedis.get(fullKey);
                }
            });
            String result =  obj;
            return result;
        } catch (Exception e) {
            logger.error("get redis occured an exception", e);
        }
        return "";
    }
    public String getLockId() {
        return lockId;
    }

    public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate) {
        return new RedisDistributedLock(redisTemplate);
    }

    public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) {
        return new RedisDistributedLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
    }

    public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
        return new RedisDistributedLock(redisTemplate, expireSeconds, retryTimes, internalTime);
    }

}
©2020 edoou.com   京ICP备16001874号-3