FreeRedis 实现源码
/// <summary>
/// 开启分布式锁,若超时返回null
/// </summary>
/// <param name="name">锁名称</param>
/// <param name="timeoutSeconds">超时(秒)</param>
/// <param name="autoDelay">自动延长锁超时时间,看门狗线程的超时时间为timeoutSeconds/2 , 在看门狗线程超时时间时自动延长锁的时间为timeoutSeconds。除非程序意外退出,否则永不超时。</param>
/// <returns></returns>
public LockController Lock(string name, int timeoutSeconds, bool autoDelay = true)
{
name = $"RedisClientLock:{name}";
var startTime = DateTime.Now;
while (DateTime.Now.Subtract(startTime).TotalSeconds < timeoutSeconds)
{
var value = Guid.NewGuid().ToString();
if (SetNx(name, value, timeoutSeconds) == true)
{
double refreshSeconds = (double)timeoutSeconds / 2.0;
return new LockController(this, name, value, timeoutSeconds, refreshSeconds, autoDelay);
}
Thread.CurrentThread.Join(3);
}
return null;
}
使用SETNX命令可以将一个键值对设置到Redis中,但只有在该键不存在时才会设置成功
if (SetNx(name, value, timeoutSeconds) == true)
将锁对应的键设置为某个固定的值,如果设置成功则表示获取到了锁
然后进去看看具体类做了什么
延长锁
/// <summary>
/// 延长锁时间,锁在占用期内操作时返回true,若因锁超时被其他使用者占用则返回false
/// </summary>
/// <param name="milliseconds">延长的毫秒数</param>
/// <returns>成功/失败</returns>
public bool Delay(int milliseconds)
{
var ret = _client.Eval(@"local gva = redis.call('GET', KEYS[1])
if gva == ARGV[1] then
local ttlva = redis.call('PTTL', KEYS[1])
redis.call('PEXPIRE', KEYS[1], ARGV[2] + ttlva)
return 1
end
return 0", new[] { _name }, _value, milliseconds)?.ToString() == "1";
if (ret == false) _autoDelayTimer?.Dispose(); //未知情况,关闭定时器
return ret;
}
中间一大段lua脚本,大意就是 Redis 中与 KEYS[1] 对应的键的值等于 ARGV[1] 时,设置该键的过期时间为 ARGV[2] + ttlva,并返回操作成功;否则返回操作失败
其中local ttlva = redis.call('PTTL', KEYS[1])
调用 Redis 的 PTTL命令获取与 KEYS[1] 对应的键的剩余过期毫秒单位时间,并将其赋给变量 ttlva
执行成功返回true,失败是false,如果是false则代表未知情况
刷新锁
/// <summary>
/// 刷新锁时间,把key的ttl重新设置为milliseconds,锁在占用期内操作时返回true,若因锁超时被其他使用者占用则返回false
/// </summary>
/// <param name="milliseconds">刷新的毫秒数</param>
/// <returns>成功/失败</returns>
public bool Refresh(int milliseconds)
{
var ret = _client.Eval(@"local gva = redis.call('GET', KEYS[1])
if gva == ARGV[1] then
redis.call('PEXPIRE', KEYS[1], ARGV[2])
return 1
end
return 0", new[] { _name }, _value, milliseconds)?.ToString() == "1";
if (ret == false) _autoDelayTimer?.Dispose(); //未知情况,关闭定时器
return ret;
}
当 Redis 中与 KEYS[1] 对应的键的值等于 ARGV[1] 时,设置该键的过期时间为 ARGV[2],并返回操作成功;否则返回操作失败,其他和上一个类似
释放锁
/// <summary>
/// 释放分布式锁
/// </summary>
/// <returns>成功/失败</returns>
public bool Unlock()
{
_autoDelayTimer?.Dispose();
return _client.Eval(@"local gva = redis.call('GET', KEYS[1])
if gva == ARGV[1] then
redis.call('DEL', KEYS[1])
return 1
end
return 0", new[] { _name }, _value)?.ToString() == "1";
}
当 Redis 中与 KEYS[1] 对应的键的值等于 ARGV[1] 时,删除该键,并返回操作成功
标签:总结,return,name,timeoutSeconds,KEYS,Redis,ARGV,分布式 From: https://www.cnblogs.com/ncellit/p/17763643.html