你的位置:首页 > ASP.net教程

[ASP.net教程]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的方法:

/// <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 RemainMemoryBytes127     {128       get { return 0; }129     }130 131     public override long RemainMemoryPercentage132     {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 WorkInfo164     {165       get166       {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大神群里,有人问我,最近写的文章有人打赏么?我只弱弱的回了一句:还没。