CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍

前言:

自从CYQ.Data框架出了数据库读写分离、分布式缓存MemCache、自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段。

从以下的更新列表就可以看出来了,3个月更新了100条次功能:

+ View Code

其实更多的时间,是放在ASP.NET Aries 业务开发框架上,上里下外全部重构了一遍。

前几天,决定把Redis集成进来,一鼓作气,解决了。

下面分享一下经历:

最初的想法:

一开始我是拒绝的,不愿动态调用第三方的客户端(关联依赖的dll太多)。

最近打算支持Redis,有点妥协了,动态加载就动态加载了吧:

考虑着引入:StackExchange.Redis或ServiceStack.Redis?

看着这些DLL,太重量级,方法反射起来也费劲!

中间思维停顿了一会。。。

发现轻量级:Bettle.Redis

在寻找Redis的API资料时,无意发现了这个开源的轻量级Bettle.Redis。

看到源码编绎后才46K,感觉就是它了。

不过才几刻间,发现了以下几个问题了:

1:自身虽然46K,但代码引用了另外两个3个dll(依赖太多):

2:使用的方法不符合使用习惯,一个命令类型就对应一个类。

3:不支持集群的水平扩展(没实现支持一致性Hash)。

4:代码是用.NET 4.0 以下版本写的,(CYQ.Data 框架是支持2.0起的,改代码改到我手痛)

所以,以上原因估计是它没被普及的原因,也是最终没有被我选择集成的原因。

但是它开放了源码、对我还是有点启发和参考意义。

Redis API 扫盲:

在决定支持Redis的过程中,花了不少时间扫了Redis的文档:

 

更多命令详情可以看:http://doc.redisfans.com

从这么一堆的命令中,找到基本命令:Get、Set、Exists、Expire、Info,可怜没有Add。

其它的命令,多数都是可以用基本命令实现的,就被无视了。

经过短时间内大量的集中思考,决定自己实现了:

重新定位的思路:

框架之前已经集成了MemCache,而Redis和MemCache又大同小异。

一些共性的东西,可以复用:

1:hash算法。

2:一致性Hash(水平扩展)。

3:SocketPool。

4:ServerPool。

5:序列化(压缩)

剩下的,就是完成Socket和Redis的交互及使用方式。

以下是Redis的协议规范,不过是我实现Redis相关功能后才发现的:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

     协议规范

 

redis允许客户端以TCP方式连接,默认6379端口。传输数据都以\r\n结尾。

 

请求格式

 

*<number of arguments>\r\n$<number of bytes of argument 1>\r\n<argument data>\r\n

 

例:*1\r\n$4\r\nINFO\r\n

 

响应格式

 

1:简单字符串,非二进制安全字符串,一般是状态回复。  +开头,例:+OK\r\n

 

2: 错误信息。          -开头, 例:-ERR unknown command 'mush'\r\n

 

3: 整型数字。                            :开头, 例::1\r\n

 

4:大块回复值,最大512M。           $开头+数据长度。 例:$4\r\mush\r\n

 

5:多条回复。                           *开头, 例:*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n

折腾的经过:

Bettle.Redis里有源码,看看实现就可以了,所以没找协议规范:

通过几个小时的引进和代码调整,测试。

以为大功告成之际,测试到当Set的数据太大时,NetworkStream报异常:此流不支持Seek操作。

怀疑是Redis的Set有大小限制?:用Bettle.Redis自身试了下,发现正常,梦B了。

经代码调试,发现Bettle的Socket实现(Socket.Send)和Socket池的实现(NetworkStream.Write)不一样。

Bettle.Redis是把所有的协议构造好一次性Socket.Send(byte[])。

怀疑NetworkStream的默认缓存池太小引发的?:用memCache,Set了大量的数据,发现NetworkStream并没有抛异常,又梦B了。

怀疑是Redis协议的问题了?:改造代码,把协议分拆,先发送:$长度 ,再发送数据,发现竟然正常了,无语问苍天了!

经过一夜一天的折腾,Cache目录下补了4个类,同时进行了算法优化,清掉一些没用的代码。

支持Redis后,发现cyq.data.dll的大小竟然没变化,结果超出了预期,很好!

最后改造成的源码结构是:

 

完整的源码已经提交在:https://github.com/cyq1162/cyqdata

Redis使用方式:

        AppConfig.Cache.RedisServers = "127.0.0.1:6379,127.0.0.1:1121";//配置启用,
            AppConfig.Cache.RedisServersBak = "127.0.0.1:6379";//备用配置。

            CacheManage cache = CacheManage.RedisInstance;//操作对象
            cache.Add("obj", cache.CacheTable);//添加DataTable
            MDataTable obj = cache.Get<MDataTable>("obj");
            Console.WriteLine(obj.Rows.Count);

            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("路过秋天", "http://www.cnblogs.com/cyq1162");
            cache.Add("dic", dic);//添加字段
            Dictionary<string, string> dicObj = cache.Get<Dictionary<string, string>>("dic");
            Console.WriteLine(dicObj["路过秋天"]);

            cache.Remove("dic");//移除Dic
            bool hasKey = cache.Contains("dic");//检测是否存在
            Console.WriteLine(hasKey);

            Console.Read();

结果:

 

对于存储类型的改进:

由于Redis的Get只支持字符串,为了达到支持任意类型,我必须改进算法:

1:存档:目标是对象时=》进行序列化(对于>128K的会进行压缩)

2:数据的第1个字节:存档数据类型。

3:获取数据时:根据第1个字节,进行准确的数据类型还原。

(aaa是通过命令行Set的,而a0是通过代码设置的,所以多了\x02的类型标识)

因此:框架靠Set与Get能支持任意类型的存取档!

对于分布式算法的改进:

1:对于水平增加节点的扩展:

内部已经实现了一致性Hash算法,因此省了不少工作:

简单的描述为:把ip1产生N个hash ,ip2产生N个hash,... 然后排序(最后就看key的hash值离谁最新就粘谁了)

借用一张图表示为:

2:对于节点故障的转移:

在测试的过程中,我填写了一台异常的主机,发现被分配到异常的主机的key的读写都没反应了:

(我潜意识默认以为会自动转移到相邻的主机中)

默认的算法:

1:没有自动切换相邻的主机【用思考代码疑问:主动切换可能导致雪崩效应,(累积的压力可能把所有的服务器都搞挂)】。

2:有重试连接机制(2分钟试1次)。

改进了算法:增加了一个备份机的配置(AppConfig.Cache.RedisServersBak)

1:根据Hash,每一台主机都会指向一台备份机。

2:主机异常时,由备份机代理服务器15分钟(即每15分检测主机是否正常一次,如果正常,则恢复主机服务)。

3:当主机恢复时,从备份机里恢复数据,并清空备份机的数据(未实现

由于可能同时挂掉N台,所以备份机可能存档多台主机的信息。

于是算法的思路有3个:

1:数据不要了(主机重新缓存即可)

2:主机被请求时(检测是否挂过,如果是,读自身(若没有)=》读备份机(同时发表移除指令)(若有数据)=》返回(同时写入主机)

3:主机被请求时(检测是否挂过,如果是开启线程(读备份机所有Key,检测Hash是否符合自身,如果是,则从备份读取并写入,同时清除备份机的数据)

总结:

至此,CYQ.Data已经支持上Redis了,而且在分布式算法上,借了memCache的风,以及改进的算法,显的更为实用!

当然,细节仍需打磨,代码还可以改的更简洁优美。

在分布式已经泛滥的今天,能正确的判断并用好分布式框架是一种能力的体现。

刚刚群里有人发了这条消息:

其实前面的问题都可以无视,因为最后解决方案他只是把Redis部署从Windows转移到Linux就好了。

QPS最大时听说7万多(两台Web分来就是3万多,大部分是刷票造成的请求)

Redis在Windows上的表现并不如Linux的好,这个可以理解。

但是如果在架构设计方案上稍为调整,其实也毫无压力了。

最后我发现问题的根源不在于技术,在于人:.NET缺少有足够知识和思维的架构师。

不要遇到点问题就力不从心,在.NET的阵营上坚持吧,少年!

本文原创发表于博客园,作者为路过秋天,原文链接:http://www.cnblogs.com/cyq1162/p/6013140.html

时间: 2024-10-28 11:53:11

CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍的相关文章

CYQ.Data V5 分布式缓存MemCached应用开发介绍

前言 今天大伙还在热议关于.NET Core的东西,我只想说一句:在.NET 跨平台叫了这么多年间,其实人们期待的是一个知名的跨平台案例,而不是一堆能跨平台的消息. 好,回头说说框架:  在框架完成数据库读写分离的功能后,开始回想起2年前所构思的:关于框架集成分布式缓存MemCached的实现. 之前一直没动手,是因为思路比较飘,秉承着框架应该简单干净轻量引用无依赖和使用不复杂的思维: 看着MemCached的服务端协议,整天思考着自己用Socket写一个客户端. 后来可能是没激情,迟迟没动手.

CYQ.Data V5 分布式自动化缓存设计介绍(二)

CYQ.Data V5 分布式自动化缓存设计介绍(二) 前言: 最近一段时间,开始了<IT连>创业,所以精力和写的文章多数是在分享创业的过程. 而关于本人三大框架CYQ.Data.Aries.Taurus.MVC的相关文章,基本都很少写了. 但框架的维护升级,还是时不时的在进行中的,这点从开源的Github上的代码提交时间上就可以看出来了. 毕竟<IT连>的后台WebAPI,用的是Taurus.MVC,后台系统管理用的是Aries. 不过今天,就不写创业相关的文章了,先分享篇技术类

CYQ.Data V5 分布式自动化缓存设计介绍

前方: 其实完成这个功能之前,我就在思考:是先把想法写了来,和大伙讨论讨论后再实现,还是实现后再写文论述自己的思维. 忽然脑后传来一个声音说:你发文后会进入发呆阶段. 所以还是静下心,让我轻轻地把代码撸完再说. 最近这几天,自己在大脑里演练过各种技术难点,解决方案,推敲了各种该解决的问题,觉的差不多了,才决定撸码. 忽然发觉,原来代码是可以写在大脑里的. 要是你看到一个员工坐着2天没写一行代码,说明人家是高手,正在大脑编程. 好,不扯,回正文! 传统ORM的二级缓存为何失效? 有些ORM会提供:

CYQ.Data V5 从入门到放弃ORM系列:框架的优势

前言: 框架开源后,学习使用的人越来越多了,所以我也更加积极的用代码回应了. 在框架完成了:数据库读写分离功能 和 分布式缓存功能 后: 经过三天三夜的不眠不休,终于完成框架第三个重量级的功能:自动化分布式缓存. 源代码已经提交,源码地址见:终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了 记得很多年前,大概2010年左右吧,CYQ.Data框架进入快速更新版本阶段的时候, 那时候的我会经常在一些技术群里有意无意的说起自己的框架,然后群里总有那么一些自我而自负的人会说: 你

CYQ.Data V5文本数据库技术原理解密

前言: 这两天有点感冒状态,除了以前折腾 微博粉丝精灵 腾到三更,最近也在折腾个别工具到四更,偶尔心来心潮,赶紧写写文章,最近有很多朋友对CYQ.Data V5里的文本数据库感兴趣,这里就给大伙说下文本数据库技术原理,给大伙解下密.     CYQ.Data 框架的稳定与前进: CYQ.Data 对于V4系列,版本号就在V4.55版本就不再提高了,主要是为了保留一个最稳定的版本,基本除了Bug修正,不会再有大于V4.N的版本号出现了. 而V5版本,这一两年来,事实上,代码改动相当大,内部类的结构

终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了

前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说过  这么多年 秋天有两点没变 一是还是单身 另外一个就是cyq.data还没开源  终于等到开源了! 也许吧,只有把cyq.data最终开源了,才能解决单身问题,不然我在这上面花的时间太多,都没时间和妹子聊天了.   几个重要网址: 源码SVN地址:https://github.com/cyq11

CYQ.Data V5 MDataTable 专属篇介绍

前言 以前一两个月才出一篇,这三天有点变态地连续1天1篇(其实都是上周末两天写好的存货). 短期应该没有新的和此框架相关的文章要写了,这应该是最后一篇,大伙且看且珍惜. 前两篇讲数据库读写分离和分布式缓存,这篇隆重地介绍一下:MDataTable,毕竟它有很多未公布或隐藏技能,值得一看. CYQ.Data 核心使用类介绍 常用: 1:操作数据库:MAction.MProc (名称空间:CYQ.Data) 2:日志操作:Log.SysLogs(名称空间:CYQ.Data) 3:配置和性能调试:Ap

CYQ.Data V5 从入门到放弃ORM系列:教程 - MAction类使用

背景: 随着V5框架使用者的快速增加,终于促使我开始对整个框架编写完整的Demo. 上周大概花了一星期的时间,每天写到夜里3点半,终完成了框架所有功能的Demo. 同时,按V5框架名称空间的顺序,对每个类的使用,补充相应的文章介绍,以漏补缺. 以下开始介绍: MAction Demo的项目文件: 1:项目解决方案: 2:两个文件在Debug目录里: 演示的是SQLite数据库(默认System.Data.SQLite.DLL是64位版本,如果运行提示加载出错,自己解压32位的复盖即可) demo

CYQ.Data V5 数据库读写分离功能介绍

前言 好多年没写关于此框架的新功能的介绍了,这些年一直在默默地更新,从Nuget上的记录就可以看出来: 这几天在看Java的一些东西,除了觉的Java和.NET的相似度实在太高之外,就是Java太原始,急需被拯救. 之后又回到.NET的思维来,想着怎么在框架里实现读写分离控制请求切换. 之前几个月一直都有在思考,想及对框架现有的代码改动可能较大,所以迟迟没怎么下手. 最近终于在厕所蹲下的一瞬间,灵光一闪,感觉思绪来了,然后一鼓作气,就把它给作了. 数据库读写分离的三步曲 1:实现主(读)从(写)