.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. 1. Mysql(唯一主键索引)(通过插入业务唯一ID来实现,如果有插入的,说明抢锁失败)

  2. 2. Redis,Memcache (缓存)

  3. 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;
        }
    }

结果也很不错

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

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;
        }
    }

效果也是一样的

测试方法

 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());
        }
    }

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);
        }
    }

程序运行的效果

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

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

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

总结

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

代码地址

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

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