maven + redis + 分布式锁

Maven redis redisTemplate

说明:jedispoolredisTemplate

jedispoolredisTemplate这是俩种利用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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @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 http://blog.csdn.net/java2000_wl

     * @param jedis

     */

    private void returnBrokenResource(Jedis jedis) {

        if (jedis ==null) {

            return;

        }

        try {

            //容错

            jedisPool.returnBrokenResource(jedis);

        } catch (Exception e) {

            LOGGER.error(e.getMessage(), e);

        }

    }

    

    /**

     * @author http://blog.csdn.net/java2000_wl

     * @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);

}

}

猜你喜欢

转载自blog.csdn.net/lixuegen/article/details/52003556