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();
		}
	}
}

  

 

-------------------------------------------------------------------------------------------------------------------------java

更新web

新的redis分佈式鎖,基於redis2.6版本以上redis

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;

    }
}

  

替換緣由spring

 一、釋放鎖存在:直接使用redis.del()方法刪除鎖,不限判斷擁有者而直接解鎖的方式,會致使任何客戶端均可以隨時解鎖,即時鎖不是它的。
 二、獲取鎖存在:
2.1:因爲是客戶端本身生成過時時間,因此須要強制要求分佈式下每一個客戶端的時間必須同步.
 2.2:當鎖過時的時候,若是多個客戶端同時執行jedis.getSet()方法,那麼雖然最終只有一個客戶端能夠加鎖,可是這個客戶端的鎖的過時時間可能被其餘客戶端覆蓋。
 2.3:鎖不具有擁有者標識,即任何客戶端均可以解鎖。分佈式

相關文章
相關標籤/搜索