public InnerRedisLock(IRedisClient redisClient, string key, TimeSpan timeOut) { this.redisClient = redisClient; this.key = key; ExecExtensions.RetryUntilTrue(() => { var realSpan = timeOut == TimeSpan.Zero ? new TimeSpan(365, 0, 0, 0) : timeOut; //if nothing is passed in the timeout hold for a year var expireTime = NetworkTime.Now.Add(realSpan); var lockString = ((long)(expireTime.ToUnixTime() + 1)).ToString(); var nx = redisClient.SetEntryIfNotExists(key, lockString); if (nx) { return(true); } var lockExpireString = redisClient.Get <string>(key); long lockExpireTime; if (!long.TryParse(lockExpireString, out lockExpireTime)) { return(false); } if (lockExpireTime > NetworkTime.Now.ToUnixTime()) { return(false); } return(redisClient.GetAndSetEntry(key, lockString) == lockExpireString); }, timeOut ); }
public RedisLock(RedisClient redisClient, string key, TimeSpan?timeOut) { this.redisClient = redisClient; this.key = key; ExecExtensions.RetryUntilTrue( () => redisClient.SetEntryIfNotExists(key, "lock " + DateTime.UtcNow.ToUnixTime()), timeOut ); }
public RedisLock(RedisClient redisClient, string key, TimeSpan?timeOut) { this.redisClient = redisClient; this.key = key; ExecExtensions.RetryUntilTrue( () => { //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx //Calculate a unix time for when the lock should expire TimeSpan realSpan = timeOut ?? new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year DateTime expireTime = DateTime.UtcNow.Add(realSpan); string lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); //Try to set the lock, if it does not exist this will succeed and the lock is obtained var nx = redisClient.SetValueIfNotExists(key, lockString); if (nx) { return(true); } //If we've gotten here then a key for the lock is present. This could be because the lock is //correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly). //Therefore we need to get the value of the lock to see when it should expire redisClient.Watch(key); string lockExpireString = redisClient.Get <string>(key); long lockExpireTime; if (!long.TryParse(lockExpireString, out lockExpireTime)) { redisClient.UnWatch(); // since the client is scoped externally return(false); } //If the expire time is greater than the current time then we can't let the lock go yet if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) { redisClient.UnWatch(); // since the client is scoped externally return(false); } //If the expire time is less than the current time then it wasn't released properly and we can attempt to //acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes //before we call Commit() below, the Commit will fail and return false, which means that another thread //was able to acquire the lock before we finished processing. using (var trans = redisClient.CreateTransaction()) // we started the "Watch" above; this tx will succeed if the value has not moved { trans.QueueCommand(r => r.Set(key, lockString)); return(trans.Commit()); //returns false if Transaction failed } }, timeOut ); }
public RedisLock(RedisClient redisClient, string key, TimeSpan?timeOut) { this.redisClient = redisClient; this.key = key; ExecExtensions.RetryUntilTrue( () => { //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx //Calculate a unix time for when the lock should expire TimeSpan realSpan = timeOut ?? new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year DateTime expireTime = DateTime.UtcNow.Add(realSpan); string lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); //Try to set the lock, if it does not exist this will succeed and the lock is obtained var nx = redisClient.SetEntryIfNotExists(key, lockString); if (nx) { return(true); } //If we've gotten here then a key for the lock is present. This could be because the lock is //correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly). //Therefore we need to get the value of the lock to see when it should expire string lockExpireString = redisClient.Get <string>(key); long lockExpireTime; if (!long.TryParse(lockExpireString, out lockExpireTime)) { return(false); } //If the expire time is greater than the current time then we can't let the lock go yet if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) { return(false); } //If the expire time is less than the current time then it wasn't released properly and we can attempt to //acquire the lock. This is done by setting the lock to our timeout string AND checking to make sure //that what is returned is the old timeout string in order to account for a possible race condition. return(redisClient.GetAndSetEntry(key, lockString) == lockExpireString); }, timeOut ); }
public virtual void Lock(object key) { log.DebugFormat("acquiring cache lock : {0}", key); try { var globalKey = CacheNamespace.GlobalKey(key, RedisNamespace.NumTagsForLockKey); using (var client = this.clientManager.GetClient()) { // Derived from ServiceStack's RedisLock. ExecExtensions.RetryUntilTrue(() => { var wasSet = true; //client.SetEntryIfNotExists(globalKey, "lock " + DateTime.UtcNow.ToUnixTime()); if (wasSet) { acquiredLocks[key] = globalKey; } return(wasSet); }, lockTimeout); } } catch (Exception e) { log.ErrorFormat("could not acquire cache lock : ", key); var evtArg = new RedisCacheExceptionEventArgs(e); OnException(evtArg); if (evtArg.Throw) { throw; } } }
public virtual void WaitForWorkersToStop(TimeSpan?timeout = null) { ExecExtensions.RetryUntilTrue( () => Interlocked.CompareExchange(ref status, 0, 0) == WorkerStatus.Stopped, timeout); }
public override LockResult TryGetDistributedLock(TimeSpan?getlockTimeOut, TimeSpan?taskrunTimeOut) { if (lockresult == LockResult.Success) { throw new Exception("检测到当前锁已获取"); } try { using (var redisClient = DistributedLockConfig.GetRedisPoolClient()) { //内部的函数返回true,这个方法才会结束 ExecExtensions.RetryUntilTrue( () => { //SETNX 语法规则详见: http://redis.io/commands/setnx //计算lock的 unix time 过期时间 //TimeSpan realSpan = taskrunTimeOut ?? TimeSpan.FromMilliseconds(DistributedLockConfig.MaxLockTaskRunTime); TimeSpan realSpan = taskrunTimeOut.Value; DateTime expireTime = DateTime.UtcNow.Add(realSpan); string lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); //SetEntryIfNotExists:尝试 set 锁,如果锁不存在则 set 到 redis 里,并返回true var nx = redisClient.SetEntryIfNotExists(key, lockString); if (nx) { lockresult = LockResult.Success; return(true); } //如果锁已经存在,有可能是正确的获取,也可能是进程异常,没有正确释放锁, //因此我们要检测 lock 的值(unix time),判断 lock 何时过期,否则会引起死锁 //监视 lock 的 key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 redisClient.Watch(key); string lockExpireString = redisClient.Get <string>(key); long lockExpireTime; //如果 lock 的 value 不能正确转换为过期时间,则 lock 不正确, //有可能是因为 lock 被其他进程改动了 if (!long.TryParse(lockExpireString, out lockExpireTime)) { redisClient.UnWatch(); lockresult = LockResult.GetLockTimeOutFailure; return(false); } //如果过期时间大于当前时间,则锁还未过期,当前进程不能继续执行 if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) { redisClient.UnWatch(); lockresult = LockResult.GetLockTimeOutFailure; return(false); } //如果过期时间小于当前时间,则锁没有被正常释放,之前的线程可能出现了异常 //在这之前用watch监视了lock的key,如果lock的值没有被改动,则当前事务将正常执行 using (var trans = redisClient.CreateTransaction()) { trans.QueueCommand(r => r.Set(key, lockString)); //lockString在循环的开始会重新计算 var t = trans.Commit(); lockresult = t == false ? LockResult.GetLockTimeOutFailure : LockResult.Success; return(t); } }, getlockTimeOut ); } } catch (Exception exp) { LogManager.DefaultLogger.Error(exp, "redis分布式尝试锁系统级别严重异常"); lockresult = LockResult.LockSystemExceptionFailure; } return(lockresult); }
public override LockResult TryGetDistributedLock(TimeSpan?getlockTimeOut, TimeSpan?taskrunTimeOut) { if (lockresult == LockResult.Success) { throw new DistributedLockException("检测到当前锁已获取"); } try { using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient()) { ExecExtensions.RetryUntilTrue( () => { //This pattern is taken from the redis command for SETNX http://redis.io/commands/setnx //Calculate a unix time for when the lock should expire TimeSpan realSpan = taskrunTimeOut ?? TimeSpan.FromMilliseconds(DistributedLockConfig.MaxLockTaskRunTime); //new TimeSpan(365, 0, 0, 0); //if nothing is passed in the timeout hold for a year DateTime expireTime = DateTime.UtcNow.Add(realSpan); string lockString = (expireTime.ToUnixTimeMs() + 1).ToString(); //Try to set the lock, if it does not exist this will succeed and the lock is obtained var nx = redisClient.SetEntryIfNotExists(key, lockString); if (nx) { lockresult = LockResult.Success; return(true); } //If we've gotten here then a key for the lock is present. This could be because the lock is //correctly acquired or it could be because a client that had acquired the lock crashed (or didn't release it properly). //Therefore we need to get the value of the lock to see when it should expire redisClient.Watch(key); string lockExpireString = redisClient.Get <string>(key); long lockExpireTime; if (!long.TryParse(lockExpireString, out lockExpireTime)) { redisClient.UnWatch(); // since the client is scoped externally lockresult = LockResult.GetLockTimeOutFailure; return(false); } //If the expire time is greater than the current time then we can't let the lock go yet if (lockExpireTime > DateTime.UtcNow.ToUnixTimeMs()) { redisClient.UnWatch(); // since the client is scoped externally lockresult = LockResult.GetLockTimeOutFailure; return(false); } //If the expire time is less than the current time then it wasn't released properly and we can attempt to //acquire the lock. The above call to Watch(_lockKey) enrolled the key in monitoring, so if it changes //before we call Commit() below, the Commit will fail and return false, which means that another thread //was able to acquire the lock before we finished processing. using (var trans = redisClient.CreateTransaction()) // we started the "Watch" above; this tx will succeed if the value has not moved { trans.QueueCommand(r => r.Set(key, lockString)); //return trans.Commit(); //returns false if Transaction failed var t = trans.Commit(); if (t == false) { lockresult = LockResult.GetLockTimeOutFailure; } else { lockresult = LockResult.Success; } return(t); } }, getlockTimeOut ); } } catch (Exception exp) { XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp); lockresult = LockResult.LockSystemExceptionFailure; } return(lockresult); }