• Redis Distributed lock


    using StackExchange.Redis;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace RedisDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                TestRedisLockWithFencingToken();
                Console.ReadKey();
            }
    
            public static List<int> SLEEP_OF_SECONDS = new List<int> { 20, 0, 0, 10, 10 };
    
            public static void TestRedisLockWithFencingToken()
            {
                var tasks = new List<Task>();
                var mockService = new MockService();
    
                Enumerable.Range(1, 3).ToList().ForEach(it =>
                {
                    var task = Task.Run(() =>
                    {
    
                        var taskId = $"task_{it}";
                        var lockHelper = new RedisLockHelper();
                        var lockKey = "article:1";
                        try
                        {
                            Console.WriteLine($"-> {taskId} begin acquire redis lock.");
                            var result = lockHelper.GetLock(lockKey, TimeSpan.FromSeconds(10));
                            if (result.Success)
                            {
                                var taskInfo = new TaskInfo { TaskId = taskId, FencingToken = result.FencingToken ?? 0 };
                                Console.WriteLine($"-> {taskId} acquire lock success {taskInfo}.");
                                var sleepOfSeconds = SLEEP_OF_SECONDS[it - 1];
                                Thread.Sleep(sleepOfSeconds * 1000);
                                mockService.Save(taskInfo);
                                Console.WriteLine($"{taskInfo} Done");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"{taskId} {ex.Message}");
                        }
                        finally
                        {
                            lockHelper.ReleaseLock("article:1");
                        }
                    });
                    tasks.Add(task);
                });
    
                try
                {
                    Task.WaitAll(tasks.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                }
            }
        }
    
        public class TaskInfo
        {
            public long FencingToken { get; set; }
            public string TaskId { get; set; }
    
            public override string ToString()
            {
                return $"TaskId: {TaskId} - FencingToken: {FencingToken}";
            }
        }
    
        public class MockService
        {
            private IConnectionMultiplexer connection;
            private String fencingTokenKey = "redis_fencingToken";
    
            public MockService()
            {
                this.Initialize();
            }
    
            private void Initialize()
            {
                connection = ConnectionMultiplexer.Connect("localhost");
            }
    
            public void Close()
            {
                if (connection != null)
                {
                    connection.Close();
                }
            }
    
    
    
            public void Save(TaskInfo taskInfo)
            {
                Console.WriteLine($"MockService#Save: {taskInfo}");
    
                var fencingToken = taskInfo.FencingToken;
    
                var serverFencingToken = connection.GetDatabase().StringGet(fencingTokenKey);
                Console.WriteLine($"MockService#Save: {taskInfo} - Server Fencing Token {serverFencingToken}");
    
                if (serverFencingToken.HasValue)
                {
                    if (fencingToken == (long)serverFencingToken)
                    {
                        Console.WriteLine($"MockService#Save: {taskInfo} Save Success.");
                    }
                    else
                    {
                        Console.WriteLine($"MockService#Save: {taskInfo} Save Failure, Fencingtoken not equal server token.");
                    }
                }
                else
                {
                    Console.WriteLine($"{taskInfo} Save error");
    
                }
            }
        }
    
        public class RedisLockResult
        {
            public bool Success { get; set; }
            public long? FencingToken { get; set; }
        }
    
        public class RedisLockHelper : IDisposable
        {
            private IConnectionMultiplexer connection;
            private String value;
            private String fencingTokenKey = "redis_fencingToken";
    
            public RedisLockHelper()
            {
                value = Guid.NewGuid().ToString();
                this.Initialize();
            }
    
            public RedisLockResult GetLock(String key, TimeSpan expiry)
            {
                var db = connection.GetDatabase();
                var lockSuccess = db.StringSet(key, value, expiry, When.NotExists, CommandFlags.None);
    
                var start = DateTime.Now;
                while (!lockSuccess)
                {
                    Thread.Sleep(50);
                    lockSuccess = db.StringSet(key, value, expiry, When.NotExists, CommandFlags.None);
                    start = start.AddMilliseconds(50);
                    if (DateTime.Now.Subtract(start).TotalSeconds > 20)
                    {
                        throw new InvalidOperationException($"can not acquire distributed lock");
                    }
                }
    
                var result = new RedisLockResult()
                {
                    Success = lockSuccess
                };
    
                if (lockSuccess)
                {
                    var fencingToken = db.StringIncrement(fencingTokenKey, 1, CommandFlags.None);
                    result.FencingToken = fencingToken;
                }
    
                return result;
            }
    
            public void ReleaseLock(RedisKey key)
            {
                var lua = @"if redis.call('get', @key) == @value then
                                return redis.call('del', @key)
                            else
                                return 0
                            end";
                var db = connection.GetDatabase();
                var prepare = LuaScript.Prepare(lua);
                var result = db.ScriptEvaluate(prepare, new { key = key, value = value });
            }
    
            private void Initialize()
            {
                connection = ConnectionMultiplexer.Connect("localhost");
            }
    
            public void Dispose()
            {
                if (connection != null)
                {
                    connection.Dispose();
                }
            }
        }
    }
    
  • 相关阅读:
    SQL server分离和附加数据库
    sql-server的添加数据库文件(日志数据)以及收缩数据库文件(日志数据)
    sql语句中的join用法(可视化解释)
    SQL语句(floor、ceiling和round以及left和right)
    怎样重新获得别人的信任-知识就是力量(思维导图)
    怎样让孩子爱上学习-知识就是力量(思维导图)
    洛谷-P1036 选数
    洛谷-P1028 数的计算
    洛谷-P1914 小书童——密码
    洛谷-P1598 垂直柱状图
  • 原文地址:https://www.cnblogs.com/byxxw/p/11344463.html
Copyright © 2020-2023  润新知