• 关于系统架构中数据缓存的设计
  • EVAN 发表于 2017/3/4 21:45:00 | 分类标签: 架构设计 缓存设计
  •  数据访问层,提供整个项目的数据访问与持久化功能。在分层系统中所有有关数据访问、检索、持久化的任务,最终都将在这一层完成。

    来看一个比较经典的数据访问层结构图

    大概可以看出如下信息

    1、有缓存、日志、异常处理、数据CRUD、查询及数据事务等功能

    2、无缝对接如EF、ADO.NET、NH、Dapper等数据访问技术

    3、对外只开放接口层,隐藏具体实现,这样就可以解耦业务层与数据访问层

     

    今天斗胆通过一个简单实例来实践一下,如有不妥的地方,欢迎指正

    创建接口层,定义可以提供的一些服务接口

     

    这里我们一个有5种服务接口,方法的功能就不介绍了,应该都能看懂

    缓存接口:ICache.cs

    1     public interface ICache<T> where T : class
    2     {
    3         IEnumerable<T> Gets(string key);
    4         T Get(string key);
    5         bool Sets(string key, IEnumerable<T> value, TimeSpan expiresIn);
    6         bool Set(string key, T value, TimeSpan expiresIn);
    7         bool Remove(string key);
    8     }

    缓存服务的实现

    因为可能支持多种缓存,所以我实现了Web缓存与Redis缓存,这2中缓存分别在项目初期和后期集群中可能会用到

     

    我们来看HttpRuntimeCache.cs (还有一种Web缓存HttpContext.Cache,不够这种只能在Web应用使用,所以一般不推荐)

     1 public class HttpRuntimeCache<T> : ICache<T> where T : class
     2     {
     3         public HttpRuntimeCache()
     4         {
     5 
     6         }
     7 
     8         public T Get(string key)
     9         {
    10             if (System.Web.HttpRuntime.Cache[key] == null)
    11             {
    12                 return default(T);
    13             }
    14 
    15             return System.Web.HttpRuntime.Cache[key] as T;
    16         }
    17 
    18         public bool Set(string key, T value, TimeSpan expiresIn)
    19         {
    20             Set(key, value, expiresIn.Seconds);
    21             return true;
    22         }
    23 
    24         public bool Remove(string key)
    25         {
    26             System.Web.HttpRuntime.Cache.Remove(key);
    27             return true;
    28         }
    29 
    30         private void Set(string key, object value, int absoluteSeconds)
    31         {
    32             System.Web.HttpRuntime.Cache.Insert(key, value, null, DateTime.Now.AddSeconds(absoluteSeconds), TimeSpan.FromSeconds(0));
    33         }
    34     }

    现在缓存功能已经实现了;大家应该很容易想到怎麼使用了,比如在业务层这样使用

    1 ICache<User>  cache = new HttpRuntimeCache<User>();
    2 var user = cache.Get("key");

    其实这样是不对的,因为这样的话接口ICache相当于没什么用处,没有起到应有的作用(隔离具体实现)

    如果要换另一种缓存实现(比如redis),那还要在所有使用了 new HttpRuntimeCache<User>() 的地方改正过来

    这样的耦合要去掉;有2种方式,通过IOC在实例化的时候依赖注入;另一种就是新建一个基础设施层,业务层依赖于这一层

    因为业务层肯定是需要调用一些Utilities、Helper等类型的工具类,这个应该是躲不掉的,再怎么接口隔离也去除不了这点

    基础设施层的实现

    Cache.cs 

     1     public sealed class Cache<T> where T : class
     2     {
     3         private readonly static ICache<T> cacheProvider;
     4 
     5         static Cache()
     6         {
     7             cacheProvider = ProviderHelper<T>.GetCacheProvider();
     8         }
     9 
    10         public static IEnumerable<T> Gets(string key)
    11         {
    12             return cacheProvider.Gets(key);
    13         }
    14 
    15         public static T Get(string key)
    16         {
    17             return cacheProvider.Get(key);
    18         }
    19 
    20         public static bool Sets(string key, IEnumerable<T> value, TimeSpan expiresIn)
    21         {
    22             return cacheProvider.Sets(key, value, expiresIn);
    23         }
    24 
    25         public static bool Set(string key, T value, TimeSpan expiresIn)
    26         {
    27             return cacheProvider.Set(key, value, expiresIn);
    28         }
    29 
    30         public static bool Remove(string key)
    31         {
    32             return cacheProvider.Remove(key);
    33         }
    34     }

     ProviderHelper.cs 实现如下图

    至此,缓存功能实现完毕,我们新建一个测试项目看看结果

     1     [TestClass]
     2     public class CacheTest
     3     {
     4         [TestMethod]
     5         public void Set()
     6         {
     7             var user = new LoginUser()
     8             {
     9                 Id = Guid.NewGuid(), 
    10                 LoginName = "LoginName",
    11                 IsEnabled = 1,
    12                 Password = "mima1987",
    13                 CreateTime = DateTime.Now
    14             };
    15 
    16             Cache<LoginUser>.Set("UnitTest3.TestMethod1", user, TimeSpan.FromSeconds(10));
    17             var user2 = Cache<LoginUser>.Get("UnitTest3.TestMethod1");
    18 
    19             Assert.AreEqual(user.Id, user2.Id);
    20         }
    21     }


    看来没有什么问题。

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:EVAN
  • 加入时间:2014/6/1 14:45:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮