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

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
阿里云实时数仓实战 - 项目介绍及架构设计
课程简介 1)学习搭建一个数据仓库的过程,理解数据在整个数仓架构的从采集、存储、计算、输出、展示的整个业务流程。 2)整个数仓体系完全搭建在阿里云架构上,理解并学会运用各个服务组件,了解各个组件之间如何配合联动。 3&nbsp;)前置知识要求 &nbsp; 课程大纲 第一章&nbsp;了解数据仓库概念 初步了解数据仓库是干什么的 第二章&nbsp;按照企业开发的标准去搭建一个数据仓库 数据仓库的需求是什么 架构 怎么选型怎么购买服务器 第三章&nbsp;数据生成模块 用户形成数据的一个准备 按照企业的标准,准备了十一张用户行为表 方便使用 第四章&nbsp;采集模块的搭建 购买阿里云服务器 安装 JDK 安装 Flume 第五章&nbsp;用户行为数据仓库 严格按照企业的标准开发 第六章&nbsp;搭建业务数仓理论基础和对表的分类同步 第七章&nbsp;业务数仓的搭建&nbsp; 业务行为数仓效果图&nbsp;&nbsp;
相关文章
|
3月前
|
缓存 算法 NoSQL
【分布式详解】一致性算法、全局唯一ID、分布式锁、分布式事务、 分布式缓存、分布式任务、分布式会话
分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性(consistency)。副本一致性是针对分布式系统而言的,不是针对某一个副本而言。强一致性(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。单调一致性(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。
375 0
|
存储 缓存 JSON
实战干货 | 分布式多级缓存设计方案
分布式多级缓存设计方案,解决海量数据读取的性能问题,包含多级缓存的存储设计,流程设计;利用多数据副本保证数据的可用性,同时通过不同数据源特点提供更高性能、更多场景数据差异化的支持
937 0
实战干货 | 分布式多级缓存设计方案
|
3月前
|
缓存 网络协议 算法
Golang简单实现 分布式缓存+一致性哈希+节点再平衡(gossip + consistent + rebalance)
Golang简单实现 分布式缓存+一致性哈希+节点再平衡(gossip + consistent + rebalance)
60 0
|
2月前
|
缓存 NoSQL Redis
如何在Python中使用Redis或Memcached进行缓存?
如何在Python中使用Redis或Memcached进行缓存?
26 2
|
8月前
|
缓存 关系型数据库 MySQL
高性能内存对象缓存Memcached
高性能内存对象缓存Memcached案例
|
6月前
|
存储 缓存 NoSQL
Redis高级篇 - 分布式缓存-2
Redis高级篇 - 分布式缓存-2
24 0
|
6月前
|
存储 缓存 NoSQL
Redis高级篇 - 分布式缓存-1
Redis高级篇 - 分布式缓存-1
48 0
|
6月前
|
存储 缓存 NoSQL
springboot快速整合Memcached缓存技术
springboot快速整合Memcached缓存技术
|
12月前
|
消息中间件 存储 缓存
干货 | 分布式缓存与DB秒级一致设计实践(2)
干货 | 分布式缓存与DB秒级一致设计实践
|
12月前
|
存储 消息中间件 缓存
干货 | 分布式缓存与DB秒级一致设计实践(1)
干货 | 分布式缓存与DB秒级一致设计实践