/// <summary>
        /// 分布式尝试锁
        /// </summary>
        /// <param name="info"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static LockResult TryLock(BaseDistributedLockInfo info, Action action)
        {
            try
            {
                if (info is RedisDistributedLockInfo)
                {
                    var redisinfo = info as RedisDistributedLockInfo;
                    if (string.IsNullOrEmpty(DistributedLockConfig.DistributedLockRedisServers))
                    {
                        throw new DistributedLockException("请在web.config或app.config中配置DistributedLockRedisServers");
                    }
                    var redisserver = new RedisLoadBalance(DistributedLockConfig.GetDistributedLockRedisServerList()).ChooseRedisServer(info.DistributedLockKey);

                    using (var @lock = new RedisDistributedLockFromJava(redisserver, info.DistributedLockKey))
                    {
                        var islock = @lock.TryGetDistributedLock(info.GetLockTimeOut, info.TaskRunTimeOut);
                        if (islock == LockResult.Success)
                        {
                            action();
                        }
                        return(islock);
                    }
                }
                throw new DistributedLockException("info 参数传入错误");
            }
            catch (Exception exp)
            {
                XXF.Log.ErrorLog.Write(string.Format("分布式尝试锁错误,key:{0}", (info == null ? "" : info.DistributedLockKey.NullToEmpty())), exp);
                throw exp;
            }
        }
        public override void Dispose()
        {
            if (lockresult == LockResult.Success || lockresult == LockResult.LockSystemExceptionFailure)
            {
                try
                {
                    long current = CurrentUnixTimeMillis();
                    using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
                    {
                        var v = redisclient.Get(key);
                        if (v != null)

                        {
                            // 避免删除非自己获取得到的锁
                            if (current < BitConverter.ToInt64(v, 0))
                            {
                                redisclient.Del(key);
                            }
                        }
                    }
                }
                catch (Exception exp)
                {
                    XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
                }
            }
        }
Beispiel #3
0
 public override void Dispose()
 {
     try
     {
         using (var redisClient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
         {
             redisClient.Remove(key);
         }
     }
     catch (Exception exp)
     {
         XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁释放严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
     }
 }
Beispiel #4
0
        public override LockResult TryGetDistributedLock(TimeSpan?getlockTimeOut, TimeSpan?taskrunTimeOut)
        {
            if (lockresult == LockResult.Success)
            {
                throw new DistributedLockException("检测到当前锁已获取");
            }
            _client = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient();

            /*
             * 阅读源码发现当其获取锁后,redis连接资源会一直占用,知道获取锁的资源释放后,连接才会跳出,可能会导致连接池资源的浪费。
             */
            try
            {
                this._lock = new ServiceStack.Redis.RedisLock(_client, key, getlockTimeOut);
                lockresult = LockResult.Success;
            }
            catch (Exception exp)
            {
                XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
                lockresult = LockResult.LockSystemExceptionFailure;
            }
            return(lockresult);
        }
Beispiel #5
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);
        }
        public override LockResult TryGetDistributedLock(TimeSpan?getlockTimeOut, TimeSpan?taskrunTimeOut)
        {
            if (lockresult == LockResult.Success)
            {
                throw new DistributedLockException("检测到当前锁已获取");
            }
            try
            {
                // 1. 通过SETNX试图获取一个lock
                string @lock = key;
                long   taskexpiredMilliseconds    = (taskrunTimeOut != null ? (long)taskrunTimeOut.Value.TotalMilliseconds : (long)DistributedLockConfig.MaxLockTaskRunTime);
                long   getlockexpiredMilliseconds = (getlockTimeOut != null ? (long)getlockTimeOut.Value.TotalMilliseconds : 0);
                long   hassleepMilliseconds       = 0;
                while (true)
                {
                    using (var redisclient = DistributedLockConfig.GetRedisPoolClient(redisserver).GetClient())
                    {
                        long value = CurrentUnixTimeMillis() + taskexpiredMilliseconds + 1;

                        /*Java以前版本都是用SetNX,但是这种是无法设置超时时间的,不是很理解为什么,
                        * 可能是因为原来的redis命令比较少导致的?现在用Add不知道效果如何.
                        *  因对redis细节不了解,但个人怀疑若异常未释放锁经常发生,可能会导致内存逐步溢出*/
                        bool acquired = redisclient.Add <long>(@lock, value, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));
                        //SETNX成功,则成功获取一个锁
                        if (acquired == true)
                        {
                            lockresult = LockResult.Success;
                        }
                        //SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时
                        else
                        {
                            var oldValueBytes = redisclient.Get(@lock);
                            //超时
                            if (oldValueBytes != null && BitConverter.ToInt64(oldValueBytes, 0) < CurrentUnixTimeMillis())
                            {
                                /*此处虽然重设并获取锁,但是超时时间可能被覆盖,故重设超时时间;若有进程一直在尝试获取锁,那么锁存活时间应该被延迟*/
                                var getValueBytes = redisclient.GetSet(@lock, BitConverter.GetBytes(value));
                                var o1            = redisclient.ExpireEntryIn(@lock, TimeSpan.FromMilliseconds(taskexpiredMilliseconds + DistributedLockConfig.TaskLockDelayCleepUpTime));//这里如果程序异常终止,依然会有部分锁未释放的情况。
                                // 获取锁成功
                                if (getValueBytes == oldValueBytes)
                                {
                                    lockresult = LockResult.Success;
                                }
                                // 已被其他进程捷足先登了
                                else
                                {
                                    lockresult = LockResult.GetLockTimeOutFailure;
                                }
                            }
                            //未超时,则直接返回失败
                            else
                            {
                                lockresult = LockResult.GetLockTimeOutFailure;
                            }
                        }
                    }

                    //成功拿到锁
                    if (lockresult == LockResult.Success)
                    {
                        break;
                    }

                    //获取锁超时
                    if (hassleepMilliseconds >= getlockexpiredMilliseconds)
                    {
                        lockresult = LockResult.GetLockTimeOutFailure;
                        break;
                    }

                    //继续等待
                    System.Threading.Thread.Sleep(DistributedLockConfig.GetLockFailSleepTime);
                    hassleepMilliseconds += DistributedLockConfig.GetLockFailSleepTime;
                }
            }
            catch (Exception exp)
            {
                XXF.Log.ErrorLog.Write(string.Format("redis分布式尝试锁系统级别严重异常,redisserver:{0}", redisserver.NullToEmpty()), exp);
                lockresult = LockResult.LockSystemExceptionFailure;
            }
            return(lockresult);
        }