什么是Redisssion
Redisssion是一个基于Redis的Python库,用于实现分布式锁和其他高级锁机制。它在Redis的原生锁功能的基础上提供了更高级别的功能,如可重入锁、公平锁、读写锁等。Redisssion旨在解决在分布式环境下的并发控制问题,它能够保证多个进程或线程之间的互斥访问和同步操作。
说白了就是一个基于Redis的一个分布式方案解决框架
Redisssion使用Redis作为后端存储,通过利用Redis的原子操作和内存存储特性,实现了高效、安全的分布式锁机制。它支持对锁进行配置和管理,并提供了超时机制、可重入性、可公平性等特性,使得开发人员可以灵活地控制和应用锁策略。
Redisssion提供了简单易用的API,可以方便地在Python项目中使用。它与Python中常用的线程和进程模型兼容,并支持在分布式环境中使用,例如在分布式任务调度、分布式缓存管理、分布式资源控制等场景中都可以使用Redisssion来实现分布式锁。
可重入锁
指同一个线程能多次获取同一个锁,在多次获取锁时先去判断HashMap结构中该锁是否为当前线程的,如果是,给当前线程锁计数加一。在多次获取锁后释放锁时,不能直接删除锁,而是减少可重入锁的计数次数。在释放锁时,当重入次数为0时才能真正删除锁
执行流程
这里结合秒杀功能一人一单展开讲
判断锁是否存在:
根据以上理论知识,可以得出秒杀功能优化方案:使用lua脚本完成加锁解锁操作,保证在进行业务操作时加锁和解锁的原子性,防止过程中出现不可控因素,导致库存数量异常
lua加锁:
local key = KEYS[1];
local threadId = ARGV[1];
local releaseTime = ARGV[2];
-- 锁不存在,设置锁
if(redis.call('exists',key,threadId) == 0) then
-- 设置锁和计数
redis.call('hset',key,threadId,'1');
-- 设置过期时间
redis.call('expire',key,releaseTime);
return 1;
end;
-- 锁存在,判断锁是否为自己的
if(redis.call('hexists',key,threaId) == 1) then
-- 计数+1
redis.call('hincrby',key,thread,'1');
-- 重置时间
redis.call('expire',key,releaseTime);
return 1;
end;
-- 获取的锁不是自己的
return 0;
lua释放锁
local key = KEYS[1];
local threadId = ARGV[2];
local releaseTime = ARGV[3];
-- 不是自己的线程锁了
if(redis.call('hexists',key,threadId) == 0) then
reutn nil;
end;
-- 是自己的计数-1
local count = redis.call('hincrby',key,threadId,-1);
-- 计数是否为0了
if(count > 0) then
redis.call('expire',key,releaseTime);
return nil;
else
redis.call('DEL',key)
end;
Redission的可重入锁基于hash结构实现,记录线程id和重入次数,获取锁时先判断锁是否存在,存在重入次数+1,释放锁时-1,直到重入次数减为0,才真正释放锁
重试机制
Redisssion提供了一个可靠的重试机制,用于处理在分布式环境下可能出现的锁竞争和网络故障等问题。重试机制可以确保在锁竞争失败或Redis连接中断时进行重新尝试,以提高系统的可靠性和稳定性。
在Redisssion中,重试机制主要通过以下两个方面来实现:
- 自动重试: 当Redis连接断开或发生其他网络故障时,Redisssion会自动尝试重新连接,并继续执行后续的操作。它会根据配置的重试次数和重试间隔进行尝试,直到连接成功或达到最大重试次数为止。
- 锁竞争重试: 当获取分布式锁时发生竞争失败,Redisssion会根据配置的重试次数和重试间隔进行重试,直到成功获取锁或达到最大重试次数为止。这个重试机制可以防止由于锁竞争导致的并发问题。
超时机制
在源码中,包括一个"看门狗"(Watch Dog)的机制,"看门狗"是一种监控机制,用于检测并处理长时间未释放的锁。
在调用lock方法后,都会调用以下方法org.redisson.RedissonLock#tryAcquireAsync
其中this.scheduleExpirationRenewal(thredId)为到期续订,也就是看门狗的具体实现方法,想要进入下方内容,需要保证leaseTime==-1,即锁的过期时间,当调用lock方法时传入超时时间限制,那么就不会开启看门狗
lock.lock(); // 开启
lock.lock(5000,TimeUnit.SECONDS); // 不开启
想要进入续期,需要保证ttlRemaining==null,而这个参数是加锁成功后返回的值
org.redisson.RedissonLock#renewExpiration
超时续期这里,当任务每 internalLockLeaseTime/3 ms后执行一次,其中internalLockLeaseTime的默认时间为30000,所以任务每10s执行一次
核心业务
这里getLockaName(threadId)中可拿到一些自定义的参数,如可自定义internalLockLeaseTime的值,来指定看门狗多久续期一次
到这里重试机制中的部分实现大概清楚了,无非在后台开启定时任务线程,每隔一段时间对锁续命,延长锁时间。那这样的话这个数据是否会永不过期呢?理论上是这样,但是在实际业务中,存在于Redis中的数据即使不会过期,也会有相关人员进行维护,或者产生bug,陷入死循环