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

2年前 (2022) 程序员胖胖胖虎阿
220 0 0

前言

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

好,回头说说框架: 

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

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

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

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

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

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

框架集成MemCache

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

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

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

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

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

框架的代码重构思维

最终定案的重构思维:

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的方法:

/// <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:服务端先安装,并运行起来

服务端的文件是这样的:

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

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

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

2:代码使用是这样的

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

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

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

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

运行的结果是这样的:

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

总结

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

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

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

版权声明:程序员胖胖胖虎阿 发表于 2022年10月4日 上午2:24。
转载请注明:CYQ.Data V5 分布式缓存MemCached应用开发介绍 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...