Redis分布式锁,基于StringRedisTemplate和基于Lettuce实现setNx

使用redis分布式锁,来确保多个服务对共享数据操做的惟一性
通常来讲有StringRedisTemplate和RedisTemplate两种redis操做模板。java

根据key-value的类型决定使用哪一种模板,若是k-v均是String类型,则使用StringRedisTemplate,不然使用RedisTemplateredis

redis加锁操做
必须遵循原子性操做,保证加锁的惟一性
核心方法
set(lockKey,value,"NXXX","EXPX",expireTime)
NXXX:只能取NX或者XX,NX-key不存在时进行保存,XX-key存在时才进行保存
EXPX:过时时间单位 (EX,PX),EX-秒,PX-毫秒spring

使用StringRedisTemplate实现加锁springboot

public class StringRedisTemplateImplClient { // NX,XX //NX-key不存在则保存,XX-key存在则保存
    private static final String STNX= "NX"; //EX,PX //EX-秒,PX-毫秒
    private static final String SET_EXPIRE_TIME = "PX"; private RedisTemplate redisTemplate; private StringRedisTemplate stringRedisTemplate; public StringRedisTemplateImplClient(RedisTemplate redisTemplate){ this.redisTemplate = redisTemplate; this.stringRedisTemplate = new StringRedisTemplate(); this.stringRedisTemplate.setConnectionFactory(redisTemplate.getConnectionFactory()); this.stringRedisTemplate.afterPropertiesSet(); } public StringRedisTemplateImplClient(StringRedisTemplate redisTemplate){ this.stringRedisTemplate = redisTemplate; } public boolean addRedisLock(String lockKey,String requestId,long expireTime){ boolean result = stringRedisTemplate.opsForValue() .setIfAbsent(lockKey,lockKey,expireTime, TimeUnit.SECONDS); return result; } }

下面简要分析下这个方法的一致性,查看setIfAbsent的源码分布式

setIfAbsent这个方法是spring-data-redis提供的,分析其源码,实现类为org.springframework.data.redis.core.DefaultValueOperations
方法以下:
key-须要加锁的key
value-须要加锁的value
timeout-失效的时间
timeunit-常量,指定失效的时间单位ide

connection默认为lettuce驱动链接(springboot 2.0之后的版本)ui

public Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit) {
    byte[] rawKey = this.rawKey(key);
    byte[] rawValue = this.rawValue(value);
    Expiration expiration = Expiration.from(timeout, unit);
    return (Boolean)this.execute((connection) -> {
        return connection.set(rawKey, rawValue, expiration, SetOption.ifAbsent());
    }, true);
}

其中SetOption为指定NX/XX类型this

public static enum SetOption { UPSERT, SET_IF_ABSENT, SET_IF_PRESENT; private SetOption() { } public static RedisStringCommands.SetOption upsert() { return UPSERT; } public static RedisStringCommands.SetOption ifPresent() { return SET_IF_PRESENT; } public static RedisStringCommands.SetOption ifAbsent() { return SET_IF_ABSENT; } }

查询spring官网查询这三个属性spa

SET_IF_ABSENT--->NX
SET_IF_PRESENT--->XXdebug

 

自定义实现SetNx

setNx+expireTime实现加锁

 核心代码

String status = stringRedisTemplate.execute(new RedisCallback<String>() { @Override public String doInRedis(RedisConnection connection) throws DataAccessException { Object nativeConnection = connection.getNativeConnection(); String status = null; RedisSerializer<String> stringRedisSerializer = (RedisSerializer<String>) stringRedisTemplate.getKeySerializer(); byte[] keyByte = stringRedisSerializer.serialize(key); //springboot 2.0以上的spring-data-redis 包默认使用 lettuce链接包 //lettuce链接包,集群模式,ex为秒,px为毫秒
    if (nativeConnection instanceof RedisAdvancedClusterAsyncCommands) { logger.debug("lettuce Cluster:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds); status = ((RedisAdvancedClusterAsyncCommands) nativeConnection) .getStatefulConnection().sync() .set(keyByte,keyByte,SetArgs.Builder.nx().ex(30)); logger.debug("lettuce Cluster:---status:"+status); } //lettuce链接包,单机模式,ex为秒,px为毫秒
    if (nativeConnection instanceof RedisAsyncCommands) { logger.debug("lettuce single:---setKey:"+setKey+"---value"+value+"---maxTimes:"+expireSeconds); status = ((RedisAsyncCommands ) nativeConnection) .getStatefulConnection().sync() .set(keyByte,keyByte, SetArgs.Builder.nx().ex(30)); logger.debug("lettuce single:---status:"+status); } return status; } }); logger.debug("getLock:---status:"+status);//执行正确status="OK"
相关文章
相关标签/搜索