论底层封装--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的方法,真正在项目中使用,上手也很快,大概用了几个小时就解决了这个问题,当然解决这个问题的时候,遇到了很多的问题,将问题一个一个的解决的过程就是我们不断学习的过程,就像老师说的那样:让我们拥有了“我一定能解决这个问题”的决心。我们在项目中也是想到了如何让用户更加便捷的使用,简化了配置文件,这一点上,也做了努力。


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



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。