设计一套基于NHibernate二级缓存的MongoDB组件(上)

 

摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence等等,但是没有MongoDB的,于是自己扩展了一个支持MongoDB的缓存组件(NHibernate.Caches.MongoDBCache.dll)。本篇先把组件的源代码开放出来。

一、背景

在NHibernate的Contrib贡献项目官方网站(NHibernateContrib项目是由NHibernate开发团队或者终端用户根据需要自行编译并贡献的一系列的程序)中,拥有一个NHibernate.Caches的项目,里面包含汗多基于NHibernate二级缓存的组件,其中包括有:

NHibernate.Caches.MemCache:基于memcached分布式存储的缓存组件。这个大家都比较熟悉了就不多说了,详细可查阅相关信息。

NHibernate.Caches.Prevalence:基于Bamboo.Prevalence的缓存组件。它可产生一系列的缓存目录,通过缓存目录可以从文件中获取数据,并且在缓存目录中通过Snapshot,也就是快照,可以进行断点保存。详细介绍请看我的文章:(在Spring.Net中对于NHibernate.Caches.Prevalence的使用

NHibernate.Caches.SharedCache:基于MergeSystem.Indexus.WinServiceCommon、MergeSystem.Indexus.WinService和MergeSystem.Indexus.Notify的分布式存储的缓存组件。用于在动态WEB或Win应用程序中减少数据库的负责,提高访问速度。

NHibernate.Caches.SysCache:我们通常DotNet上所使用的System.Web.Caching.Cache。

NHibernate.Caches.SysCache2:同上。不同的是增加了对于SQL2005的缓存依赖的支持。

NHibernate.Caches.Velocity:基于微软推出的分布式缓存Velocity组件。跟memcached一样,“Velocity”维护一张大的哈希表,这张表可以跨越多个服务器,你可以通过添加或者减少服务器来平衡系统压力。

二、什么是MongoDB?

MongoDB是一个基于分布式文档存储的数据库。旨在为WEB应用提供可护展的高性能数据存储解决方案。它是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。 它的特点是高性能、易部署、易使用,存储数据非常方便。

MongoDB官方服务端下载地址:http://www.mongodb.org/downloads

MongoDB官方客户端(.NET)下载地址:https://github.com/samus/mongodb-csharp

三、准备工作

服务器端下载下来后,首先要安装MongoDB,大家可以参考下这篇文章:http://www.cnblogs.com/mamboer/archive/2010/03/05/1679292.html

在你开发之前必须先吧MongoDB的服务或者控制台启动。这里我采用启动控制台。

从图中看出,MongoDB采用的默认端口是27017,并且在我安装的时候,将MongoDB的数据库目录配置在:C:\data\db上。

现在开始,我要增加一个支持MongoDB的缓存组件,那么首先要先了解它们二级缓存流程的一些机制,本篇先不具体谈它的原理(会在下篇具体描述),先谈下它是如何实现的,要研究如何实现其实很简单,依葫芦画瓢,去看人家写的代码。

四、分析与实现

1. 在Spring.NET关于NHibernate的配置中,可以启用二级缓存其中有个配置节点是:

<entry key="cache.provider_class" value="NHibernate.Cache.HashtableCacheProvider"/>

HashtableCacheProvider是NHibernate二级缓存中自带的默认的缓存提供程序。而HashtableCacheProvider继承的是ICacheProvider接口,于是要创建一个支持MongoDB的缓冲提供程序,就必须继承它。

2. 创建一个MongoDBCacheProvider类:


代码

    /// <summary>
    /// MongoDB缓存提供程序
    /// </summary>
    public class MongoDBCacheProvider : ICacheProvider
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(MongoDBCacheProvider));         static MongoDBCacheProvider()
        {         }         public ICache BuildCache(string regionName, IDictionary<string, string> properties)
        {
            if (regionName == null)
            {
                regionName = string.Empty;
            }
            if (properties == null)
            {
                properties = new Dictionary<string, string>();
            }
            if (log.IsDebugEnabled)
            {             }             return new MongoDBCache(regionName, properties);
        }         public long NextTimestamp()
        {
            return Timestamper.Next();
        }         public void Start(IDictionary<string, string> properties)
        {
        }         public void Stop()
        {
        }
    }

这样就实现了一个初步的MongoDB缓存提供程序的构架。注意到BuildCache方法返回的是一个ICache对象。这里就必须实现一个继承ICache接口的MongoDB缓存对象。

3. 看下ICache都定义了哪些接口方法和属性:


代码

public interface ICache 
{      void Clear(); 
    void Destroy(); 
    object Get(object key); 
    void Lock(object key); 
    long NextTimestamp(); 
    void Put(object key, object value); 
    void Remove(object key); 
    void Unlock(object key);      string RegionName { get; } 
    int Timeout { get; } 
}

从字面上解释,应该大家都能够明白的:Clear清空缓存,Destroy和Clear类似,但是具体问题具体分析,Get取缓存,Lock锁定缓存,在ReadWrite模式的缓存上需要使用到,NextTimestamp下一时间段的时间戳,Put设置缓存,Remove清除指定的缓存数据,Unlock解除锁定,同样在ReadWrite模式的缓存上需要使用,RegionName区域名称,Timeout缓存过期时间。

4. 创建一个MongoDBCache的缓存类:

在它的构造函数中的代码:


代码

        public MongoDBCache(string regionName, IDictionary<string, string> properties)
        {
            _regionName = regionName;             if (properties != null)
            {
                string dbName = string.Empty;
                if (properties.TryGetValue("mongodb.dasebaseName", out dbName))
                {
                    if (!string.IsNullOrEmpty(dbName))
                    {
                        _dbName = dbName;
                    }
                }                 string connectionString = string.Empty;
                if (properties.TryGetValue("mongodb.connectionString", out connectionString))
                {
                    if (!string.IsNullOrEmpty(connectionString))
                    {
                        _connectionString = connectionString;
                    }
                }                 string pattern = string.Empty;
                if (properties.TryGetValue("mongodb.pattern", out pattern))
                {
                    if (!string.IsNullOrEmpty(pattern))
                    {
                        _pattern = pattern;
                    }
                }                 string regionPrefix = string.Empty;
                if (properties.TryGetValue("regionPrefix", out regionPrefix))
                {
                    if (!string.IsNullOrEmpty(regionPrefix))
                    {
                        _regionPrefix = regionPrefix;
                    }
                }
            }             mongo = new Mongo(_connectionString);             // 连接
            mongo.Connect();             // 获取Mongo数据库实体
            db = mongo[_dbName];
        }

其中可以看出这里需要连接mongo的对象,并且指定它的数据库。

而在它的析构函数中:


代码

~MongoDBCache() 

    Dispose(); 
}  /// <summary> 
/// 释放资源 
/// </summary> 
public void Dispose() 

    // 关闭连接 
    mongo.Disconnect(); 
    // 释放mongo资源 
    mongo.Dispose(); 
}

必须关闭mongo的连接,并且释放mongo资源。

对于存储缓存数据(存在Mongo数据库的表中):

设置缓存数据Put

这里会将value对象序列化为字节数组,有人会问为什么不直接存储对象呢,还需要序列化,这是由于它的存储的数据结构决定的,它最后在数据库中形成的结果为一个BSON结构;还有人会问可以把它序列化为JSON字符串吗,我也做过尝试,但是后来发现value实际上的类型是CacheItem或者CacheEntity,它们都没有无参的构造函数,所以无法反序列化。因此,这里我采用了字节转换的方式。

从代码中,可以看到document包含Key,Value,Type,Date(非必须的)的字段,其中Type在获取缓存数据(Get)的时候非常有用。

对于获取数据:

获取缓存数据Get

其中Document document = table.FindOne(query);是从表中根据指定的Document查询数据。并且对于字节数据Value字段,必须进行字节反序列化。

在Spring.NET对于NH的配置节点中可以这样子写:

代码

<!-- MongoDB缓存机制 --> 
<entry key="cache.provider_class" value="NHibernate.Caches.MongoDBCache.MongoDBCacheProvider, NHibernate.Caches.MongoDBCache" /> 
<entry key="mongodb.dasebaseName" value="xinogxt" /> 
<entry key="mongodb.connectionString" value="servers=127.0.0.1:27017" /> 
<entry key="mongodb.pattern" value="^TestWebServer\.Model\..+?"/>

其中mongodb.dasebaseName是给MongoDB配置的数据库名称;mongodb.connectionString是MongoDB服务的连接字符串;mongodb.pattern是为了作为表名称的匹配正则表达式,可以看下这段代码:


代码

/// <summary> 
/// 生成表格名称 
/// </summary> 
/// <param name="key"></param> 
private void GenerateTableName(object key) 

    if (key is CacheKey) 
    { 
        CacheKey cacheKey = (CacheKey)key;          // 判断是否匹配正则表达式 
        if (Regex.IsMatch(cacheKey.EntityOrRoleName, _pattern)) 
        { 
            _tableName = cacheKey.EntityOrRoleName.Replace(".", "_"); 
        } 
    } 
}

它是通过CacheKey的EntityOrRoleName属性,进行筛选,比如:这里的EntityOrRoleName为”“TestWebServer.Model.TblEnterprise”的字符串(这是一个NH自动生成的实体类),我给它的正则表达式为“^TestWebServer\.Model\..+?”,那么它匹配了,我就取它的这个字符串为表名称,最后的表名为:“TestWebServer_Model_TblEnterprise”。这样我缓存每一个实体,都能够自动创建相应的一个Mongo表。

5. 看下运行的结果:

测试代码如下:


[Test] 
public void EnterpriseDaoTest6() 

    IEnterpriseDao dao = (IEnterpriseDao)applicationContext.GetObject("EnterpriseDao"); 
    ITblEnterprise enterprise = dao.GetInfo(1);     … }

第一次执行:

第一次的时候,执行了数据库的SELECT的SQL语句。

我查看本地目录以及用MongoVUE客户端工具查看了下Mongo数据库:

缓存数据已经存在目录(数据库)中。

第二次执行:

发现这里没有执行SQL。

说明MongoDB缓存成功。

6. 通过对对于NHibernate二级缓存机制的理解,我们完全可以扩展属于我们自己的缓存组件。不仅仅是作为MongoDB为载体的缓存实现。

因此,在下一篇文章中,我将重点介绍关于NHibernate二级缓存机制的原理,并且继续深入探讨MongoDB缓存组件的相关原理。

NHibernate.Caches.MongoDBCache.dll项目源代码下载:NHibernate.Caches.MongoDBCache.rar

摘要: 本篇记录NLayerApp分层架构在学习中遇到的关键点。阅读全文
posted @ 2011-07-07 09:07 Leepy 阅读(1876) | 评论 (1) 编辑
 
摘要: 在我们刚开始学习架构的时候,首先会想到分层的概念,分层架构比较经典的是三层架构,那么,什么是三层架构呢?它包括表现层,业务层,数据访问层;而对于一个新手来说,从抽象意义上的三层架构,逻辑上就划分为三个层。阅读全文
posted @ 2011-05-11 16:32 Leepy 阅读(21087) | 评论 (174) 编辑
 
摘要: NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence等等,但是没有MongoDB的,于是自己扩展了一个支持MongoDB的缓存组件(NHibernate.Caches.MongoDBCache.dll)。阅读全文
posted @ 2010-12-30 22:35 Leepy 阅读(2676) | 评论 (10) 编辑
 
 
 
 
 

基于NHibernate二级缓存的MongoDB组件的更多相关文章

  1. [Nhibernate]二级缓存(一)

    目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象 ...

  2. [Nhibernate]二级缓存

    [Nhibernate]二级缓存 目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级 ...

  3. [Nhibernate]二级缓存(二)

    目录 写在前面 文档与系列文章 更新数据 二级缓存管理 总结 写在前面 本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bu ...

  4. NHibernate二级缓存(第十一篇)

    NHibernate二级缓存(第十一篇) 一.NHibernate二级缓存简介 NHibernate由ISessionFactory创建,可以被所有的ISession共享. 注意NHibernate查 ...

  5. 01-08-05【Nhibernate (版本3.3.1.4000) 出入江湖】NHibernate二级缓存:第三方MemCache缓存

    一.准备工作 [1]根据操作系统(位数)选择下载相应版本的MemCache, MemCache的下载和安装,参看: http://www.cnblogs.com/easy5weikai/p/37606 ...

  6. NHibernate教程(20)——二级缓存(上)

    本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession ...

  7. NHibernate系列文章九:NHibernate对象二级缓存上

    摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...

  8. NHibernate系列文章十:NHibernate对象二级缓存下

    摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...

  9. 01-08-03【Nhibernate (版本3.3.1.4000) 出入江湖】二级缓存:NHibernate自带的HashtableProvider之缓存管理

    http://www.cnblogs.com/lyj/archive/2008/11/28/1343418.html 管理NHibernate二级缓存 NHibernate二级缓存由ISessionF ...

随机推荐

  1. RandomAccessFile实时读取大文件(转)

    最近有一个银行数据漂白系统,要求操作人员在页面调用远端Linux服务器的shell,并将shell输出的信息保存到一个日志文件,前台页面要实时显示日志文件的内容.这个问题难点在于如何判断哪些数据是新增 ...

  2. Mesos-error

    1,configure: error: cannot find libcurl 解决 yum install  curl-devel 版权声明:本文博客原创文章,博客,未经同意,不得转载.

  3. Hibernate的一些相关信息

    在没有学习Hibernate之前,我们一直都是用jdbc来连接数据库和操纵数据库.所以在刚接触Hibernate时,我们都有一个疑问,为什么要学Hibernate,jdbc不是挺好的吗?那么接下来就来 ...

  4. USACO dualpal

    /* ID:kevin_s1 PROG:dualpal LANG:C++ */ #include <iostream> #include <cstdio> #include & ...

  5. oracle PL/SQL(procedure language/SQL)程序设计之函数+过程+包(转)

    匿名PL/SQL块回顾 DECLARE (可选)     定义在PL/SQL块中要使用的对象 BEGIN (必须)     执行语句 EXCEPTION (可选)     错误处理语句 END; (必 ...

  6. hdu 5060 War

    War Time Limit: 8000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

  7. POJ 2352 &amp;&amp; HDU 1541 Stars (树状数组)

    一開始想,总感觉是DP,但是最后什么都没想到.还暴力的交了一发. 然后開始写线段树,结果超时.感觉自己线段树的写法有问题.改天再写.先把树状数组的写法贴出来吧. ~~~~~~~~~~~~~~~~~~~ ...

  8. GPS转换为百度坐标

    原文地址:http://www.cnblogs.com/zhaohuionly/archive/2013/06/18/3142623.html 最近在做一个关于手机定位的小应用,需求是这样的,用户通过 ...

  9. php_公共方法01_传入数组_打印可见信息

    function decodeUnicode($str) { return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'convert', $str ...

  10. ReactJs入门思路

    ReactJs入门思路小指南 原文  http://segmentfault.com/blog/fakefish/1190000002449277 React是怎么搞的? React中,把一切东西都看 ...