发新帖

REDIS分布式锁代码实现

zhujun 2月前 47
REDIS配置:
<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.0.xsd"
       default-autowire="byName">
       <!--  redis连接配置  start-->
        <bean  id="poolConfig"  class="redis.clients.jedis.JedisPoolConfig">
                <property  name="maxIdle"  value="${redis.maxIdle}"/>
                <property  name="maxTotal"  value="${redis.maxTotal}"/>
                <property  name="timeBetweenEvictionRunsMillis"  value="${redis.timeBetweenEvictionRunsMillis}"/>
                <property  name="testOnBorrow"  value="${redis.testOnBorrow}"/>
        </bean>
        <!--  redis连接配置  end-->
       
        <!--  sharding  -->
        <bean  id="jedis.shardInfo"  class="redis.clients.jedis.JedisShardInfo">
               <constructor-arg  index="0"  value="${redis.host1}"  />
               <constructor-arg  index="1"  value="${redis.port1}"  />
               <constructor-arg  index="2"  value="shardInfo-0"  />
         <property  name="password"  value="${redis.pass1}"  /> 
       </bean>   
         <bean  id="jedis.shardInfo1"  class="redis.clients.jedis.JedisShardInfo">
               <constructor-arg  index="0"  value="${redis.host2}"  />
               <constructor-arg  index="1"  value="${redis.port2}"  />
               <constructor-arg  index="2"  value="shardInfo-1"  />
                 <property  name="password"  value="${redis.pass2}"  /> 
       </bean> 
       <!--  jedis  shard  pool配置  -->
       <bean  id="shardedJedisPool"  class="redis.clients.jedis.ShardedJedisPool">
               <constructor-arg  index="0"  ref="poolConfig"  />
               <constructor-arg  index="1">
                       <list>
                               <ref  bean="jedis.shardInfo"  />
                               <ref  bean="jedis.shardInfo1"  />
                       </list>
               </constructor-arg>
       </bean> 
       
       <bean  id="redisUtil"  class="cn.ranko.core.utils.RedisUtil"/>
       
       <bean  id="memberRedisUtil"  class="cn.ranko.utils.MemberRedisUtils"/>
       <bean  id="jedisDataSource"  class  ="cn.ranko.redis.JedisDataSourceImpl"></bean>
       <bean  id="redisClientTemplate"  class  ="cn.ranko.redis.RedisClientTemplate"></bean>
       <bean  id="memberServiceAccessLogRedisUtils"  class="cn.ranko.member.util.MemberServiceAccessLogRedisUtil"/>
       <bean  id="lock"  class="cn.ranko.utils.lock.LockImplRedisNX"></bean>
</beans>


JedisDataSourceImpl:

package  cn.ranko.redis;

import  javax.annotation.Resource;

import  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
import  org.springframework.stereotype.Service;

import  redis.clients.jedis.ShardedJedis;
import  redis.clients.jedis.ShardedJedisPool;

/**
  * 
  *  获取redis数据源
  *
  */
@Service("jedisDataSource")
public  class  JedisDataSourceImpl  implements  JedisDataSource{
        private  final  static  Logger  LOG  =  LoggerFactory.getLogger(JedisDataSourceImpl.class);
       
        @Resource(name="shardedJedisPool")
       private  ShardedJedisPool  shardedJedisPool;
       
        public  ShardedJedis  getRedisClient()  {
                ShardedJedis  shardJedis  =  null;
                try  {
                        shardJedis  =  shardedJedisPool.getResource();
                        return  shardJedis;
                }  catch  (Exception  e)  {
                        LOG.error("[JedisDS]  getRedisClent  error:"  +  e.getMessage());
                        if  (null  !=  shardJedis)
                                shardJedis.close();
                }
                return  null;
        }

        public  void  returnResource(ShardedJedis  shardedJedis)  {
                shardedJedis.close();
        }

        public  void  returnResource(ShardedJedis  shardedJedis,  boolean  broken)  {
                shardedJedis.close();
        }

}

package  cn.ranko.redis;

import  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
import  org.springframework.beans.factory.annotation.Autowired;
import  org.springframework.stereotype.Repository;

import  redis.clients.jedis.ShardedJedis;

/**
  *
  * 
  *  REDIS工具类
  * 
  */
@Repository("redisClientTemplate")
public  class  RedisClientTemplate  {
        private  static  final  Logger  log  =  LoggerFactory.getLogger(RedisClientTemplate.class);

        @Autowired
        private  JedisDataSource  redisDataSource;

        public  void  disconnect()  {
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                shardedJedis.disconnect();
        }

        /**
          *  设置单个值
          * 
          *  @param  key
          *  @param  value
          *  @return
          */
        public  String  set(String  key,  String  value)  {
                String  result  =  null;

                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.set(key,  value);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }
       

        /**
          *  获取单个值
          * 
          *  @param  key
          *  @return
          */
        public  String  get(String  key)  {
                String  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }

                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.get(key);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  Boolean  exists(String  key)  {
                Boolean  result  =  false;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.exists(key);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  String  type(String  key)  {
                String  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.type(key);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        /**
          *  在某段时间后失效
          * 
          *  @param  key
          *  @param  seconds
          *  @return
          */
        public  Long  expire(String  key,  int  seconds)  {
                Long  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.expire(key,  seconds);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        /**
          *  在某个时间点失效
          * 
          *  @param  key
          *  @param  unixTime
          *  @return
          */
        public  Long  expireAt(String  key,  long  unixTime)  {
                Long  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.expireAt(key,  unixTime);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  Long  ttl(String  key)  {
                Long  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.ttl(key);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  boolean  setbit(String  key,  long  offset,  boolean  value)  {

                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                boolean  result  =  false;
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.setbit(key,  offset,  value);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  boolean  getbit(String  key,  long  offset)  {
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                boolean  result  =  false;
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;

                try  {
                        result  =  shardedJedis.getbit(key,  offset);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  long  setRange(String  key,  long  offset,  String  value)  {
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                long  result  =  0;
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.setrange(key,  offset,  value);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  String  getRange(String  key,  long  startOffset,  long  endOffset)  {
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                String  result  =  null;
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.getrange(key,  startOffset,  endOffset);

                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  long  setnx(String  key,  String  value)  {
                Long  result  =  0l;

                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                if  (shardedJedis  ==  null)  {
                        return  result;
                }
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.setnx(key,  value);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
        }

        public  Long  del(String  key)  {
                Long  result  =  null;
                ShardedJedis  shardedJedis  =  redisDataSource.getRedisClient();
                boolean  broken  =  false;
                try  {
                        result  =  shardedJedis.del(key);
                }  catch  (Exception  e)  {
                        log.error(e.getMessage(),  e);
                        broken  =  true;
                }  finally  {
                        redisDataSource.returnResource(shardedJedis,  broken);
                }
                return  result;
               
        }


}




LockImplRedisNX:REDIS分布式锁的实现类

package  cn.ranko.utils.lock;

import  javax.annotation.Resource;

import  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
import  org.springframework.stereotype.Service;

import  cn.ranko.redis.RedisClientTemplate;

/**
  *  〈一句话功能简述〉分布式并发悲观锁<br>
  * 
  */
@Service
public  class  LockImplRedisNX  implements  Lock  {
        private  final  Logger  LOGGER  =  LoggerFactory.getLogger(LockImplRedisNX.class);
        private  static  final  long  threadSleepMilliseconds  =  20;
       
        @Resource(name="redisClientTemplate")
        public  RedisClientTemplate  redisClient;

        public  <T>  T  lock(String  lockForDoTask,  int  LOCK_RETRY_COUNT,  long  redisKeyExpireSeconds,
                        LockCallBack<T>  lockCallBack)  throws  LockInsideExecutedException,  LockCantObtainException  {
                long  curentTime  =  System.currentTimeMillis();
                long  expireSecond  =  curentTime  /  1000  +  redisKeyExpireSeconds;
                long  expireMillisSecond  =  curentTime  +  redisKeyExpireSeconds  *  1000;

                for  (int  i  =  0;  i  <  LOCK_RETRY_COUNT;  i++)  {
                        if  (redisClient.setnx(lockForDoTask,  String.valueOf(expireMillisSecond))  ==  1)  {
                                try  {
                                        redisClient.expireAt(lockForDoTask,  expireSecond);
                                        return  lockCallBack.handleObtainLock();
                                }  catch  (Exception  e)  {
                                        e.printStackTrace();
                                        LockInsideExecutedException  ie  =  new  LockInsideExecutedException(e);
                                        e.printStackTrace();
                                        return  lockCallBack.handleException(ie);
                                }  finally  {
                                        //  logger.info("释放锁{}",key2);
                                        LOGGER.info("get  lock  success"  +  lockForDoTask  +  "  count:"  +  i);
                                        redisClient.del(lockForDoTask);
                                }
                        }  else  {
                                LOGGER.info("do  not  obtain  the  lock"  +  lockForDoTask  +  "  count:"  +  i);
                                try  {
                                        Thread.sleep(threadSleepMilliseconds);
                                }  catch  (InterruptedException  e)  {
                                        LOGGER.error("Interrupte  exception",  e);
                                }

                                String  expireSpecifiedInString  =  redisClient.get(lockForDoTask);
                                if  (expireSpecifiedInString  !=  null)  {
                                        long  expireSpecified  =  Long.valueOf(expireSpecifiedInString);
                                        if  (curentTime  >  expireSpecified)  {
                                                LOGGER.warn("detect  the  task  lock  is  expired,  key:  {},  expireSpecified:{},  currentTime:{}",
                                                                lockForDoTask,  expireSpecified,  curentTime);
                                                redisClient.del(lockForDoTask);
                                        }
                                }

                        }
                }
                T  r  =  lockCallBack.handleNotObtainLock();
                return  r;
        }
}


实现案例:
@Resource(type  =  LockImplRedisNX.class)
private  Lock  lock;

private  static  final  String  LOCK_PREFIX="ADDFAVORITES_";


//检查数据库(MEMBER.MEMBER_FAVORITES_INDEX1)索引约束,如果不违反进行插入数据,违反不进行插入数据。
private  int  checkFavoriteUnique(int  count,  MemberFavorites  record)  throws  LockCantObtainException  {

final  MemberFavorites  favorites  =  new  MemberFavorites();
BeanUtils.copyProperties(record,  favorites);

//由于并发情况下,任然出现的约束索引冲突,所以通过REDIS分布式悲观锁解决该并发INSERT问题
String  insertLockKey  =  LOCK_PREFIX+record.getMemberId()+"_"+record.getProductId();
lock.lock(insertLockKey,  20,  2,  new  LockCallBack<MemberFavorites>()  {
       //加锁,并且获得锁,执行业务逻辑
       @Override
       public  MemberFavorites  handleObtainLock()  {
               MemberFavorites  res=favoritesMapper.checkUniqueByCondition(favorites);
               if(res==null){
                       favorites.setAffectRows(favoritesMapper.insertSelective(favorites));
               }else{
                       favorites.setAffectRows(0);
               }
               return  favorites;
       }

       //加锁,但加锁失败
       @Override
       public  MemberFavorites  handleNotObtainLock()  throws  LockCantObtainException  {
               throw  new  LockCantObtainException("MemberInfoServiceImpl.checkFavoriteUnique方法:加锁,但加锁失败");
       }

       //获取分布式锁发生异常
       @Override
       public  MemberFavorites  handleException(LockInsideExecutedException  e)  throws  LockInsideExecutedException  {
               throw  new  LockInsideExecutedException(e);
       }
});

count  +=  favorites.getAffectRows();
return  count;
}
最新回复 (0)
返回
发新帖
zhujun
主题数
24
帖子数
2
注册排名
1