Maven redis redisTemplate
说明:jedispool、redisTemplate
jedispool、redisTemplate这是俩种利用redis缓存的分布式锁。
redisTemplate
Pom.xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.3.4.RELEASE</version>
</dependency>
applicationContext-config.xml
<bean id="configurationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/redis.properties</value>
</list>
</property>
</bean>
Spring.xml
<bean id="redisDistributedLock" class="com.hp.wandafilm.pts.util.RedisDistributedLock">
<property name="redisTemplate" ref="redisTemplate"></property>
</bean>
<!-- redisTemplate配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,封装使操作更便捷 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
<!-- Jedis ConnectionFactory 数据库连接配置-->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="poolConfig" ref="jedisPoolConfig" />
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
</bean>
<!-- Jedis 连接池配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
Redis.properties
redis.host=服务ip
redis.port=6379
redis.maxIdle=11
redis.maxTotal=12
redis.maxWaitMillis=1000
redis.testOnBorrow=true
RedisDistributedLock .java
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;
/**
* redis分布式锁
* @author robin
*
*/
public class RedisDistributedLock {
private static final Log log = LogFactory.getLog(RedisDistributedLock.class);
private RedisTemplate<String, Long> redisTemplate;
/**
* 锁失效时长,默认1天
*/
private long lockTimeOut = 1000 * 3600 * 24L;
/**
* 获取锁等待时长(获取单个锁的时候使用),默认20s
*/
private long lockWaitTime = 1000 * 20L;
/**
* 获取锁自旋时长(获取一组锁的时候使用),默认1s
*/
private long spinTime = 1000 * 1L;
/**
* 休眠时长,以毫秒为单位
* 默认为1000毫秒
*/
private long sleeptime = 1000 * 1L;
/**
* 当前时间
*/
private long currentLockTime;
/**
* 加锁
* @return 返回 true 成功 false 失败
*/
public boolean lock(String key){
log.info(String.format("获取分布式锁请求参数【锁的名称 = %s,锁失效时长 = %s,获取锁等待时间 = %s,获取锁自旋时长 = %s,休眠时间 = %s】", key, lockTimeOut, lockWaitTime, spinTime, sleeptime));
try{
//设置超时时间
RedisCacheManager redisManage = new RedisCacheManager(redisTemplate);
Map<String, Long> expires = new HashMap<String, Long>();
expires.put(key, lockTimeOut);
//如果key不存在, 直接返回false
if(!StringUtils.isNotBlank(key)){
log.info(String.format("获取分布式锁失败,分布式锁%s不存在!", "key"));
throw new Exception("抛出异常信息");//修改为自定义异常
}
//等待时间小于失效时间否则不合理
if (lockWaitTime > lockTimeOut) {
log.info(String.format("设置分布式锁过期时间[%s]或获取分布式锁等待时间[%s]有误]!",String.valueOf(lockTimeOut),String.valueOf(lockWaitTime)));
throw new Exception("抛出异常信息");//修改为自定义异常
}
//获取当前时间
currentLockTime = System.currentTimeMillis();
//等待超时时间
long lockWaitTimeAt = lockWaitTime + currentLockTime;
while(true) {
//取得当前时间
currentLockTime = System.currentTimeMillis();
//等待超时时间 1468919062570 1468919068071
if(lockWaitTimeAt <= currentLockTime){
break;
}
//获取锁
Boolean boo = redisTemplate.opsForValue().setIfAbsent(key,currentLockTime);
if(boo){
//获取所成功
log.info(String.format("直接获取锁key:%s,当前时间:%s", key,currentLockTime));
//设置锁失效时间
redisManage.setExpires(expires);
return true;
}else{
//其他线程占用了锁
log.info(String.format("检测到锁被占用{key: %s, 当前时间: %s}", key, currentLockTime));
Long otherLockTime = redisTemplate.opsForValue().get(key);
if(otherLockTime == null) {
// 其他系统释放了锁
// 立刻重新尝试加锁
log.info(String.format("检测到锁被释放{key: %s, 当前时间: %s}", key, currentLockTime));
continue;
} else {
if(currentLockTime - otherLockTime >= lockTimeOut) {
//锁超时
//尝试更新锁
log.info(String.format("检测到锁超时{key: {%s}, 当前时间: {%s}, 检测到锁的时间: {%s}, 超时时间:{%s}}", key, currentLockTime,otherLockTime,lockTimeOut));
Long otherLockTime2 = redisTemplate.opsForValue().getAndSet(key, currentLockTime);
if(otherLockTime2 == null || otherLockTime.equals(otherLockTime2)) {
log.info(String.format("获取到超时锁{key: {%s}, currentLockTime: {%s}, otherLockTime: {%s}, otherLockTime2: {%s}}", key, currentLockTime, otherLockTime, otherLockTime2));
//设置锁失效时间
redisManage.setExpires(expires);
return true;
} else {
sleep();
//重新尝试加锁
log.info(String.format("重新尝试加锁{key: %s, 当前时间: %s}", key, currentLockTime));
continue;
}
} else {
//锁未超时
sleep();
//重新尝试加锁
log.info(String.format("重新尝试加锁{key: %s, 当前时间: %s}", key, currentLockTime));
continue;
}
}
}
}
}catch (Exception e) {
log.error(String.format("获取分布式锁异常,异常信息:" + e.getMessage(),e));
//抛出自定义异常信息
}
return false;
}
/**
* 解锁
* @param key
* @return
*/
public boolean unLock(String key) {
log.info(String.format("解锁{key: {%s}}", key));
redisTemplate.delete(key);
return true;
}
/**
* 休眠<br />
* @param sleeptime
*/
private void sleep() {
try {
Thread.sleep(sleeptime);
} catch (InterruptedException e) {
log.error("线程异常中断异常,"+e.getMessage(),e);
//抛出自定义异常信息
}
}
public long getLockTimeOut() {
return lockTimeOut;
}
/**
* 锁失效时长,默认1天 单位:S
* @param lockTimeOut
*/
public void setLockTimeOut(long lockTimeOut) {
this.lockTimeOut = lockTimeOut * 1000;
}
public long getLockWaitTime() {
return lockWaitTime;
}
/**
* 获取锁等待时长(获取单个锁的时候使用),默认20s 单位:S
* @param lockWaitTime
*/
public void setLockWaitTime(long lockWaitTime) {
this.lockWaitTime = lockWaitTime * 1000;
}
public long getSpinTime() {
return spinTime;
}
/**
* 获取锁自旋时长(获取一组锁的时候使用),默认1s 单位:S
* @param spinTime
*/
public void setSpinTime(long spinTime) {
this.spinTime = spinTime * 1000;
}
public long getSleeptime() {
return sleeptime;
}
/**
* 休眠时长,默认1s 单位 :S
* @param sleeptime
*/
public void setSleeptime(long sleeptime) {
this.sleeptime = sleeptime * 1000;
}
public long getCurrentLockTime() {
return currentLockTime;
}
public void setCurrentLockTime(long currentLockTime) {
this.currentLockTime = currentLockTime * 1000;
}
public RedisTemplate<String, Long> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Long> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Test
public void test(){
Jedis jedis = new Jedis("10.199.88.169", 6379);
try {
long l = jedis.setnx("qwe","qwe");
// RedisTemplate<String, Long> redisTemplate = redisTemplate.opsForValue().setIfAbsent();
long ll = jedis.setnx("qwe","qwe");
System.out.println(l);
System.out.println(ll);
}catch (Exception e) {
e.printStackTrace();
}
}
}
redistDemo.java
public class redistDemo {
public static void main(String[] args) {
ApplicationContext appContext5 = new ClassPathXmlApplicationContext(new String[]{"classpath*:spring/applicationContext-config.xml","classpath*:spring/spring.xml"});
RedisDistributedLockrd = (RedisDistributedLock)appContext5.getBean("redisDistributedLock");
rd.setLockTimeOut(20L);
rd.setLockWaitTime(30L);
boolean bo = rd.lock("redistDemo");
System.out.println(bo);
}
}
JedisPool
Pom.xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
</dependency>
applicationContext-config.xml
<bean id="configurationProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/redis.properties</value>
</list>
</property>
</bean>
Spring.xml
<!-- redis-jedisPool分布式锁加载 -->
<bean id="redisBillLockHandler" class="com.hp.wandafilm.pts.util.RedisBillLockHandler">
<property name="jedisPool" ref="jedisPool"></property>
</bean>
<!-- jedisPool -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1" value="${redis.host}"/>
<constructor-arg index="2" value="${redis.port}" type="int"/>
</bean>
<!-- Jedis 连接池配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
Redis.properties
redis.host=服务ip
redis.port=6379
redis.maxIdle=11
redis.maxTotal=12
redis.maxWaitMillis=1000
redis.testOnBorrow=true
IBillIdentify.java
public class IBillIdentify {
private StringpayOrderId;
private StringKey = "iIBillIdentify_KEY_" ;
public Object uniqueIdentify(){
return Key+payOrderId;
}
public String getPayOrderId() {
return payOrderId;
}
public void setPayOrderId(String payOrderId) {
this.payOrderId = payOrderId;
}
}
RedisBillLockHandler.java
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisConnectionException;
/**
*
* @author long
*
*/
public class RedisBillLockHandler {
private static final LogLOGGER = LogFactory.getLog(RedisBillLockHandler.class);
private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;
private static final int DEFAULT_BATCH_EXPIRE_TIME = 6;
private JedisPool jedisPool;
/**
* 获取锁 如果锁可用 立即返回true, 否则返回false
* @author
* @see com.fx.platform.components.lock.IBillLockHandler#tryLock(com.fx.platform.components.lock.IBillIdentify)
* @param billIdentify
* @return
*/
public boolean tryLock(IBillIdentify billIdentify) {
return tryLock(billIdentify, 0L,null);
}
/**
* 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false
* @author
* @see com.fx.platform.components.lock.IBillLockHandler#tryLock(com.fx.platform.components.lock.IBillIdentify,
* long, java.util.concurrent.TimeUnit)
* @param billIdentify
* @param timeout
* @param unit
* @return
*/
public boolean tryLock(IBillIdentify billIdentify,long timeout, TimeUnit unit) {
String key = (String) billIdentify.uniqueIdentify();
Jedis jedis = null;
try {
jedis = getResource();
long nano = System.nanoTime();
do {
LOGGER.debug("try lock key: " + key);
Long i = jedis.setnx(key, key);
if (i == 1) {
jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
LOGGER.debug("get lock, key: " + key +" , expire in " +DEFAULT_SINGLE_EXPIRE_TIME +" seconds.");
return Boolean.TRUE;
} else {// 存在锁
if (LOGGER.isDebugEnabled()) {
String desc = jedis.get(key);
LOGGER.debug("key: " + key +" locked by another business:" + desc);
}
}
if (timeout == 0) {
break;
}
Thread.sleep(300);
} while ((System.nanoTime() - nano) < unit.toNanos(timeout));
return Boolean.FALSE;
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
returnResource(jedis);
}
return Boolean.FALSE;
}
/**
* 如果锁空闲立即返回 获取失败 一直等待
* @author
* @see com.fx.platform.components.lock.IBillLockHandler#lock(com.fx.platform.components.lock.IBillIdentify)
* @param billIdentify
*/
public void lock(IBillIdentify billIdentify) {
String key = (String) billIdentify.uniqueIdentify();
Jedis jedis = null;
try {
jedis = getResource();
do {
LOGGER.debug("lock key: " + key);
Long i = jedis.setnx(key, key);
if (i == 1) {
jedis.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
LOGGER.debug("get lock, key: " + key +" , expire in " +DEFAULT_SINGLE_EXPIRE_TIME +" seconds.");
return;
} else {
if (LOGGER.isDebugEnabled()) {
String desc = jedis.get(key);
LOGGER.debug("key: " + key +" locked by another business:" + desc);
}
}
Thread.sleep(300);
} while (true);
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
returnResource(jedis);
}
}
/**
* 释放锁
* @author
* @see com.fx.platform.components.lock.IBillLockHandler#unLock(com.fx.platform.components.lock.IBillIdentify)
* @param billIdentify
*/
public void unLock(IBillIdentify billIdentify) {
List<IBillIdentify> list = new ArrayList<IBillIdentify>();
list.add(billIdentify);
unLock(list);
}
/**
* 批量获取锁 如果全部获取 立即返回true, 部分获取失败 返回false
* @author
* @date 2013-7-22 下午10:27:44
* @see com.fx.platform.components.lock.IBatchBillLockHandler#tryLock(java.util.List)
* @param billIdentifyList
* @return
*/
public boolean tryLock(List<IBillIdentify> billIdentifyList) {
return tryLock(billIdentifyList, 0L,null);
}
/**
* 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false
* @author
* @param billIdentifyList
* @param timeout
* @param unit
* @return
*/
public boolean tryLock(List<IBillIdentify> billIdentifyList,long timeout, TimeUnit unit) {
Jedis jedis = null;
try {
List<String> needLocking = new CopyOnWriteArrayList<String>();
List<String> locked = new CopyOnWriteArrayList<String>();
jedis = getResource();
long nano = System.nanoTime();
do {
// 构建pipeline,批量提交
Pipeline pipeline = jedis.pipelined();
for (IBillIdentify identify : billIdentifyList) {
String key = (String) identify.uniqueIdentify();
needLocking.add(key);
pipeline.setnx(key, key);
}
LOGGER.debug("try lock keys: " + needLocking);
// 提交redis执行计数
List<Object> results = pipeline.syncAndReturnAll();
for (int i = 0; i < results.size(); ++i) {
Long result = (Long) results.get(i);
String key = needLocking.get(i);
if (result == 1) { //setnx成功,获得锁
jedis.expire(key, DEFAULT_BATCH_EXPIRE_TIME);
locked.add(key);
}
}
needLocking.removeAll(locked); // 已锁定资源去除
if (CollectionUtils.isEmpty(needLocking)) {
return true;
} else {
// 部分资源未能锁住
LOGGER.debug("keys: " + needLocking +" locked by another business:");
}
if (timeout == 0) {
break;
}
Thread.sleep(500);
} while ((System.nanoTime() - nano) < unit.toNanos(timeout));
// 得不到锁,释放锁定的部分对象,并返回失败
if (!CollectionUtils.isEmpty(locked)) {
jedis.del(locked.toArray(new String[0]));
}
return false;
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
returnResource(jedis);
}
return true;
}
/**
* 批量释放锁
* @author
* @see com.fx.platform.components.lock.IBatchBillLockHandler#unLock(java.util.List)
* @param billIdentifyList
*/
public void unLock(List<IBillIdentify> billIdentifyList) {
List<String> keys = new CopyOnWriteArrayList<String>();
for (IBillIdentify identify : billIdentifyList) {
String key = (String) identify.uniqueIdentify();
keys.add(key);
}
Jedis jedis = null;
try {
jedis = getResource();
jedis.del(keys.toArray(new String[0]));
LOGGER.debug("release lock, keys :" + keys);
} catch (JedisConnectionException je) {
LOGGER.error(je.getMessage(), je);
returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
returnResource(jedis);
}
}
/**
* @author
* @date 2013-7-22 下午9:33:45
* @return
*/
private Jedis getResource() {
return jedisPool.getResource();
}
public JedisPool getJedisPool() {
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 销毁连接
* @author
* @param jedis
*/
private void returnBrokenResource(Jedis jedis) {
if (jedis ==null) {
return;
}
try {
//容错
jedisPool.returnBrokenResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
/**
* @author
* @param jedis
*/
private void returnResource(Jedis jedis) {
if (jedis ==null) {
return;
}
try {
jedisPool.returnResource(jedis);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
}
redistDemo.java
public class redistDemo {
public static void main(String[] args) {
ApplicationContext appContext5 =new ClassPathXmlApplicationContext(new String[]{"classpath*:spring/applicationContext-config.xml","classpath*:spring/spring.xml"});
RedisBillLockHandler rb = (RedisBillLockHandler)appContext5.getBean("redisBillLockHandler");
IBillIdentify billIdentify = new IBillIdentify();
billIdentify.setPayOrderId("1231232312312345");
boolean bo = rb.tryLock(billIdentify);
System.out.println(bo);
}
}