• .Net Redis 实现分布式锁以及实现Gzip数据压缩提升性能


    上文问题处理

    上文写了.Net Reids 入门到熟练,有道友说,不如起个名字叫 入门到删库,我说,删库不得行,最后,改成 入门到雪崩,哈哈。还是可以的

    上文 .Net Reids 入门到熟练,文章中的 Redis Desktop Manager 软件收费,我是没得办法,用的老版本,有道友介绍说可以用 Another Redis Desktop Manager ,我自己也下载了,用着也是不错的说

    Another Redis Desktop Manager 下载地址 : https://github.com/qishibo/AnotherRedisDesktopManager/releases

    也有道友说 StackExchange.Redis 这个.Net 的组件在特殊情况下会有 TimeOut的异常(通过增加线程池上限可以解决?可验证),建议使用 CSRedisCoe 或者 使用 FreeRedis 听说用这个更舒服。

    #分布式锁
    分布式环境中,最重要的三个技术点分别是,分布式锁、分布式事务、分布式选举,这三个技术点。

    今天先对分布式锁进行简单的介绍和实现。

    场景:当前服务非单体服务,在并发的业务的情况下,需要对独占的资源进行占用式分配使用,就需要用到分布式锁,简单来讲就是锁库存,锁单个的操作(比如,要更新某个数据,可能有点耗时,但是,又不能让N个服务同时都更新某个数据,就需要先锁住,更新完,对方再根据具体的情况执行其他业务。)

    实现的技术,可以通过

    1. Mysql(唯一主键索引)(通过插入业务唯一ID来实现,如果有插入的,说明抢锁失败)
    2. Redis,Memcache (缓存)
    3. Zookeeper (文件io)

    这几个技术类型来看,速度最快的就是缓存了。

    我们接下来就通过缓存来实现redis的分布式锁

    锁的原理

    其实就是假设有N个服务在运行,它们都需要做一件事情,但是,这个事情要排队来做,那就需要抢一个标志,证明谁可以做这个事情,这个标志,定义为锁,当其中一个服务抢到后,其他服务就抢不到了。只有抢到的服务释放了锁之后,其他服务才能抢这个锁,抢到了继续执行它自己的业务。

    实际上,锁的速度是很快的,就像单体里的lock一样方便。

    Redis 分布式锁 方案1

    采用简单的方案实现

    IDatabase.StringSet(key, value, TimeSpan, When.NotExists);

    主要就是 When.NotExists ,当不存在的时候,插入成功,会有返回值,来判断。

        /// <summary>
        /// 实现一个redis锁,实现对特定资源的占用
        /// </summary>
        public class RedisLock
        {
            private IDatabase IDatabase;
            private string key;
            private string value;
            private TimeSpan TimeSpan;
            private TimeSpan TimeOut;
            public RedisLock(IDatabase IDatabase, string key, string value, TimeSpan timeSpan, TimeSpan? timeout = null)
            {
                this.key = key;
                this.value = value;
                this.TimeSpan = timeSpan;
                this.TimeOut = timeout.HasValue ? timeout.Value : timeSpan.Add(new TimeSpan(0, 0, 2));
                this.IDatabase = IDatabase;
            }
            /// <summary>
            /// 处理动作
            /// </summary>
            public bool Process(Action action)
            {
                bool state = false;
                try
                {
                    state = Lock();
                    if (state)
                    {
                        action?.Invoke();
                    }
                }
                finally
                {
                    UnLock(state);
                }
                return state;
            }
            /// <summary>
            /// 申请锁
            /// </summary>
            /// <returns></returns>
            private bool Lock()
            {
                DateTime dateTime = DateTime.Now;
                var state = false;
                //申请标志位
                while (!state && (DateTime.Now - dateTime) < TimeOut)
                {
                    state = IDatabase.StringSet(key, value, TimeSpan, When.NotExists);
                    if (state)
                    {
                        break;
                    }
                    SpinWait.SpinUntil(() => false, 100);
                }
                return state;
            }
            /// <summary>
            /// 释放锁
            /// </summary>
            /// <returns></returns>
            private bool UnLock(bool lockState)
            {
                if (lockState)
                {
                    IDatabase.KeyDelete(key);
                }
                else
                {
                    var data = IDatabase.StringGet(key);
                    if (data == value)
                    {
                        IDatabase.KeyDelete(key);
                    }
                }
                return true;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    结果也很不错

    Redis 分布式锁 方案2

    采用标准方式实现

    redis 内置了lock的实现,主要是 LockTake LockQuery 和 LockRelease

    主要是基于这几个方法进行的扩展

        /// <summary>
        /// 实现一个redis锁,实现对特定资源的占用
        /// </summary>
        public class RedisLock
        {
            private IDatabase IDatabase;
            private string key;
            private string value;
            private TimeSpan TimeSpan;
            private TimeSpan TimeOut;
            public RedisLock(IDatabase IDatabase, string key, string value, TimeSpan timeSpan, TimeSpan? timeout = null)
            {
                this.key = key;
                this.value = value;
                this.TimeSpan = timeSpan;
                this.TimeOut = timeout.HasValue ? timeout.Value : timeSpan.Add(new TimeSpan(0, 0, 2));
                this.IDatabase = IDatabase;
            }
            /// <summary>
            /// 处理动作
            /// </summary>
            public bool Process(Action action)
            {
                bool state = false;
                try
                {
                    state = Lock();
                    if (state)
                    {
                        action?.Invoke();
                    }
                }
                finally
                {
                    UnLock(state);
                }
                return state;
            }
            /// <summary>
            /// 申请锁
            /// </summary>
            /// <returns></returns>
            private bool Lock()
            {
                DateTime dateTime = DateTime.Now;
                var state = false;
                //申请标志位
                while (!state && (DateTime.Now - dateTime) < TimeOut)
                {
                    state = IDatabase.LockTake(key, value, TimeSpan);
                    if (state)
                    {
                        break;
                    }
                    SpinWait.SpinUntil(() => false, 100);
                }
                return state;
            }
            /// <summary>
            /// 释放锁
            /// </summary>
            /// <returns></returns>
            private bool UnLock(bool lockState)
            {
                var data = IDatabase.LockQuery(key);
                if (data == value)
                {
                    IDatabase.LockRelease(key, value);
                }
                return true;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    效果也是一样的

    测试方法

     class Program
        {
            static void Main(string[] args)
            {
                Console.Title = "redis lock Demo1 by 蓝创精英团队";
                var connMultiplexer = ConnectionMultiplexer.Connect("127.0.0.1");
                Console.WriteLine($"redis连接状态:{connMultiplexer.IsConnected}");
                var db = connMultiplexer.GetDatabase(1);//可以选择指定的db,0-15
                Test(db);
                Console.WriteLine("redis lock 1 案例!");
                Console.ReadLine();
            }
            public static void Test(IDatabase database)
            {
                var tasks = new List<Task>();
                for (int i = 0; i < 5; i++)
                {
                    tasks.Add(Task.Run(() =>
                    {
                        var ID = Guid.NewGuid().ToString("N");
                        var redislock = new RedisLock(database, "key", ID, new TimeSpan(0, 0, 15), new TimeSpan(0, 0, 15 * 10));
                        redislock.Process(() => {
                            Console.WriteLine($"{DateTime.Now} - {ID}:申请到标志位!");
                            Console.WriteLine($"{DateTime.Now} - {ID}:处理自己的 事情!");
                            Thread.Sleep(5 * 1000);
                            Console.WriteLine($"{DateTime.Now} - {ID}:处理完毕!");
                        });
                    }));
                }
                Task.WaitAll(tasks.ToArray());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    Redis分布式锁功能完结

    完结撒花先。

    Redis支持 Gzip压缩数据的支持

     class Program
        {
            static void Main(string[] args)
            {
                Console.Title = "redis Gzip Demo by 蓝创精英团队";
                var connMultiplexer = ConnectionMultiplexer.Connect("127.0.0.1");
                Console.WriteLine($"redis连接状态:{connMultiplexer.IsConnected}");
                var db = connMultiplexer.GetDatabase(1);//可以选择指定的db,0-15
    
                //String
                db.StringSet("str1", GzipCompress("123"));
                var stringValue = db.StringGet("str1");
                var data = GzipDecompress(stringValue);
                Console.WriteLine($"获取到string 类型的值:{data}");
    
    
                Console.WriteLine("redis gzip 案例!");
                Console.ReadLine();
            }
            /// <summary>
            /// gzip压缩
            /// </summary>
            static byte[] GzipCompress(string data)
            {
                using var gzipdata = new MemoryStream();
                using var ms = new MemoryStream(Encoding.UTF8.GetBytes(data));
                using var gs = new GZipStream(gzipdata, CompressionMode.Compress, true);
                ms.CopyTo(gs);
                var bytes = gzipdata.GetBuffer();
                return bytes;
            }
            /// <summary>
            /// gzip解压
            /// </summary>
            static string GzipDecompress(byte[] data)
            {
                if (data == null || data.Length == 0)
                {
                    return null;
                }
                using MemoryStream ms = new(data);
                using GZipStream compressedzipStream = new(ms, CompressionMode.Decompress);
                using MemoryStream outBuffer = new();
                byte[] block = new byte[1024];
                while (true)
                {
                    int bytesRead = compressedzipStream.Read(block, 0, block.Length);
                    if (bytesRead <= 0)
                    {
                        break;
                    }
                    else
                    {
                        outBuffer.Write(block, 0, bytesRead);
                    }
                }
                var bytes = outBuffer.ToArray();
                return Encoding.UTF8.GetString(bytes);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    程序运行的效果

    以及 Another Redis Desktop Manager 看到的效果 :

    总结

    搞完了。这个gzip压缩功能用上了。性能提升也是看得见的。

    代码地址

    https://github.com/kesshei/RedisLockDemo.git

    https://gitee.com/kesshei/RedisLockDemo.git

    感谢大佬支持,一键三连哦,您的支持就是我的动力!

  • 相关阅读:
    【Struts2框架】idea快速搭建struts2框架
    面试官:项目中最大的风险是什么?
    《向量数据库指南》——向量数据库 大模型的“海马体”
    多级编号和目录
    Appium 移动端自动化测试(Mac)
    Java项目:失物招领管理系统(java+SSM+HTML+JavaScript+jsp+mysql)
    CVPR2020-视觉不平衡立体匹配
    Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能
    事件冒泡、事件捕获、事件委托
    6.30模拟赛总结
  • 原文地址:https://blog.csdn.net/kesshei/article/details/125418979