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
                                          );
        }
示例#2
0
        public RedisLock(RedisClient redisClient, string key, TimeSpan?timeOut)
        {
            this.redisClient = redisClient;
            this.key         = key;

            ExecExtensions.RetryUntilTrue(
                () => redisClient.SetEntryIfNotExists(key, "lock " + DateTime.UtcNow.ToUnixTime()),
                timeOut
                );
        }
示例#3
0
        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
                );
        }
示例#4
0
        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;
                }
            }
        }
示例#6
0
 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);
        }
示例#8
0
        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);
        }