• CYQ.Data V5 分布式缓存MemCached应用开发介绍


    前言

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

      1  /// <summary>
      2     /// 分布式缓存类
      3     /// </summary>
      4     internal class MemCache : CacheManage
      5     {
      6         MemcachedClient client;
      7         internal MemCache()
      8         {
      9             MemcachedClient.Setup("MyCache", AppConfig.Cache.MemCacheServers.Split(','));
     10             client = MemcachedClient.GetInstance("MyCache");
     11 
     12         }
     13 
     14         public override void Add(string key, object value, double cacheMinutes)
     15         {
     16             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
     17         }
     18         public override void Add(string key, object value, string fileName, double cacheMinutes)
     19         {
     20             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
     21         }
     22 
     23         public override void Add(string key, object value, string fileName)
     24         {
     25             client.Add(key, value);
     26         }
     27 
     28         public override void Add(string key, object value)
     29         {
     30             client.Add(key, value);
     31         }
     32 
     33         public override void Add(string key, object value, string fileName, double cacheMinutes, CacheItemPriority level)
     34         {
     35             client.Add(key, value, DateTime.Now.AddMinutes(cacheMinutes));
     36         }
     37 
     38         public override Dictionary<string, CacheDependencyInfo> CacheInfo
     39         {
     40             get { return null; }
     41         }
     42         DateTime allowCacheTableTime = DateTime.Now;
     43         private MDataTable cacheTable = null;
     44         public override MDataTable CacheTable
     45         {
     46             get
     47             {
     48                 if (cacheTable == null || DateTime.Now > allowCacheTableTime)
     49                 {
     50                     cacheTable = null;
     51                     cacheTable = new MDataTable();
     52                     Dictionary<string, Dictionary<string, string>> status = client.Stats();
     53                     if (status != null)
     54                     {
     55 
     56                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
     57                         {
     58                             if (item.Value.Count > 0)
     59                             {
     60                                 MDataTable dt = MDataTable.CreateFrom(item.Value);
     61                                 if (cacheTable.Columns.Count == 0)//第一次
     62                                 {
     63                                     cacheTable = dt;
     64                                 }
     65                                 else
     66                                 {
     67                                     cacheTable.JoinOnName = "Key";
     68                                     cacheTable = cacheTable.Join(dt, "Value");
     69                                 }
     70                                 cacheTable.Columns["Value"].ColumnName = item.Key;
     71                             }
     72                         }
     73                     }
     74                     cacheTable.TableName = "MemCache";
     75                     allowCacheTableTime = DateTime.Now.AddMinutes(1);
     76                 }
     77                 return cacheTable;
     78             }
     79         }
     80 
     81         public override void Clear()
     82         {
     83             client.FlushAll();
     84         }
     85 
     86         public override bool Contains(string key)
     87         {
     88             return Get(key) != null;
     89         }
     90 
     91         //int count = -1;
     92         //DateTime allowGetCountTime = DateTime.Now;
     93         public override int Count
     94         {
     95             get
     96             {
     97                 int count = 0;
     98                 MDataRow row = CacheTable.FindRow("Key='curr_items'");
     99                 if (row != null)
    100                 {
    101                     for (int i = 1; i < row.Columns.Count; i++)
    102                     {
    103                         count += int.Parse(row[i].strValue);
    104                     }
    105                 }
    106                 return count;
    107             }
    108         }
    109 
    110         public override object Get(string key)
    111         {
    112             return client.Get(key);
    113         }
    114 
    115 
    116         public override bool GetFileDependencyHasChanged(string key)
    117         {
    118             return false;
    119         }
    120 
    121         public override bool GetHasChanged(string key)
    122         {
    123             return false;
    124         }
    125 
    126         public override long RemainMemoryBytes
    127         {
    128             get { return 0; }
    129         }
    130 
    131         public override long RemainMemoryPercentage
    132         {
    133             get { return 0; }
    134         }
    135 
    136         public override void Remove(string key)
    137         {
    138             client.Delete(key);
    139         }
    140 
    141         public override void Set(string key, object value)
    142         {
    143             client.Set(key, value);
    144         }
    145 
    146         public override void Set(string key, object value, double cacheMinutes)
    147         {
    148             client.Set(key, value, DateTime.Now.AddMinutes(cacheMinutes));
    149         }
    150 
    151         public override void SetChange(string key, bool isChange)
    152         {
    153 
    154         }
    155 
    156         public override void Update(string key, object value)
    157         {
    158             client.Replace(key, value);
    159         }
    160 
    161         DateTime allowGetWorkInfoTime = DateTime.Now;
    162         string workInfo = string.Empty;
    163         public override string WorkInfo
    164         {
    165             get
    166             {
    167                 if (workInfo == string.Empty || DateTime.Now > allowGetWorkInfoTime)
    168                 {
    169                     workInfo = null;
    170                     Dictionary<string, Dictionary<string, string>> status = client.Status();
    171                     if (status != null)
    172                     {
    173                         JsonHelper js = new JsonHelper(false, false);
    174                         js.Add("OKServerCount", client.okServer.ToString());
    175                         js.Add("DeadServerCount", client.errorServer.ToString());
    176                         foreach (KeyValuePair<string, Dictionary<string, string>> item in status)
    177                         {
    178                             js.Add(item.Key, JsonHelper.ToJson(item.Value));
    179                         }
    180                         js.AddBr();
    181                         workInfo = js.ToString();
    182                     }
    183                     allowGetWorkInfoTime = DateTime.Now.AddMinutes(5);
    184                 }
    185                 return workInfo;
    186             }
    187         }
    188     }

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

    框架里使用MemCache功能的演示

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

    服务端的文件是这样的:

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

    2:代码使用是这样的

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

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

    运行的结果是这样的:

    总结

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

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

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

  • 相关阅读:
    [转载-声网]18个实时音视频开发中会用到开源项目
    linux shell命令之wc/split及特殊字符
    RTP推流及验证
    Ubuntu18.04提示wifi无法连接
    ubuntu上Android开发环境及依赖项
    HLS playlist典型示例
    ffmpeg-hls实现分析
    AOSP中的HLS协议解析
    使用ffmpeg搭建HLS直播系统
    流媒体之HLS——综述(二)
  • 原文地址:https://www.cnblogs.com/cyq1162/p/5617761.html
Copyright © 2020-2023  润新知