基于redis实现分布式锁(方式2)

178 阅读1分钟

背景

之前介绍了一种方式,这里又介绍了一种方式,不过都差不多,都是两步
1.获取锁
2.释放锁


之前介绍的一种方式参考 juejin.cn/post/687980…

原理

实现差不多。

之前是基于redis客户端实现,现在是基于spring RedisTemplate实现,不过都差不多,因为spring RedisTemplate只是一个门面,真正的实现还是基于redis客户端。不过不是jedis,而是其他的客户端。

RedisTemplate只是spring-data-redis.jar里的一个类,基于门面设计模式的一个类,具体实现是各个不同的redis客户端。早期是jedis客户端,现在默认是lettuce。

门面模式

架构图

门面模式就是为了可以切换不同的客户端。


关于门面模式参考 juejin.cn/post/684490…

代码

package xxx.common.redis;

import java.util.Collections;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

public class DistributedLock<T> implements BeanFactoryAware{
	
	private RedisTemplate<String, T> redisTemplate;
	
	private static final Long SUCCESS = 1L;
	
	public static final String BEAN_NAME="distributedLock";
	
	private Log logger = LogFactory.getLog(this.getClass());
	
    /**
     * 获取锁
     * @param lockKey
     * @param value
     * @param expireTime:单位-秒
     * @return
     */
    public boolean getLock(String lockKey, String value, int expireTime){
        boolean ret = false;
        try{
            String script = "if redis.call('set',KEYS[1],ARGV[1],'NX','EX',ARGV[2]) then return 1 else return 0 end";
            //指定ReturnType为Long.class,只支持List.class, Boolean.class和Long.class
            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);

            Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime);
 
            if(SUCCESS.equals(result)){
                return true;
            }
 
        }catch(Exception e){
        	logger.error("获取分布式锁异常", e);
        }
        return ret;
    }
 
    /**
     * 释放锁
     * @param lockKey
     * @param value
     * @return
     */
    public boolean releaseLock(String lockKey, String value){
 
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
 
        RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
 
        Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value);
        if(SUCCESS.equals(result)) {
            return true;
        }
 
        return false;
    }
    
	@SuppressWarnings("unchecked")
	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.redisTemplate = (RedisTemplate<String, T>)beanFactory.getBean("redisTemplate");
	}

}

redis客户端

redis不止一个客户端。

Redis Java客户端有很多的开源产品,比如Redission、Jedis、lettuce。


jedis
是最早的客户端,spring-data-redis.jar(即RedisTemplate)早期用这个,后面用lettuce。


Redission
是官方的一个客户端,很晚才出现,主要是为了解决分布式锁问题。

本质

就是两个进程写同一个数据,比如要修改同一条记录数据,就必须使用分布式锁,否则就可能数据被篡改。

如果只是单个进程,只需要使用同步关键字即可。

参考

mp.weixin.qq.com/s/qJK61ew0k…

juejin.cn/post/684490…

blog.csdn.net/striveb/art…