CYQ.Data V5 分布式缓存MemCached应用开发介绍
2016-06-28 10:15
337 查看
前言
今天大伙还在热议关于.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里提取接口ICache2: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的源码:
/// <summary> /// 分布式缓存类 /// </summary> internal class MemCache : CacheManage { MemcachedClient client; internal MemCache() { MemcachedClient.Setup("MyCache", AppConfig.Cache.MemCacheServers.Split(',')); client = MemcachedClient.GetInstance("MyCache"); } public override void Add(string key, object value, double cacheMinutes) { client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes)); } public override void Add(string key, object value, string fileName, double cacheMinutes) { client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes)); } public override void Add(string key, object value, string fileName) { client.Add(key, value); } public override void Add(string key, object value) { client.Add(key, value); } public override void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level) { client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes)); } public override Dictionary<string, CacheDependencyInfo> CacheInfo { get { return null; } } DateTime allowCacheTableTime = DateTime.Now; private MDataTable cacheTable = null; public override MDataTable CacheTable { get { if (cacheTable == null || DateTime.Now > allowCacheTableTime) { cacheTable = null; cacheTable = new MDataTable(); Dictionary<string, Dictionary<string, string>> status = client.Stats(); if (status != null) { foreach (KeyValuePair<string, Dictionary<string, string>> item in status) { if (item.Value.Count > 0) { MDataTable dt = MDataTable.CreateFrom(item.Value); if (cacheTable.Columns.Count == 0)//第一次 { cacheTable = dt; } else { cacheTable.JoinOnName = "Key"; cacheTable = cacheTable.Join(dt, "Value"); } cacheTable.Columns["Value"].ColumnName = item.Key; } } } cacheTable.TableName = "MemCache"; allowCacheTableTime = DateTime.Now.AddMinutes(1); } return cacheTable; } } public override void Clear() { client.FlushAll(); } public override bool Contains(string key) { return Get(key) != null; } //int count = -1; //DateTime allowGetCountTime = DateTime.Now; public override int Count { get { int count = 0; MDataRow row = CacheTable.FindRow("Key='curr_items'"); if (row != null) { for (int i = 1; i < row.Columns.Count; i++) { count += int.Parse(row[i].strValue); } } return count; } } public override object Get(string key) { return client.Get(key); } public override bool GetFileDependencyHasChanged(string key) { return false; } public override bool GetHasChanged(string key) { return false; } public override long RemainMemoryBytes { get { return 0; } } public override long RemainMemoryPercentage { get { return 0; } } public override void Remove(string key) { client.Delete(key); } public override void Set(string key, object value) { client.Set(key, value); } public override void Set(string key, object value, double cacheMinutes) { client.Set(key, value, DateTime.Now.AddMinutes(cacheMinutes)); } public override void SetChange(string key, bool isChange) { } public override void Update(string key, object value) { client.Replace(key, value); } DateTime allowGetWorkInfoTime = DateTime.Now; string workInfo = string.Empty; public override string WorkInfo { get { if (workInfo == string.Empty || DateTime.Now > allowGetWorkInfoTime) { workInfo = null; Dictionary<string, Dictionary<string, string>> status = client.Status(); if (status != null) { JsonHelper js = new JsonHelper(false, false); js.Add("OKServerCount", client.okServer.ToString()); js.Add("DeadServerCount", client.errorServer.ToString()); foreach (KeyValuePair<string, Dictionary<string, string>> item in status) { js.Add(item.Key, JsonHelper.ToJson(item.Value)); } js.AddBr(); workInfo = js.ToString(); } allowGetWorkInfoTime = DateTime.Now.AddMinutes(5); } return workInfo; } } }
讲完实现的过程和贴完源码,下面讲一下使用过程了:
框架里使用MemCache功能的演示
1:服务端先安装,并运行起来
服务端的文件是这样的:运行后的服务是这样的,这里开了两个服务进程,分别对应:11211和11212端口:
2:代码使用是这样的
原有的使用方式不变,只是增加了一行配置,就自动切换到分布式了,是不是从单机过渡到分布式太简单了。
通常我们不在代码里配置,而是配置在:
运行的结果是这样的:
总结
使用此框架,不管是进化到数据库读写分离,还是演进到分布式缓存,整个架构的升级过程,只需增加1行配置文件。几年前就一直在构思,浮浮沉沉地随着框架的演进,如今顺水推舟地实现了,想想都觉得有点不可思议。
另外最后Top150大神群里,有人问我,最近写的文章有人打赏么?我只弱弱的回了一句:还没。
相关文章推荐
- Redis和Memcached的区别
- Memcached全面剖析
- php CI框架中调用接口 memcache缓存数据
- PHP遍历memcache
- Ubuntu安装Apache PHP Mysql phpmyadmin Memcache CURL
- 让laravel5支持memcache
- memcache的安装和使用
- memcache与redis的区别
- 【 Linux 】为lnmp架构添加memcached支持
- Redis、Memcached、Guava、Ehcache中的算法
- memcached的安装和使用
- memcached c#
- Memcached常规应用与分布式部署方案
- Redis与Memcache异同(三)
- Redis与Memcache异同(二)
- Redis与Memcache异同(一)
- mongodb redis memcached的对比
- 利用nginx+tomcat+memcached组建web服务器负载均衡
- 安装memcached服务器和PHP中添加memcache拓展模块
- memcached学习总结