Beispiel #1
0
        private static async Task <DistributedLockStatus> AcquireResourceAsync(RedisLock redisLock, ILogger logger)
        {
            if (redisLock.CancellationToken.HasValue && redisLock.CancellationToken.Value.IsCancellationRequested)
            {
                redisLock.Status = DistributedLockStatus.Cancelled;
                redisLock.CancellationToken.Value.ThrowIfCancellationRequested();
            }

            List <RedisKey>   redisKeys   = new List <RedisKey>();
            List <RedisValue> redisValues = new List <RedisValue>();

            AddAcquireOrExtendRedisInfo(redisLock, redisKeys, redisValues, logger);

            IDatabase database = await GetDatabaseAsync(redisLock.Options.ConnectionSetting, logger).ConfigureAwait(false);

            try
            {
                RedisResult result = await database.ScriptEvaluateAsync(
                    _loadedLuas.LoadedLockLua,
                    redisKeys.ToArray(),
                    redisValues.ToArray()).ConfigureAwait(false);

                int rt = (int)result;

                return(rt == 1 ? DistributedLockStatus.Acquired : DistributedLockStatus.Failed);
            }
            catch (RedisServerException ex) when(ex.Message.StartsWith("NOSCRIPT", StringComparison.InvariantCulture))
            {
                logger.LogError(ex, "NOSCRIPT, will try again.");

                InitLoadedLuas(redisLock.Options.ConnectionSetting, logger);

                return(await AcquireResourceAsync(redisLock, logger).ConfigureAwait(false));
            }
        }
Beispiel #2
0
        private static void AddAcquireOrExtendRedisInfo(RedisLock redisLock, List <RedisKey> redisKeys, List <RedisValue> redisValues, ILogger logger)
        {
            /// keys: resource1, resource2, resource3
            /// argv: 3(resource_count), expire_milliseconds, resource1_value, resource2_value, resource3_value

            //有可能到这里,dispose了,redisLock.Resources都为空


            try
            {
                foreach (string item in redisLock.Resources)
                {
                    redisKeys.Add(item);
                }

                redisValues.Add(redisKeys.Count);
                redisValues.Add((int)redisLock.ExpiryTime.TotalMilliseconds);

                foreach (string item in redisLock.ResourceValues)
                {
                    redisValues.Add(item);
                }
            }
            catch (NullReferenceException ex)
            {
                logger.LogError(ex, $"在试图延长锁的时候,ResourceValues被清空. Resources:{redisLock.Resources.ToJoinedString(",")}, Status:{redisLock.Status}");
            }
        }
Beispiel #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="resources"></param>
        /// <param name="expiryTime">对资源的最大占用时间,应该大于TimeSpan.Zero, null表示使用默认</param>
        /// <param name="waitTime">如果资源被占用,你愿意等多久,TimeSpan.Zero表明不愿意等。null表示使用默认等待时间</param>
        /// <param name="retryInterval">等待时不断尝试获取资源 的 等待间隔,应该大于TimeSpan.Zero, null 表示使用默认时间</param>
        /// <returns></returns>
        public async Task <IDistributedLock> LockAsync(IEnumerable <string> resources, TimeSpan expiryTime, TimeSpan?waitTime, TimeSpan?retryInterval, bool notUnlockWhenDispose = false, CancellationToken?cancellationToken = null)
        {
            if (expiryTime < _minimumExpiryTime)
            {
                _logger.LogWarning("Expiry {settingTime} ms too low, setting to {minimumExpiryTime} ms", expiryTime.TotalMilliseconds, _minimumExpiryTime.TotalMilliseconds);
                expiryTime = _minimumExpiryTime;
            }

            if (retryInterval != null && retryInterval.Value < _minimumRetryTime)
            {
                _logger.LogWarning("Retry {settingTime} ms too low, setting to {minimumRetryTime} ms", retryInterval.Value.TotalMilliseconds, _minimumRetryTime.TotalMilliseconds);
                retryInterval = _minimumRetryTime;
            }

            RedisLock redisLock = new RedisLock(
                _options,
                _logger,
                resources,
                expiryTime,
                waitTime ?? TimeSpan.FromMilliseconds(_options.DefaultWaitMilliseconds),
                retryInterval ?? TimeSpan.FromMilliseconds(_options.DefaultRetryIntervalMilliseconds),
                notUnlockWhenDispose,
                cancellationToken);

            await StartAsync(redisLock, _logger).ConfigureAwait(false);

            return(redisLock);
        }
Beispiel #4
0
        private static void StartAutoExtendTimer(RedisLock redisLock, ILogger logger)
        {
            long interval = (long)redisLock.ExpiryTime.TotalMilliseconds / 2;

            redisLock.KeepAliveTimer = new Timer(
                state => { ExtendLockLifetime(redisLock, logger); },
                null,
                interval,
                interval);
        }
Beispiel #5
0
        private static async Task StartAsync(RedisLock redisLock, ILogger logger)
        {
            if (redisLock.WaitTime != TimeSpan.Zero)
            {
                redisLock.Status = DistributedLockStatus.Waiting;

                Stopwatch stopwatch = Stopwatch.StartNew();

                while (!redisLock.IsAcquired && stopwatch.Elapsed <= redisLock.WaitTime)
                {
                    logger.LogDebug($"锁在等待... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");

                    redisLock.Status = await AcquireResourceAsync(redisLock, logger).ConfigureAwait(false);

                    if (!redisLock.IsAcquired)
                    {
                        logger.LogDebug($"锁继续等待... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
                        if (redisLock.CancellationToken == null)
                        {
                            await Task.Delay((int)redisLock.RetryTime.TotalMilliseconds).ConfigureAwait(false);
                        }
                        else
                        {
                            await Task.Delay((int)redisLock.RetryTime.TotalMilliseconds, redisLock.CancellationToken.Value).ConfigureAwait(false);
                        }
                    }
                }

                if (!redisLock.IsAcquired)
                {
                    logger.LogDebug($"锁等待超时... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
                    redisLock.Status = DistributedLockStatus.Expired;
                }

                stopwatch.Stop();
            }
            else
            {
                //不等待
                redisLock.Status = await AcquireResourceAsync(redisLock, logger).ConfigureAwait(false);
            }

            if (redisLock.IsAcquired)
            {
                logger.LogDebug($"锁获取成功... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
                StartAutoExtendTimer(redisLock, logger);
            }
            else
            {
                logger.LogDebug($"锁获取失败... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
            }
        }
Beispiel #6
0
        /// <summary>
        /// ReleaseResourceAsync
        /// </summary>
        /// <param name="redisLock"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        /// <exception cref="LockException"></exception>
        internal static async Task ReleaseResourceAsync(RedisLock redisLock, ILogger logger)
        {
            StopKeepAliveTimer(redisLock, logger);

            if (redisLock.NotUnlockWhenDispose)
            {
                logger.LogDebug($"自动延期停止,但锁等他自己过期... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
                return;
            }

            List <RedisKey>   redisKeys   = new List <RedisKey>();
            List <RedisValue> redisValues = new List <RedisValue>();

            AddReleaseResourceRedisInfo(redisLock, redisKeys, redisValues);

            IDatabase database = await GetDatabaseAsync(redisLock.Options.ConnectionSetting, logger).ConfigureAwait(false);

            try
            {
                RedisResult result = await database.ScriptEvaluateAsync(
                    _loadedLuas.LoadedUnLockLua,
                    redisKeys.ToArray(),
                    redisValues.ToArray()).ConfigureAwait(false);

                int rt = (int)result;

                if (rt == 1)
                {
                    logger.LogDebug($"锁已经解锁... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");
                }
                else
                {
                    throw Exceptions.DistributedLockUnLockFailed(threadId: Thread.CurrentThread.ManagedThreadId, resources: redisLock.Resources);
                }
            }
            catch (RedisServerException ex) when(ex.Message.StartsWith("NOSCRIPT", StringComparison.InvariantCulture))
            {
                InitLoadedLuas(redisLock.Options.ConnectionSetting, logger);

                await ReleaseResourceAsync(redisLock, logger).ConfigureAwait(false);
            }
            catch (Exception ex) when(ex is not LockException)
            {
                logger.LogDebug(ex, "锁解锁失败... {ThreadID}, {Resources}", Thread.CurrentThread.ManagedThreadId, redisLock.Resources);

                throw Exceptions.DistributedLockUnLockFailed(threadId: Thread.CurrentThread.ManagedThreadId, resources: redisLock.Resources, innerException: ex);
            }
        }
Beispiel #7
0
        private static void AddReleaseResourceRedisInfo(RedisLock redisLock, List <RedisKey> redisKeys, List <RedisValue> redisValues)
        {
            /// keys: resource1,resource2,resource3
            /// argv:3(resource_count), resource1_value, resource2_value, resource3_value

            foreach (string item in redisLock.Resources)
            {
                redisKeys.Add(item);
            }

            redisValues.Add(redisKeys.Count);

            foreach (string item in redisLock.ResourceValues)
            {
                redisValues.Add(item);
            }
        }
Beispiel #8
0
        private static void StopKeepAliveTimer(RedisLock redisLock, ILogger logger)
        {
            if (redisLock.KeepAliveTimer != null)
            {
                lock (redisLock.StopKeepAliveTimerLockObj)
                {
                    if (redisLock.KeepAliveTimer != null)
                    {
                        redisLock.KeepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite);
                        redisLock.KeepAliveTimer.Dispose();
                        redisLock.KeepAliveTimer = null;

                        logger.LogDebug($"锁停止自动延期,Resources:{redisLock.Resources.ToJoinedString(",")}");
                    }
                }
            }
        }
Beispiel #9
0
        private static void ExtendLockLifetime(RedisLock redisLock, ILogger logger)
        {
            if (redisLock.Status != DistributedLockStatus.Acquired)
            {
                logger.LogDebug($"锁已不是获取状态,停止自动延期... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}, Status:{redisLock.Status}");
                return;
            }

            logger.LogDebug($"锁在自动延期... ThreadID: {Thread.CurrentThread.ManagedThreadId}, Resources:{redisLock.Resources.ToJoinedString(",")}");

            List <RedisKey>   redisKeys   = new List <RedisKey>();
            List <RedisValue> redisValues = new List <RedisValue>();

            AddAcquireOrExtendRedisInfo(redisLock, redisKeys, redisValues, logger);

            IDatabase database = GetDatabase(redisLock.Options.ConnectionSetting, logger);

            try
            {
                RedisResult result = database.ScriptEvaluate(
                    _loadedLuas.LoadedExtendLua,
                    redisKeys.ToArray(),
                    redisValues.ToArray());

                int rt = (int)result;

                if (rt != 1)
                {
                    logger.LogError($"RedisLock 延期 失败. Resources:{redisLock.Resources.ToJoinedString(",")}, Status:{redisLock.Status}");
                    return;
                }

                redisLock.ExtendCount++;
            }
            catch (RedisServerException ex) when(ex.Message.StartsWith("NOSCRIPT", StringComparison.InvariantCulture))
            {
                logger.LogError(ex, "NOSCRIPT, will try again.");

                InitLoadedLuas(redisLock.Options.ConnectionSetting, logger);

                ExtendLockLifetime(redisLock, logger);
            }
        }