zoukankan      html  css  js  c++  java
  • redis 分布式锁

    package com.mobile.web.portal.util.lock;
    
    import redis.clients.jedis.Jedis;
    
    public class JedisLock {
    
    	private int timeoutMsecs;
    
    	private String lockKey;
    
    	private static long expireMsecs = 1000 * 60 * 1; // min 锁持有超时
    
    	public JedisLock(String lockKey, Integer timeoutMsecs) {
    		this.timeoutMsecs = timeoutMsecs;
    		this.lockKey = lockKey;
    	}
    
    	public synchronized boolean acquire(Jedis jedis)
    			throws InterruptedException {
    		int timeout = timeoutMsecs;
    		while (timeout >= 0) {
    			long expires = System.currentTimeMillis() + expireMsecs + 1;
    			String expiresStr = String.valueOf(expires); // 锁到期时间
    			if (jedis.setnx(lockKey, expiresStr) == 1) {
    				return true;
    			}
    			String currentValueStr = jedis.get(lockKey); // redis里的时间
    			// 表示已经锁失效,要重新设置锁
    			if (currentValueStr != null
    					&& Long.parseLong(currentValueStr) < System
    							.currentTimeMillis()) {
    				// 判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
    				// lock is expired
    				String oldValueStr = jedis.getSet(lockKey, expiresStr);
    				// 获取上一个锁到期时间,并设置现在的锁到期时间,;
    				// 只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
    				if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
    					// 如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
    					return true;
    				}
    			}
    			timeout -= 100;
    			Thread.sleep(100);
    		}
    		return false;
    	}
    
    	public void unLock(Jedis jedis) {
    		jedis.del(lockKey);
    	}
    }
    

      

    package com.mobile.web.portal.util.lock;
    
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisShardInfo;
    
    import com.zhicall.care.system.basic.BeanFactory;
    
    public class RequestValidate {
    
    	private RedisTemplate<Object, Object> redisTemplate = (RedisTemplate<Object, Object>) BeanFactory.getInstance().getBean("redisTemplate");
    	
    	private static final String ERR_MSG = "www!";
    	private static final int TIMEOUTMSECS = 300;
    	/**
    	 * 使用redis 分布式锁对请求验证
    	 * 		
    	 * @return
    	 */
    	public String validate(long a, String b){
    		String lockKey = a+":"+b+"_lock";
    		int timeoutMsecs = TIMEOUTMSECS;
    		JedisLock lock = new JedisLock(lockKey, timeoutMsecs);
    		Jedis jedis = getJedis();
    		try {
    			boolean flag = lock.acquire(jedis);
    			if(!flag){
    				return ERR_MSG;
    			}
    		} catch (InterruptedException e) {
    			return ERR_MSG;
    		}finally {
    			jedis.disconnect();
    		}
    		return null;
    	}
    	
    	private Jedis getJedis(){
    		JedisConnectionFactory factory = (JedisConnectionFactory)redisTemplate.getConnectionFactory();
    		JedisShardInfo shardInfo = factory.getShardInfo();
    		Jedis jedis = shardInfo.createResource();
    		return jedis;
    	}
    	
    	public void unLock(long a, String b){
    		String lockKey = a+":"+b+"_lock";
    		int timeoutMsecs = TIMEOUTMSECS;
    		JedisLock lock = new JedisLock(lockKey, timeoutMsecs);
    		Jedis jedis = getJedis();
    		try {
    			lock.unLock(jedis);
    		}finally {
    			jedis.disconnect();
    		}
    	}
    }
    

      

    -------------------------------------------------------------------------------------------------------------------------

    更新

    新的redis分布式锁,基于redis2.6版本以上

    package com.mobile.web.util.redis;
    
    import java.util.Collections;
    
    import redis.clients.jedis.Jedis;
    
    /**
     * 新的Redis分布式锁实现
     * 基于jedis版本2.8.0以上
     * @author lsnBin
     * @date 2018/01/17
     */
    public class JedisTool {
    
    	private static final String LOCK_SUCCESS = "OK";
    	
    	/** 即当key不存在时,我们进行set操作  */
    	private static final String SET_IF_NOT_EXIST = "NX";
    	
    	private static final String SET_WITH_EXPIRE_TIME = "PX";
    	
    	private static final Long RELEASE_SUCCESS = 1L;
    	
    	/**
    	 * 尝试获取分布式锁
    	 * @param jedis Redis客户端
    	 * @param lockKey 锁
    	 * @param requestId 请求标识
    	 * @param expireTime 超期时间
    	 * @return 是否获取成功
    	 */
    	public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
    		
    		String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
    		
    		if (LOCK_SUCCESS.equals(result)) {
    			return true;
    		}
    		
    		return false;
    	}
    	
    	/**
         * 释放分布式锁
         * @param jedis Redis客户端
         * @param lockKey 锁
         * @param requestId 请求标识
         * @return 是否释放成功
         */
        public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
    
        	//Lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行
        	//首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。
        	//eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。
        	
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
    
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            
            return false;
    
        }
    }
    

      

    替换原因

     1、释放锁存在:直接使用redis.del()方法删除锁,不限判断拥有者而直接解锁的方式,会导致任何客户端都可以随时解锁,即时锁不是它的。
     2、获取锁存在:
    2.1:由于是客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步.
     2.2:当锁过期的时候,如果多个客户端同时执行jedis.getSet()方法,那么虽然最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖。
     2.3:锁不具备拥有者标识,即任何客户端都可以解锁。

  • 相关阅读:
    jdbc学习一半的代码
    实验室的毕业照
    IOS_地图_定位_天气预报_Block回调_单例
    POJ -1062 昂贵的聘礼(前向星 && SPFA)
    【监控】Nagios-NRPE脚本返回值
    cocos2d-x-3.0 Windos 新建项目(coco2d-x 学习笔记一)
    托付与事件
    Netty源代码学习——EventLoopGroup原理:NioEventLoopGroup分析
    vs2013 error c4996: 'fopen': This function or variable may be unsafe
    Customize User Interfaces and Pass User Input to Installer Classes
  • 原文地址:https://www.cnblogs.com/binbang/p/6231682.html
Copyright © 2011-2022 走看看