前言

今天大伙还在热议关于.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的源码:

  /// <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 > )
{
MDataTable dt = MDataTable.CreateFrom(item.Value);
if (cacheTable.Columns.Count == )//第一次
{
cacheTable = dt;
}
else
{
cacheTable.JoinOnName = "Key";
cacheTable = cacheTable.Join(dt, "Value");
}
cacheTable.Columns["Value"].ColumnName = item.Key;
}
}
}
cacheTable.TableName = "MemCache";
allowCacheTableTime = DateTime.Now.AddMinutes();
}
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 = ;
MDataRow row = CacheTable.FindRow("Key='curr_items'");
if (row != null)
{
for (int i = ; 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 ; }
} public override long RemainMemoryPercentage
{
get { return ; }
} 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();
}
return workInfo;
}
}
}

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

框架里使用MemCache功能的演示

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

服务端的文件是这样的:

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

2:代码使用是这样的

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

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

运行的结果是这样的:

总结

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

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

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

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

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

    前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: 3 ...

  2. CYQ.Data V5 分布式自动化缓存设计介绍(二)

    前言: 最近一段时间,开始了<IT连>创业,所以精力和写的文章多数是在分享创业的过程. 而关于本人三大框架CYQ.Data.Aries.Taurus.MVC的相关文章,基本都很少写了. 但 ...

  3. CYQ.Data V5 分布式自动化缓存设计介绍

    前方: 其实完成这个功能之前,我就在思考:是先把想法写了来,和大伙讨论讨论后再实现,还是实现后再写文论述自己的思维. 忽然脑后传来一个声音说:你发文后会进入发呆阶段. 所以还是静下心,让我轻轻地把代码 ...

  4. CYQ.Data 对于分布式缓存Redis、MemCache高可用的改进及性能测试

    背景: 随着.NET Core 在 Linux 下的热动,相信动不动就要分布式或集群的应用的需求,会慢慢火起来. 所以这段时间一直在研究和思考分布式集群的问题,同时也在思考把几个框架的思维相对提升到这 ...

  5. CYQ.Data 支持分布式数据库(主从备)高可用及负载调试

    前言: 继上一篇,介绍 CYQ.Data 在分布式缓存上支持高可用,详见:CYQ.Data 对于分布式缓存Redis.MemCache高可用的改进及性能测试 本篇介绍 CYQ.Data 在对数据库层面 ...

  6. 终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了

    前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说 ...

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

    前言: 框架开源后,学习使用的人越来越多了,所以我也更加积极的用代码回应了. 在框架完成了:数据库读写分离功能 和 分布式缓存功能 后: 经过三天三夜的不眠不休,终于完成框架第三个重量级的功能:自动化 ...

  8. 终于等到你:CYQ.Data V5系列 (ORM数据层,支持.NET Core)最新版本开源了

    前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说 ...

  9. NET分布式缓存Memcached测试体验

    原文地址:http://onlyonewt.blog.sohu.com/160168896.html 一直在学习关注大访问量网站的缓存是如何实现,之前看过Memcached的资料,忙于没有时间来真正测 ...

随机推荐

  1. 如何用 SQL Tuning Advisor (STA) 优化SQL语句

    在Oracle10g之前,优化SQL是个比较费力的技术活,不停的分析执行计划,加hint,分析统计信息等等.在10g中,Oracle推出了自己的SQL优化辅助工具: SQL优化器(SQL Tuning ...

  2. 【leetcode】Remove Nth Node From End of List

    题目简述: Given a linked list, remove the nth node from the end of list and return its head. For example ...

  3. android_studio上传svn的时候那些不提交

    buid文件夹不需要提交

  4. jQuery九类选择器详解

    (1)基本选择器 <body> <div id="div1ID">div1</div> <div id="div2ID" ...

  5. json相关,浏览器打开json格式的api接口时,进行格式化,chrome插件

    在chrome浏览器中安装Google jsonview插件能够自动格式化json格式的数据.

  6. html5+css3+jquery完成响应式布局

    响应式布局,流体布局,自适应布局,静态布局等等,这是移动设备的飞速发展,前端人员不得不跟上潮流的一个方向.实际上各种设备尺寸,分辨率大都不同,真的适应各种设备依旧是不现实的,首先是浏览器差异化,让我们 ...

  7. iOS 真机测试时报错:Provisioning profile "iOS Team Provisioning Profile: XXX” doesn't include the currently selected device “XXX”.

    这几天因工作需要,去给客户演示iOS项目打包的过程.之前演示都是顺利的,但后来客户自己操作时打电话说遇到了问题,出现报错. 就过去看了一下,发现一个很陌生的错误提示: The operation co ...

  8. jackrabbit学习笔记(1)

    http://dove19900520.iteye.com/blog/1654346 看的这个文章照着来的,遇到了一些问题,记录一下 运行报这个错:NamespaceException: wiki: ...

  9. 获取终端ip地址

    网上找的,记录下 import java.io.*; import java.net.*; import java.util.*; //import org.apache.http.conn.util ...

  10. SOAPUI使用教程-测试JDBC数据库

    soapUI中有除了开源版本的一些非常实用的功能: 使用在项目级配置的JDBC连接 使用向导创建复杂的查询. 结果显示XML输出视图(以及该使用向导在此视图中提供的XPath断言). 提供JDBC连接 ...