zoukankan      html  css  js  c++  java
  • 基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有測试代码(何志雄)

    转载请标明出处。


    在分布式系统中,常常会出现须要竞争同一资源的情况,本代码基于redis3.0.1+jedis2.7.1实现了分布式锁。

    redis集群的搭建,请见我的另外一篇文章:<>《redis3.0.1集群环境搭建》

    可用于比如秒杀系统中的商品库存的管理。付完整代码及測试用例。

    package com.gaojiasoft.gaojiaRedis;
    
    import java.util.UUID;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.PostConstruct;
    
    import org.apache.commons.lang3.RandomUtils;
    import org.apache.commons.lang3.concurrent.BasicThreadFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import redis.clients.jedis.JedisCluster;
    import redis.clients.jedis.exceptions.JedisConnectionException;
    
    /**
     * 分布式锁管理器,支持对单个资源加锁解锁。或给一批资源的批量加锁及解锁
    * @ClassName: DistributedLockManger 
    * @Description:  
    * @author 何志雄 001
    * @company 南京高嘉软件技术有限公司
    * @date 2015年6月3日 下午5:44:06
     */
    public class DistributedLockManger
    {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLockManger.class);
    
        private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;
    
        //    private static final int DEFAULT_BATCH_EXPIRE_TIME = 6;
    
        //static的变量无法注解
        @Autowired
        JedisCluster jc;
    
        private static DistributedLockManger lockManger;
    
        public DistributedLockManger()
        {
        }
    
        @PostConstruct
        public void init()
        {
            lockManger = this;
            lockManger.jc = this.jc;
        }
    
        /**
         * 获取锁 假设锁可用   马上返回true。  否则马上返回false,作为非堵塞式锁使用 
         * @param key
         * @param value
         * @return
         */
        public static boolean tryLock(String key , String value)
        {
            try
            {
                return tryLock(key, value, 0L, null);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            return false;
        }
    
        /**
         * 锁在给定的等待时间内空暇,则获取锁成功 返回true, 否则返回false。作为堵塞式锁使用
         * @param key 锁键
         * @param value 被谁锁定
         * @param timeout 尝试获取锁时长。建议传递500,结合实践单位。则可表示500毫秒
         * @param unit。建议传递TimeUnit.MILLISECONDS
         * @return
         * @throws InterruptedException 
         */
        public static boolean tryLock(String key , String value , long timeout , TimeUnit unit) throws InterruptedException
        {
            //纳秒
            long begin = System.nanoTime();
            do
            {
                //LOGGER.debug("{}尝试获得{}的锁.", value, key);
                Long i = lockManger.jc.setnx(key, value);
                if (i == 1)
                {
                    lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
                    LOGGER.debug("{}成功获取{}的锁,设置锁过期时间为{}秒 ", value, key, DEFAULT_SINGLE_EXPIRE_TIME);
                    return true;
                }
                else
                {
                    // 存在锁 ,但可能获取不到,原因是获取的一刹那间
    //                String desc = lockManger.jc.get(key);
    //                LOGGER.error("{}正被{}锁定.", key, desc);
                }
                if (timeout == 0)
                {
                    break;
                }
                //在其睡眠的期间,锁可能被解,也可能又被他人占用,但会尝试继续获取锁直到指定的时间
                Thread.sleep(100);
            }
            while ((System.nanoTime() - begin) < unit.toNanos(timeout));
            //因超时没有获得锁
            return false;
        }
    
        /**
         * 释放单个锁
         * @param key 锁键
         * @param value 被谁释放
         */
        public static void unLock(String key , String value)
        {
            try
            {
                lockManger.jc.del(key);
                LOGGER.debug("{}锁被{}释放 .", key, value);
            }
            catch (JedisConnectionException je)
            {
    
            }
            catch (Exception e)
            {
    
            }
        }
    
    public void test() throws InterruptedException
        {
            String productId = "18061249844";
            String userId;
            for (int i = 1; i <= 500; i++)
            {
                //随机产生一个用户
                userId = UUID.randomUUID().toString();
                //该用户试图锁定(假设被锁,尝试等待300毫秒)。在处理一些事情后。再释放锁
                testLock(productId, userId);
                Thread.sleep(20);
            }
        }
    
        private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(150, 150, 30L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new BasicThreadFactory.Builder().daemon(true)
                        .namingPattern("mongo-oper-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
    
        private static void testLock(final String key , final String value)
        {
            executor.execute(new Runnable()
            {
                @Override
                public void run()
                {
                    try
                    {
                        //获取锁,假设不能马上获取,尝试等待1000毫秒
                        boolean isLock = DistributedLockManger.tryLock(key, value, 500, TimeUnit.MILLISECONDS);
                        if (isLock)
                        {
                            //doSomething(占用锁20毫秒到4秒,模拟处理事务)
                            long doSomeThingTime = RandomUtils.nextLong(20, 4000);
                            LOGGER.debug("{}将持有锁{}时长{}毫秒.", value, key, doSomeThingTime);
                            Thread.sleep(doSomeThingTime);
                            //然后释放锁
                            DistributedLockManger.unLock(key, value);
                        }
                    }
                    catch (Throwable th)
                    {
                    }
                }
            });
        }
    
        public JedisCluster getJc()
        {
            return jc;
        }
    
        public void setJc(JedisCluster jc)
        {
            this.jc = jc;
        }
    }
    

    Spring配置:

    redis.properties

    #最大闲置连接数
    im.hs.server.redis.maxIdle=500
    #最大连接数,超过此连接时操作redis会报错
    im.hs.server.redis.maxTotal=5000
    im.hs.server.redis.maxWaitTime=1000
    im.hs.server.redis.testOnBorrow=true
    #最小闲置连接数,spring启动的时候自己主动建立该数目的连接供应用程序使用,不够的时候会申请。
    im.hs.server.redis.minIdle=300

    spring-redis.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xsi:schemaLocation="
    			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
    	<context:property-placeholder location="classpath:conf/redis/redis.properties" />
    
    	<!-- 其实,仅仅须要连接1个节点就能够 -->
    	<bean id="culster7001" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.153" />
    		<constructor-arg name="port" value="7001" />
    	</bean>
    	<bean id="culster7002" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.153" />
    		<constructor-arg name="port" value="7002" />
    	</bean>
    	<bean id="culster7003" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.154" />
    		<constructor-arg name="port" value="7003" />
    	</bean>
    	<bean id="culster7004" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.154" />
    		<constructor-arg name="port" value="7004" />
    	</bean>
    	<bean id="culster7005" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.155" />
    		<constructor-arg name="port" value="7005" />
    	</bean>
    	<bean id="culster7006" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.155" />
    		<constructor-arg name="port" value="7006" />
    	</bean>
    	<bean id="culster7007" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.152" />
    		<constructor-arg name="port" value="7007" />
    	</bean>
    	<bean id="culster7008" class="redis.clients.jedis.HostAndPort">
    		<constructor-arg name="host" value="192.168.62.152" />
    		<constructor-arg name="port" value="7008" />
    	</bean>
    	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    		<property name="maxTotal" value="${im.hs.server.redis.maxTotal}" />
    		<property name="minIdle" value="${im.hs.server.redis.minIdle}" />
    		<property name="maxWaitMillis" value="${im.hs.server.redis.maxWaitTime}" />
    		<property name="maxIdle" value="${im.hs.server.redis.maxIdle}" />
    		<property name="testOnBorrow" value="${im.hs.server.redis.testOnBorrow}" />
    		<property name="testOnReturn" value="true" />
    		<property name="testWhileIdle" value="true" />
    	</bean>
    
    
    	<bean id="jc" class="redis.clients.jedis.JedisCluster">
    		<constructor-arg name="nodes">
    			<set>
    				<ref bean="culster7001" />
    				<ref bean="culster7002" />
    				<ref bean="culster7003" />
    				<ref bean="culster7004" />
    				<ref bean="culster7005" />
    				<ref bean="culster7006" />
    				<ref bean="culster7007" />
    				<ref bean="culster7008" />
    			</set>
    		</constructor-arg>
    		<constructor-arg name="poolConfig"> <ref bean="poolConfig" /> </constructor-arg>
    	</bean>
    	
    	<bean id="distributedLock" class="com.gaojiasoft.gaojiaRedis.DistributedLockManger" />
    
    	<context:annotation-config />
    </beans>
    


    測试代码:

    package com.gaojiasoft.gaojiaRedis;
    
    import java.util.UUID;
    import java.util.concurrent.TimeUnit;
    
    import junit.framework.TestCase;
    
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class ClusterJedisTester extends TestCase
    {
        private Logger logger = LoggerFactory.getLogger(ClusterJedisTester.class);
    
        private static String[] list = new String[] { "classpath:conf/redis/spring-redis.xml" };
        
        private static ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(list, true, null);
        
        DistributedLockManger  distributedLock = (DistributedLockManger)context.getBean("distributedLock");
        
        @Test
        public void testLock() throws InterruptedException
        {
            distributedLock.test();
            while(true)
            {
                Thread.sleep(1000);
            }
        }
    }
    







  • 相关阅读:
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    iOS开发系列--IOS程序开发概览
    iOS开发系列—Objective-C之Foundation框架
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/6933598.html
Copyright © 2011-2022 走看看