Redis分佈式鎖

Redis分佈式鎖

1.Redis Template實現分佈式鎖

package org.ssgroup.secondkill.utils;

import javax.annotation.Resource;

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.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import redis.clients.jedis.Jedis;

/**
 * 分佈式鎖的實現
 * @author HX-011
 *
 */
@Component
public class ReidsDistributionLock {
	private final static Logger logger = LoggerFactory.getLogger(ReidsDistributionLock.class);
	
    private String DEFAULT_REDIS_LOCK_KEY = "default_redis_lock";
    private long DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 3000;
    
    @Resource(name="stringRedisTemplate")
    private RedisTemplate<String, String> stringRedisTemplate; 
    
    public synchronized boolean tryLock() {
    	return tryLock(null,null);
    }
    
    public synchronized boolean tryLock(String lockKey) {
    	return tryLock(lockKey,null);
    }
    
    public synchronized boolean tryLock(String lockKey,Long timeOut) {
    	String redisLockKey = DEFAULT_REDIS_LOCK_KEY;
    	if(!StringUtils.isEmpty(lockKey)) {
    		redisLockKey = lockKey + "_lock";
    	}
    	
    	long expires = System.currentTimeMillis() + DEFAULT_ACQUIRY_RESOLUTION_MILLIS + 1;
    	if(null!=timeOut && timeOut > 0) {
    		expires = System.currentTimeMillis() + timeOut + 1;
    	}
    	
    	//獲取鎖
    	boolean isLock = this.setNX(redisLockKey, String.valueOf(expires));
    	if(isLock) {
    		logger.info("獲取到分佈式鎖:{}", redisLockKey);
    		return true;
    	}
    	
    	//判斷是否超時
    	String expireStr = this.get(redisLockKey);
    	if(!StringUtils.isEmpty(expireStr) && Long.parseLong(expireStr) < System.currentTimeMillis()) {
    		//獲取上個鎖時間
    		long newExpires = System.currentTimeMillis() + DEFAULT_ACQUIRY_RESOLUTION_MILLIS + 1;
    		if(null!=timeOut && timeOut > 0) {
    			newExpires = System.currentTimeMillis() + timeOut + 1;
        	}
    		
    		String oldExpiresStr = this.getSet(redisLockKey, String.valueOf(newExpires));
    		//防止誤刪
    		if(!StringUtils.isEmpty(oldExpiresStr) && oldExpiresStr.equals(expireStr)) {
    			logger.info("獲取到分佈式鎖:{}", redisLockKey);
    			return true;
    		}
    	}
    	logger.debug("沒有獲取到分佈式鎖:{}", redisLockKey);
    	return false;
    }
    
    /**
     * 多服務器集羣,使用下面的方法,代替System.currentTimeMillis(),獲取redis時間,避免多服務的時間不一致問題!!!
     * @return
     */
    public long currtTimeForRedis() {
        return stringRedisTemplate.execute(new RedisCallback<Long>() {
			@Override
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				return connection.time();
			}
		});
    }
    
    private boolean setNX(final String key,final String expire) {
    	Object isLock = false;
    	try {
    		isLock = stringRedisTemplate.execute(new RedisCallback<Object> () {
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					StringRedisSerializer serializer = new StringRedisSerializer();
					return connection.setNX(serializer.serialize(key), serializer.serialize(expire));
				}
			});
    	}catch (Exception e) {
			e.printStackTrace();
			logger.error("setNX redis error, key : {} | value : {}", key, expire);
		}
    	return null==isLock?false:(Boolean) isLock;
    }
    
    private String get(final String key) {
    	Object obj = null;
    	try {
    		obj = stringRedisTemplate.execute(new RedisCallback<Object>() {
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					StringRedisSerializer serializer = new StringRedisSerializer();
					byte[] data = connection.get(serializer.serialize(key));
					return data==null?null:serializer.deserialize(data);
				}
			});
    	}catch (Exception e) {
			e.printStackTrace();
			logger.error("get redis error, key : {} | return value : {}", key, obj);
		}
    	return obj==null?null:obj.toString();
    }
    
    private String getSet(final String key,final String value) {
    	Object obj = null;
    	try {
    		obj = stringRedisTemplate.execute(new RedisCallback<Object>() {
				@Override
				public Object doInRedis(RedisConnection connection) throws DataAccessException {
					StringRedisSerializer serializer = new StringRedisSerializer();
					byte[] data = connection.getSet(serializer.serialize(key), serializer.serialize(value));
					return data==null?null:serializer.deserialize(data);
				}
    		});
    	}catch (Exception e) {
			e.printStackTrace();
			logger.error("getSet redis error, key : {} | new value : {} | return value : {}", key, value, obj);
		}
    	return obj==null?null:obj.toString();
    }
    
    public synchronized void unlock() {
    	stringRedisTemplate.delete(DEFAULT_REDIS_LOCK_KEY);
    }
    
    public synchronized void unlock(String lockKey) {
    	stringRedisTemplate.delete(lockKey + "_lock");
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章