首页 > 作文

c# 理解csredis库实现分布式锁的详细流程

更新时间:2023-04-04 22:24:19 阅读: 评论:0

声明:

这里首先使用的是csredis,地址是https://github.com/2881099/csredis

该库本身已经足够完善,这里我画蛇添足一下,为了方便自己的使用。

本身csredis库已经实现了完整的加锁和去锁的逻辑,这里实现的与库本身所实现的有以下几点区别(csredis实现代码位置为:https://github.com/2881099/csredis/blob/bb6d947695770333027f3936f80052041db41b64/src/csrediscore/csredisclient.cs#l4344,有兴趣可以去了解看下)

1. 去掉了csredis的锁续租部分的功能,尽量简化

2. 将锁的token的设定交给外部,使用guid也罢,使用id也行。通过已知的token,保证了你可以在任意地方以观察者的身份释放锁。

3. 尽量不修改其key的原本诺贝尔经济学奖揭晓值,不添加前缀,防止在观测时出现不必要的麻烦。

逻辑:

加锁就是t 一个 key ,如果key 存在的情况下则返回失败。那么典型的命令就是tnx.

一个锁显然是需要一个过期时间的,那么我们可能要用到 expire命令。

释放锁则是一个del命令

查看锁的值是需要get命令

比较常见的加锁使用的是tnx,不过由于redis支持了tkeytokennxex/pxmax-lock-time(c/millc) (设置key token 是否不存在才t 秒数模式/毫秒数模式 秒数或毫秒数) 这种传参模式,由此,这里更加推荐使用t 命令。

如果在我们的代码端执行del 则小概率发生以下情况:

  a 申请锁t x,过期时间为t。

  经过时间t后,a恰好忙完了,a通过get命令看看token是否一致,得到结果发现一致的。

a决定发送del到redis服务器,此时a恰好网络拥堵。

redis服务器由于锁x超时,进而释放了锁x。

  此时b恰好也申请了锁x,无过期时间。

a网络恢复,del命令发送成功。

结果 b的锁被a释放了。

幸好redis支持了lua脚本。让我们得以简单的实现过期,加锁,去锁功能,而不需要自己手动timer过期。

这里要使用到eval命令执行脚本。

代码

using csredis;using system;using system.collections.concurrent;using system.collections.generic;namespace csredis.helper{    /// <summary>    ///  基于csredis的简单封装    /// </summary>    public class csredismanager    {               private concurrentdictionary<string, csredisclient> _rvicenamewithclient;              /// <summary>        ///  初始化        /// </summary>        public void init()        {            _rvicenamewithclient = new concurrentdictionary<string, csredisclient>();        }        ///  获取业务redis服务        /// <param name="rvicename"></param>        /// <returns></returns>        public csredisclient getredisclient(string rvicename)            csredisclient result = null;            _rvicenamewithclient.trygetvalue(rvicename,out result);            return result;        ///  添加redis服务        /// <param name="connectstr"></param>        public bool addredisclient(string rvicename,string connectstr)            csredisclient csredisclient = new csredisclient(connectstr);            return _rvicenamewithclient.tryadd(rvicename, csredisclient);        ///  设置字符串型kv         /// <param name="key">key</param>        /// <param name="value">value</param>新加坡学校;        /// <param name="expirecond">过期时间(秒)</param>        /// <returns>是否成功</returns>        public bool t(string rvicename,string key,string value,int expirecond=-1)            var redisclient = getredisclient(rvicename);            getexceptionofclient(redisclient);                        return redisclient.t(key, value, expirecond);                   ///  获取相应key的值        /// <param name="key"></param>        public string get(string rvicename, string key)            return redisclient.get(key);        ///  如果不存在则执行,存在则忽略        /// <param name="value"></param>        public bool tnx(string rvicename, string key, string value)            var res = redisclient.tnx(key, value);            retu捡拾幸福rn res;        ///  带过期时间的tnx        /// <param name="conds"></param>        public bool tnx(string rvicename, string key, string value, int millconds = -1)            var res = t(rvicename, key, value, redixistence.nx, millconds);        ///  带过期时间的txx        public bool txx(string rvicename, string key, string value, int millconds = -1)            var res = t(rvicename, key, value, redixistence.xx, millconds);        ///  带参数t        /// <param name="existence"></param>        public bool t(string rvicename, string key, string value, redixistence existence, int millconds = -1)            var res = redisclient.t(key, value, millconds, existence);        ///  设置生存时间        public bool expire(string rvicename, string key, int conds)            return redisclient.expire(key, conds);        ///  获取剩余的生存时间(秒)        public long ttl(string rvicename, string key)            return redisclient.ttl(key);        ///  删除del        public long del(string rvicename,params string[] keys)            return redisclient.del(keys);        ///  执行脚本        /// <param name="script"></param>        /// <param name="args"></param>        public object eval(string rvicename, string script,string key,params object[] args)            var res = redisclient.eval(script, key,args);        ///  添加共享锁        public bool addlock(string rvicename, string key,string token, int millconds = -1)            var valres = tnx(rvicename, key, token, millconds);            return valres;        ///  删除共享锁        public bool relealock(string rvicename, string key,string token)            var script = getrelealockscript();            var res = redisclient.eval(script, key, token);            if (0== (long)res)            {                return fal;            }            return true;        ///  获取键值        /// <param name="pattern"></param>        public string[] keys(string rvicename, string pattern)            var res = redisclient.keys(pattern);        ///  获取client发生异常        /// <param name="client"></param>        private void getexceptionofclient(csredisclient client)            if (client == null)                throw new exception("无有效的redis服务");        ///  lua脚本删除共享锁        ///  解决在a申请锁 xxkey  过期的瞬间,b 申请锁xxkey,        ///  此时恰好a执行到释放xxkey从而引起的异常释放        private static  string getrelealockscript()            return "if redis.call(\"get\",keys[1]) == argv[1] \nthen\nreturn redis.call(\"del\", keys[1])\nel\nreturn 0\nend";            }    }

这里我把要单独执行的lua脚本单独提出来

if redis.call("get",keys[1]) == argv[1]then    return redis.call("del",keys[1])el    return 0end

这段脚本对应的是c# 中的getrelealockscript()方法中的文字。

这里我个人偷了个懒,按照道理,这里应该有个loadscriptpath,加载脚本所在位置,调用的时候先检查脚本是否在内存中,不在则去loadscriptpath找对应的脚本,方便不同的人协同合作。不过那个就是脚本管理器了,还要设计interface,有点偏离主题了。

下面是测试代码

using csredis.helper;using nunit.framework;namespace testproject{    public class tests    {        [tup]        public void tup()        {        } 郢中白雪       [test]        public void test1()            csredismanager csredismanager = new csredismanager();            csredismanager.init();            csredismanager.addredisclient("test", "127.0.0.1:6379,password=123456, connecttimeout =1000,connectretry=1,synctimeout=10000,defaultdataba=0");            //csredismanager.addredisclient("product", "127.0.0.1:6379,password=123456, connecttimeout =1000,connectretry=1,synctimeout=10000,defaultdataba=1");                        var token = "123"维耶里;            var lockkey = "lockkey1";            csredismanager.addlock("test", lockkey,token,20 * 1000);            csredismanager.relealock("test", lockkey, token);    }}

这里就是对于共享锁的一点简单实现,多了挺多与本次的命令无关的代码,海涵海涵

到此这篇关于c#理解csredis实现分布式锁的文章就介绍到这了,更多相关c#分布式锁内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

本文发布于:2023-04-04 22:23:55,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/e2a3ca37f4a22fb8b4448d61787fc157.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:c# 理解csredis库实现分布式锁的详细流程.doc

本文 PDF 下载地址:c# 理解csredis库实现分布式锁的详细流程.pdf

标签:脚本   命令   时间   的是
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图