论底层封装--Memcached
背景:
这件事要从早之前说起,先前,我们考试系统中说要一起讲一下底层的东西。当时,组长给我和一清分的这个Memcached这个东西,要我们整理一下,然后给大家讲一讲。起初的东西不知道如何下手,因为没有资料、没有视频等等,我就找了九期的师哥要了一些资料,通过这些资料,我们做出了一些Demo,当时我们做出来之后,就没有真正运用到项目中,后来ITOO项目3.0开始后,底层说要叫几个负责模块的人,因为当时我做过这个方面,我就担任了底层缓存的任务。
第一版本:
我们底层之前是这样写的,在底层类库中:
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="enyim.com"> <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /> </sectionGroup> <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /> </configSections> <enyim.com> <memcached> <servers> <add address="127.0.0.1" port="11211" /> </servers> <socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> </memcached> </enyim.com> <memcached keyTransformer="Enyim.Caching.TigerHashTransformer, Enyim.Caching"> <servers> <add address="127.0.0.1" port="11211" /> </servers> <socketPool minPoolSize="2" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> </memcached> </configuration></span>
写入我们自己封装的Memecached类:
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Web; using Enyim.Caching; using Enyim.Caching.Memcached; using Enyim.Caching.Configuration; namespace ITOO.Library.Core.Memcache { public static class MemcacheHelper { private static MemcachedClient mc; static MemcacheHelper() { mc=new MemcachedClient(); } /// <summary> /// 当缓存中没有数据的时候将数据写入缓存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool Add(string key, object value) { return mc.Store(StoreMode.Add, key, value); } /// <summary> /// 当缓存中没有数据的时候将数据写入缓存(含过期时间) /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiresAt">过期时间</param> /// <returns></returns> public static bool Add(string key, object value, DateTime expiresAt) { return mc.Store(StoreMode.Add, key, value,expiresAt); } /// <summary> /// 替换缓存中的数据 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool Replace(string key, object value) { return mc.Store(StoreMode.Replace, key, value); } /// <summary> /// 替换缓存中的数据(含过期时间) /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiresAt">过期时间</param> /// <returns></returns> public static bool Replace(string key, object value, DateTime expiresAt) { return mc.Store(StoreMode.Replace, key, value, expiresAt); } /// <summary> /// 尝试获取缓存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool TryGet(string key, out object value) { return mc.TryGet(key, out value); } /// <summary> /// 获取缓存 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static T Get<T>(string key) { return mc.Get<T>(key); } /// <summary> /// 获取缓存 /// </summary> /// <param name="key"></param> /// <returns></returns> public static object Get(string key) { return mc.Get(key); } /// <summary> /// 删除缓存 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Remove(string key) { return mc.Remove(key); } /// <summary> /// 获取一组缓存 /// </summary> /// <param name="keys"></param> /// <returns></returns> public static IDictionary<string, object> Get(IEnumerable<string> keys) { return mc.Get(keys); } } }</span>
这个类中引用了:
<span style="font-size:18px;">using Enyim.Caching; using Enyim.Caching.Memcached; using Enyim.Caching.Configuration;</span>
第二版本的修改:
是关于缓存的文件。当然到了这几步了之后,发现,这个类只能添加一些基本类型,不能在缓存中添加object类型。于是,我就想,如何封装object类型的数据。
初步的时候,调用MemcacheHelper方法中的:
<span style="font-size:18px;"> /// <summary> /// 当缓存中没有数据的时候将数据写入缓存(含过期时间) /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiresAt">过期时间</param> /// <returns></returns> public static bool Add(string key, object value, DateTime expiresAt) { return mc.Store(StoreMode.Add, key, value,expiresAt); }</span>
方法的时候,根据说明第二个参数就是Object类型的,应该是可以的,但是报了一个错:
未标记为可序列化
但是就查了查,意思很简单就是:序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。简单一点说,就是在我们自己定义的对象上添加添加为:Serializable。
我们在定义实体的时候,就要这样写:
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ITOO.Library.Test { [Serializable] public class MM { public string s1; public string s2; public string s4; } }</span>
这样就是最简单的序列化,我们用IO流说明一下是否已经序列化了:
<span style="font-size:18px;"> //以下代码片段说明了如何将此类的一个实例序列化为一个文件: MM obj = new MM(); obj.s1 = "1"; obj.s2 = "24"; obj.s4 = "一些字符串"; IFormatter formatter = new BinaryFormatter(); Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None); formatter.Serialize(stream, obj); stream.Close();</span>
下面的代码是将一个文件反序列化为对象:
<span style="font-size:18px;"> //将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。以下代码片段说明了如何进行此操作。 IFormatter formatter1 = new BinaryFormatter(); Stream stream1 = new FileStream("MyFile.bin", FileMode.Open, FileAccess.Read, FileShare.Read); MyObject obj1 = (MyObject)formatter1.Deserialize(stream1); stream1.Close(); Console.WriteLine("n1: {0}", obj1.n1); Console.WriteLine("n2: {0}", obj1.n2); Console.WriteLine("str: {0}", obj1.str);</span>
其实我们用的就是IO流的技术将一个对象的数据,存数为一个文件流,在讲文件流反序列化为对象,就可以了。
当然这就给我们一个思路就是我们可以将对象通过流的技术,将对象序列化为string类型,然后存储在缓存中,当从缓存中取出的时候,我们就可以将这个string类型的反序列化为对象类型就可以了。
这样就出现了一个问题,我们不能将缓存存为文件类型保存到本地,这样数据就会暴漏。因此,就将IO流的思想封装在了序列化和反序列化两个方法中:
<span style="font-size:18px;"> #region Serialize<T>(T obj)--邱慕夏--2015年4月21日15:52:43 /// <summary> /// 序列化 对象到字符串 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <returns></returns> public static string Serialize<T>(T obj) { try { IFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, obj); stream.Position = 0; byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); stream.Flush(); stream.Close(); return Convert.ToBase64String(buffer); } catch (Exception ex) { throw new Exception("序列化失败,原因:" + ex.Message); } } #endregion #region Desrialize<T>(T obj, string str)--邱慕夏--2015年4月21日15:53:11 /// <summary> /// 反序列化 字符串到对象 /// </summary> /// <param name="obj">泛型对象</param> /// <param name="str">要转换为对象的字符串</param> /// <returns>反序列化出来的对象</returns> public static T Desrialize<T>(T obj, string str) { try { obj = default(T); IFormatter formatter = new BinaryFormatter(); byte[] buffer = Convert.FromBase64String(str); MemoryStream stream = new MemoryStream(buffer); obj = (T)formatter.Deserialize(stream); stream.Flush(); stream.Close(); } catch (Exception ex) { throw new Exception("反序列化失败,原因:" + ex.Message); } return obj; } #endregion </span>
然后根据这两个方法,封装缓存中object和string类型之间转化:
<span style="font-size:18px;"> #region Set(string key, object value, DateTime expiry)---邱慕夏--2015年4月21日14:14:52 public static bool Set(string key, object value, DateTime expiry) { string m1; m1 = Serialize(value); return mc.Set(key, m1, expiry); } #endregion #region T GetObject<T>(T obj, string key)--邱慕夏--2015年4月21日15:52:23 /// <summary> /// 获取Memcache /// </summary> /// <param name="key"></param> /// <returns></returns> public static T GetObject<T>(T obj, string key) { string m1 = (string)mc.Get(key); obj = (T)Desrialize(obj, m1); return obj; } #endregion</span>
这样,我们就可以调用方法了:
<span style="font-size:18px;"> MemcacheHelper.Add("syq1", "我是底层", DateTime.Now.AddMinutes(20)); MM mm = new MM(); mm.s1 = "123"; mm.s2 = "1234567"; mm.s4 = "123480-5"; MemcacheHelper.Set("k12", mm, DateTime.Now.AddMinutes(20)); MM m2 = new MM(); MM m1 = (MM)MemcacheHelper.GetObject(m2, "k12");</span>
同时,我们不想每次都要调用MemcacheHelper之前,都要配置一次缓存文件,怎么办?
于是我们就将缓存文件中的配置都去掉,只用了AppSetting节点,来实现对memecache的配置:
配置文件如下:
<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="enyim.com"> <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /> </sectionGroup> <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" /> </configSections> <appSettings> <!--注意这里的Serverlist添加的时候,要是添加的数组,将用“,”分开--> <add key="serverlist" value="127.0.0.1:11211" /> </appSettings> <!--<enyim.com> <memcached> <servers> <add address="127.0.0.1" port="11211" /> </servers> <socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> </memcached> </enyim.com> <memcached keyTransformer="Enyim.Caching.TigerHashTransformer, Enyim.Caching"> <servers> <add address="127.0.0.1" port="11211" /> </servers> <socketPool minPoolSize="2" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> </memcached>--> </configuration> </span>
最终版本:
完整的MemcacheHelper封装:
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Configuration; using Memcached.ClientLibrary; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace ITOO.Library.Core.Memcache { public class MemcacheHelper { private static MemcachedClient mc; #region MemcacheByCsharp--邱慕夏--2015年4月21日15:54:00 /// <summary> /// 连接Memcache /// </summary> static MemcacheHelper() { //String[] serverlist = { "127.0.0.1:11211" }; string server = ConfigurationManager.AppSettings["serverlist"]; string[] serverlist; serverlist = server.Split(','); // initialize the pool for memcache servers SockIOPool pool = SockIOPool.GetInstance("test"); pool.SetServers(serverlist); pool.Initialize(); mc = new MemcachedClient(); mc.PoolName = "test"; mc.EnableCompression = false; } #endregion #region Set(string key, object value, DateTime expiry)---邱慕夏--2015年4月21日14:14:31 /// <summary> /// 设置Memcache的值 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiry"></param> /// <returns></returns> //public static bool Set(string key, object value, DateTime expiry) //{ // IFormatter formatter = new BinaryFormatter(); // Stream stream = new FileStream(key, FileMode.Create, // FileAccess.Write, FileShare.None); // formatter.Serialize(stream, value); // stream.Close(); // bool flag; // flag = true; // return flag; // //return mc.Set(key, stream, expiry); //} #endregion #region Set(string key, object value, DateTime expiry)---邱慕夏--2015年4月21日14:14:52 public static bool Set(string key, object value, DateTime expiry) { string m1; m1 = Serialize(value); return mc.Set(key, m1, expiry); } #endregion #region T GetObject<T>(T obj, string key)--邱慕夏--2015年4月21日15:52:23 /// <summary> /// 获取Memcache /// </summary> /// <param name="key"></param> /// <returns></returns> public static T GetObject<T>(T obj, string key) { string m1 = (string)mc.Get(key); obj = (T)Desrialize(obj, m1); return obj; } #endregion #region Serialize<T>(T obj)--邱慕夏--2015年4月21日15:52:43 /// <summary> /// 序列化 对象到字符串 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <returns></returns> public static string Serialize<T>(T obj) { try { IFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, obj); stream.Position = 0; byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); stream.Flush(); stream.Close(); return Convert.ToBase64String(buffer); } catch (Exception ex) { throw new Exception("序列化失败,原因:" + ex.Message); } } #endregion #region Desrialize<T>(T obj, string str)--邱慕夏--2015年4月21日15:53:11 /// <summary> /// 反序列化 字符串到对象 /// </summary> /// <param name="obj">泛型对象</param> /// <param name="str">要转换为对象的字符串</param> /// <returns>反序列化出来的对象</returns> public static T Desrialize<T>(T obj, string str) { try { obj = default(T); IFormatter formatter = new BinaryFormatter(); byte[] buffer = Convert.FromBase64String(str); MemoryStream stream = new MemoryStream(buffer); obj = (T)formatter.Deserialize(stream); stream.Flush(); stream.Close(); } catch (Exception ex) { throw new Exception("反序列化失败,原因:" + ex.Message); } return obj; } #endregion /// <summary> /// 当缓存中没有数据的时候将数据写入缓存 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool Add(string key, object value) { return mc.Set(key, value); } /// <summary> /// 当缓存中没有数据的时候将数据写入缓存(含过期时间) /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiresAt">过期时间</param> /// <returns></returns> public static bool Add(string key, object value, DateTime expiresAt) { return mc.Set(key, value, expiresAt); } /// <summary> /// 替换缓存中的数据 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public static bool Replace(string key, object value) { return mc.Replace(key, value); } /// <summary> /// 替换缓存中的数据(含过期时间) /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="expiresAt">过期时间</param> /// <returns></returns> public static bool Replace(string key, object value, DateTime expiresAt) { return mc.Replace(key, value, expiresAt); } /// <summary> /// 尝试获取缓存,判断是否存在 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> //public static bool TryGet(string key, out object value) //{ // return mc.KeyExists(key); //} /// <summary> /// 获取缓存 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public static object Get(string key) { return mc.Get(key); } /// <summary> /// 删除缓存 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Remove(string key) { return mc.Delete(key); } /// <summary> /// 获取一组缓存 /// </summary> /// <param name="keys"></param> /// <returns></returns> public static IDictionary<string, object> Get(IEnumerable<string> keys) { List<string> list = new List<string>(); List<object> tempobj = new List<object>(); IDictionary<string, object> obj = new Dictionary<string, object>(); foreach (String slabId in keys) { list.Add(slabId); } for(int i=0;i<list.Count();i++){ tempobj.Add(mc.Get(list[i])); obj.Add(list[i], tempobj); } return obj; } } }</span>
我们尝试调用一次:
<span style="font-size:18px;">using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using ITOO.Library.Core.Memcache; using Memcached.ClientLibrary; namespace ITOO.Library.Test { public class test { public static void Main(string[] arg) { #region 使用底层的MemcacheHelper ////使用底层的MemcacheHelper MemcacheHelper.Add("syq1", "我是底层", DateTime.Now.AddMinutes(20)); MM mm = new MM(); mm.s1 = "123"; mm.s2 = "1234567"; mm.s4 = "123480-5"; MemcacheHelper.Set("k12", mm, DateTime.Now.AddMinutes(20)); MM m2 = new MM(); MM m1 = (MM)MemcacheHelper.GetObject(m2, "k12"); string[] m= new string[]{"syq1"}; IDictionary<string, object> obj = new Dictionary<string, object>(); obj = MemcacheHelper.Get(m); #endregion } } }</span>
在客户端上加上上面的配置文件:
<span style="font-size:18px;"><appSettings> <!--注意这里的Serverlist添加的时候,要是添加的数组,将用“,”分开--> <add key="serverlist" value="127.0.0.1:11211" /> </appSettings></span>
总结:
这件事情,因为当时,我们一起研究了Memcache所以,有机会封装底层的MemcacheHelper的方法,真正在项目中使用,上手也很快,大概用了几个小时就解决了这个问题,当然解决这个问题的时候,遇到了很多的问题,将问题一个一个的解决的过程就是我们不断学习的过程,就像老师说的那样:让我们拥有了“我一定能解决这个问题”的决心。我们在项目中也是想到了如何让用户更加便捷的使用,简化了配置文件,这一点上,也做了努力。
所以我觉得,做一个东西,我们需要的是一份自信、一份为用户服务的心。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。