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

前言

今天大伙还在热议关于.NET Core的东西,我只想说一句:在.NET 跨平台叫了这么多年间,其实人们期待的是一个知名的跨平台案例,而不是一堆能跨平台的消息。

好,回头说说框架: 

在框架完成数据库读写分离的功能后,开始回想起2年前所构思的:关于框架集成分布式缓存MemCached的实现。

之前一直没动手,是因为思路比较飘,秉承着框架应该简单干净轻量引用无依赖和使用不复杂的思维:

看着MemCached的服务端协议,整天思考着自己用Socket写一个客户端。

后来可能是没激情,迟迟没动手。

又在某个时刻,想过偷懒,想动态反射Memcached.ClientLibrary库,但一看到它竟然关联了四个dll,我那纯洁的心就有点抗拒它了。

所以日子就这样飘来复去,这个功能被想起又遗忘......

框架集成MemCache

这几天,翻看了往昔下载的的相关源码中,有个叫BeITMemcached,扫了一下源码,发现简单轻量没外部引用。

于是就开始改造,并测试了一下,没发现问题,于是开始思考集成的方式。

框架的缓存类本来很孤单,只有一个:CacheManage.cs

现在改造完,多了10个类,编绎后dll大小多了30K,说多了也是泪~:

框架的代码重构思维

最终定案的重构思维:

1:原有的CacheManage类变更为LocalCache。

2:CacheManage类变更为抽象类

3:新增MemCache,并和LocalCache一并实现CacheManage抽象类方法。

以上三步,就是核心的思维。

一开始的重构思维:

1:从原有的CacheManage里提取接口ICache

2:CacheManage改名WebCache并继承自ICache(由于提口提取自本类,所以代码不需要怎么调整)

3:新建MemCache继承并实现ICache接口。

4:新建CacheManage类,并从WebCache处把Instance实例属性移到此类中,并返回对应的ICache接口。

编绎后发现:

因为原来的代码有一小部分是这样写的:CacheManage cache=CacheManage.Instance;

因为返回的类型不一样,原有代码受到影响,必须改成:ICache cache=CacheManage.Instance。

为了避开影响不用改动代码,所以最终改用定案里抽象类和抽象方法实现。

下面贴一下抽象类CacheManage的方法:


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

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

/// <summary>

    /// 全局缓存类

    /// </summary>

    /// <example><code>

    /// 使用示例:

    /// 实例化: CacheManage cache=CacheManage.Instance;

    /// 添加:   cache.Add("路过秋天",new MDataTable);

    /// 判断:   if(cache.Contains("路过秋天"))

    ///          {

    /// 获取:       MDataTable table=cache.Get("路过秋天") as MDataTable;

    ///          }

    /// </code></example>

    public abstract class CacheManage

    {

        #region 对外实例

        /// <summary>

        /// 返回唯一实例(根据是否配置AppConfig.Cache.MemCacheServers的服务器决定启用本地缓存或分布式缓存)

        /// </summary>

        public static CacheManage Instance

        {

            get

            {

                if (string.IsNullOrEmpty(AppConfig.Cache.MemCacheServers))

                {

                    return LocalShell.instance;

                }

                else

                {

                    return MemShell.instance;

                }

            }

        }

        /// <summary>

        /// 单机本地缓存

        /// </summary>

        public static CacheManage LocalInstance

        {

            get

            {

 

                return LocalShell.instance;

            }

        }

 

        class LocalShell

        {

            internal static readonly LocalCache instance = new LocalCache();

        }

        class MemShell

        {

            internal static readonly MemCache instance = new MemCache();

        }

        #endregion

        /// <summary>

        /// 添加一个Cache对象

        /// </summary>

        public abstract void Add(string key, object value);

        public abstract void Add(string key, object value, double cacheMinutes);

        public abstract void Add(string key, object value, string fileName);

        public abstract void Add(string key, object value, string fileName, double cacheMinutes);

        public abstract void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level);

        public abstract Dictionary<string, CacheDependencyInfo> CacheInfo { get; }

        public abstract MDataTable CacheTable { get; }

        /// <summary>

        /// 清除所有缓存

        /// </summary>

        public abstract void Clear();

        public abstract bool Contains(string key);

        /// <summary>

        /// 获和缓存总数

        /// </summary>

        public abstract int Count { get; }

        /// <summary>

        /// 获得一个Cache对象

        /// </summary>

        public abstract object Get(string key);

        /// <summary>

        /// 获得一个Cache对象

        /// </summary>

        public T Get<T>(string key)

        {

            object o = Get(key);

            if (o != null)

            {

                Type t = typeof(T);

                try

                {

                    return (T)StaticTool.ChangeType(o, t);

                }

                catch (Exception err)

                {

                    Log.WriteLogToTxt(err);

                    return default(T);

                }

            }

            return default(T);

        }

        /// <summary>

        /// 获取目标的文件依赖是否发生更改

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public abstract bool GetFileDependencyHasChanged(string key);

        /// <summary>

        /// 获取缓存对象是否被手工标识为已更改

        /// </summary>

        /// <param name="key"></param>

        /// <returns></returns>

        public abstract bool GetHasChanged(string key);

        /// <summary>

        /// 还可用的缓存字节数

        /// </summary>

        public abstract long RemainMemoryBytes { get; }

        /// <summary>

        /// 还可用的缓存百分比

        /// </summary>

        public abstract long RemainMemoryPercentage { get; }

        /// <summary>

        /// 删除一个Cache对象

        /// </summary>

        public abstract void Remove(string key);

        /// <summary>

        /// 缓存设置:有则更新,无则添加

        /// </summary>

        public abstract void Set(string key, object value);

        public abstract void Set(string key, object value, double cacheMinutes);

        /// <summary>

        /// 手动对缓存象标识为已更改

        /// </summary>

        public abstract void SetChange(string key, bool isChange);

        /// <summary>

        /// 更新缓存,缓存存在则更更新,不存在则跳过

        /// </summary>

        public abstract void Update(string key, object value);

        public abstract string WorkInfo { get; }

 

    }

这里新增对外一个属性:LocalInstance,是因为一但配置了AppConfig.Cache.MemCacheServers后:

原有的本机缓存就自动切换到分布式缓存,为了使用本机缓存依旧可以使用,所以提供LocalInstance属性。

一开始是对外三个:Instance(自动切换型)、LocalInstance、MemInstance。

大伙可以思考一下,为什么MemInstance被去掉了?感觉有点说不清道不明的感觉。

 

由于LocalCache是变更名称自CacheManage,而CacheManage在以前文章贴过源码,所以不重复了。

现在贴一下MemCache的源码:

  1  /// <summary>
  2     /// 分布式缓存类
  3     /// </summary>
  4     internal class MemCache : CacheManage
  5     {
  6         MemcachedClient client;
  7         internal MemCache()
  8         {
  9             MemcachedClient.Setup("MyCache", AppConfig.Cache.MemCacheServers.Split(','));
 10             client = MemcachedClient.GetInstance("MyCache");
 11
 12         }
 13
 14         public override void Add(string key, object value, double cacheMinutes)
 15         {
 16             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 17         }
 18         public override void Add(string key, object value, string fileName, double cacheMinutes)
 19         {
 20             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 21         }
 22
 23         public override void Add(string key, object value, string fileName)
 24         {
 25             client.Add(key, value);
 26         }
 27
 28         public override void Add(string key, object value)
 29         {
 30             client.Add(key, value);
 31         }
 32
 33         public override void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level)
 34         {
 35             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
 36         }
 37
 38         public override Dictionary<string, CacheDependencyInfo> CacheInfo
 39         {
 40             get { return null; }
 41         }
 42         DateTime allowCacheTableTime = DateTime.Now;
 43         private MDataTable cacheTable = null;
 44         public override MDataTable CacheTable
 45         {
 46             get
 47             {
 48                 if (cacheTable == null || DateTime.Now > allowCacheTableTime)
 49                 {
 50                     cacheTable = null;
 51                     cacheTable = new MDataTable();
 52                     Dictionary<string, Dictionary<string, string>> status = client.Stats();
 53                     if (status != null)
 54                     {
 55
 56                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
 57                         {
 58                             if (item.Value.Count > 0)
 59                             {
 60                                 MDataTable dt = MDataTable.CreateFrom(item.Value);
 61                                 if (cacheTable.Columns.Count == 0)//第一次
 62                                 {
 63                                     cacheTable = dt;
 64                                 }
 65                                 else
 66                                 {
 67                                     cacheTable.JoinOnName = "Key";
 68                                     cacheTable = cacheTable.Join(dt, "Value");
 69                                 }
 70                                 cacheTable.Columns["Value"].ColumnName = item.Key;
 71                             }
 72                         }
 73                     }
 74                     cacheTable.TableName = "MemCache";
 75                     allowCacheTableTime = DateTime.Now.AddMinutes(1);
 76                 }
 77                 return cacheTable;
 78             }
 79         }
 80
 81         public override void Clear()
 82         {
 83             client.FlushAll();
 84         }
 85
 86         public override bool Contains(string key)
 87         {
 88             return Get(key) != null;
 89         }
 90
 91         //int count = -1;
 92         //DateTime allowGetCountTime = DateTime.Now;
 93         public override int Count
 94         {
 95             get
 96             {
 97                 int count = 0;
 98                 MDataRow row = CacheTable.FindRow("Key='curr_items'");
 99                 if (row != null)
100                 {
101                     for (int i = 1; i < row.Columns.Count; i++)
102                     {
103                         count += int.Parse(row[i].strValue);
104                     }
105                 }
106                 return count;
107             }
108         }
109
110         public override object Get(string key)
111         {
112             return client.Get(key);
113         }
114
115
116         public override bool GetFileDependencyHasChanged(string key)
117         {
118             return false;
119         }
120
121         public override bool GetHasChanged(string key)
122         {
123             return false;
124         }
125
126         public override long RemainMemoryBytes
127         {
128             get { return 0; }
129         }
130
131         public override long RemainMemoryPercentage
132         {
133             get { return 0; }
134         }
135
136         public override void Remove(string key)
137         {
138             client.Delete(key);
139         }
140
141         public override void Set(string key, object value)
142         {
143             client.Set(key, value);
144         }
145
146         public override void Set(string key, object value, double cacheMinutes)
147         {
148             client.Set(key, value, DateTime.Now.AddMinutes(cacheMinutes));
149         }
150
151         public override void SetChange(string key, bool isChange)
152         {
153
154         }
155
156         public override void Update(string key, object value)
157         {
158             client.Replace(key, value);
159         }
160
161         DateTime allowGetWorkInfoTime = DateTime.Now;
162         string workInfo = string.Empty;
163         public override string WorkInfo
164         {
165             get
166             {
167                 if (workInfo == string.Empty || DateTime.Now > allowGetWorkInfoTime)
168                 {
169                     workInfo = null;
170                     Dictionary<string, Dictionary<string, string>> status = client.Status();
171                     if (status != null)
172                     {
173                         JsonHelper js = new JsonHelper(false, false);
174                         js.Add("OKServerCount", client.okServer.ToString());
175                         js.Add("DeadServerCount", client.errorServer.ToString());
176                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
177                         {
178                             js.Add(item.Key, JsonHelper.ToJson(item.Value));
179                         }
180                         js.AddBr();
181                         workInfo = js.ToString();
182                     }
183                     allowGetWorkInfoTime = DateTime.Now.AddMinutes(5);
184                 }
185                 return workInfo;
186             }
187         }
188     }

讲完实现的过程和贴完源码,下面讲一下使用过程了:

框架里使用MemCache功能的演示

1:服务端先安装,并运行起来

服务端的文件是这样的:

运行后的服务是这样的,这里开了两个服务进程,分别对应:11211和11212端口:

2:代码使用是这样的

原有的使用方式不变,只是增加了一行配置,就自动切换到分布式了,是不是从单机过渡到分布式太简单了。

通常我们不在代码里配置,而是配置在:

运行的结果是这样的:

总结

使用此框架,不管是进化到数据库读写分离,还是演进到分布式缓存,整个架构的升级过程,只需增加1行配置文件。

几年前就一直在构思,浮浮沉沉地随着框架的演进,如今顺水推舟地实现了,想想都觉得有点不可思议。

另外最后Top150大神群里,有人问我,最近写的文章有人打赏么?我只弱弱的回了一句:还没。

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

时间: 2024-08-22 06:34:34

CYQ.Data V5 分布式缓存MemCached应用开发介绍的相关文章

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

前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: + View Code 其实更多的时间,是放在ASP.NET Aries 业务开发框架上,上里下外全部重构了一遍. 前几天,决定把Redis集成进来,一鼓作气,解决了. 下面分享一下经历: 最初的想法: 一开始我是拒绝的,不愿动态调用第三方的客户端(关联依赖的dll太多). 最近打算支持Redis,有

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还没开源  终于等到开源了! 也许吧,只有把cyq.data最终开源了,才能解决单身问题,不然我在这上面花的时间太多,都没时间和妹子聊天了.   几个重要网址: 源码SVN地址:https://github.com/cyq11

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

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

原创:.NET版分布式缓存Memcached测试实例

下面测试下分布式缓存Memcached软件,一直在学习关注大访问量网站的缓存是如何实现,之前看过Memcached的资料,忙于没有时间来真正测试一下,本文测试分布式缓存Memcached的环境如下:(两台电脑作为服务器) 第一台: CPU:Inter(R) Pentium(R) 4 CPU 2.8G 内存:1G 系统:windows 7 IIS: IIS 7 IP:172.10.1.97 环境:本地 安装:memcached 1.2.1 for Win32 第二台: CPU:Inter(R) P

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

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

关于分布式缓存Memcached详解

关于分布式缓存memcached详解 libevent事件机制 memcached基于libevent事件处理,用相关资料上描述,libevent是个程序库,它将linux的epoll.bsd类操作系统的kqueue等事件处理功能封装成统一的接口.即使对服务器的连接数增加,也能发挥o(1)的性能. memcached服务器,缓存数据都是以key-value hash表的内存存储,最大key不超过250个字符,最大value项默认不超过1m,因此重启程序和服务器都会导致数据丢失,但它会消耗更低的c

CYQ.Data V5 MDataTable 专属篇介绍

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