设计一套基于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. SQL Server 作业监控

    原文:SQL Server 作业监控 在讲解SQLServer Agent Jobs之前,先要讲解msdb. Msdb是SQLServer的系统数据库之一,用于存储SQLServer的配置.元数据等信 ...

  2. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...

  3. OpenWrt配置opkg.conf

    傻人干活才累. 用openwrt有一年多了, 数据传输一直使用wget从外部的webserver取. 这样的方法我个人认为还算轻松. 可是调试的多了,写大型一点的程序,总是这样就有点麻烦了. 安装软件 ...

  4. win8 metro 调用摄像头拍摄照片并将照片保存在对应的位置

    刚刚做过这类开发,所以就先献丑了,当然所贴上的源代码都是经过验证过的,已经执行成功了,希望能够给大家一些借鉴: 以下是metro UI代码: <Page x:Class="Camera ...

  5. FPGA 设计流程,延迟,时间

    FPGA 设计流程,延迟,时间 流程:每个时钟周期可以传输的数据比特. 延迟:从输入到时钟周期的输出数据需要经验. 时间:两个元件之间的最大延迟,最高时钟速度. 1 採用流水线能够提高 流量: 比如计 ...

  6. HTML5游戏开发引擎Pixi.js完全入门手册(二)元素对象属性解析

    下面,我们来解释下PIXI里面对象的各个属性.. 首先我们来看看这个各个元素对象里面到底长啥样.. alpha Number 整个舞台对象的透明度. buttonMode Boolean 渲染是否作为 ...

  7. 开始学习java

    .net 许久 看网络java猿飞计划,感觉成了少数类,学校为它,有多难不,有着vb,C,javascript和vs基金会,这并不是说一些语法和框架的熟悉做,搞两天,以一个开发环境,myeclipse ...

  8. Android 布局学习之——Layout(布局)具体解释二(常见布局和布局參数)

     [Android布局学习系列]   1.Android 布局学习之--Layout(布局)具体解释一   2.Android 布局学习之--Layout(布局)具体解释二(常见布局和布局參数)   ...

  9. C#关于HttpClient的应用(一):获取IP所在的地理位置信息

    public class IpHttpClient:BaseHttpClient { private String appKey; private const string HOST_PATH = & ...

  10. java_log4j 经典配置

    程序加载制定日志文件 public static final String log4j = "log4j.xml"; /** * @declare 加载log4j * @throw ...