论底层封装--Memcached

来源:互联网 发布:网络市场调研方法 编辑:程序博客网 时间:2024/06/16 00:13

背景:


这件事要从早之前说起,先前,我们考试系统中说要一起讲一下底层的东西。当时,组长给我和一清分的这个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;             }            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;            }            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的方法,真正在项目中使用,上手也很快,大概用了几个小时就解决了这个问题,当然解决这个问题的时候,遇到了很多的问题,将问题一个一个的解决的过程就是我们不断学习的过程,就像老师说的那样:让我们拥有了“我一定能解决这个问题”的决心。我们在项目中也是想到了如何让用户更加便捷的使用,简化了配置文件,这一点上,也做了努力。


所以我觉得,做一个东西,我们需要的是一份自信、一份为用户服务的心。



1 0
原创粉丝点击