日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2025/3/17 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设计一套基于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 ????????public?void?Put(object?key,?object?value)
????????{
????????????
if?(key?==?null)
????????????{
????????????????
throw?new?ArgumentNullException("key",?"null?key?not?allowed");
????????????}
????????????
if?(value?==?null)
????????????{
????????????????
throw?new?ArgumentNullException("value",?"null?value?not?allowed");
????????????}
????????????
if?(log.IsDebugEnabled)
????????????{
????????????????log.DebugFormat(
"setting?value?for?item?{0}",?key);
????????????}

????????????
string?hashKey?=?GetAlternateKeyHash(key);

????????????GenerateTableName(key);

????????????Console.WriteLine(
string.Format("Put------Key:{0},?Value:{1}",?hashKey,?value.ToString()));

????????????IMongoCollection
<Document>?table?=?db.GetCollection<Document>(TableName);

????????????IDictionary
<string,?object>?dict?=?new?Dictionary<string,?object>();
????????????dict.Add(
"Key",?hashKey);

????????????Document?query?
=?new?Document(dict);

????????????
//?查詢
????????????Document?document?=?table.FindOne(query);

????????????
try
????????????{
????????????????
if?(document?==?null)
????????????????{
????????????????????IDictionary
<string,?object>?newDict?=?new?Dictionary<string,?object>();
????????????????????newDict.Add(
"Value",?SerializeHelper.BinarySerialize(value));
????????????????????newDict.Add(
"Key",?hashKey);
????????????????????newDict.Add(
"Type",?value.GetType().Name);
????????????????????newDict.Add(
"Date",?DateTime.Now.ToString());

????????????????????document?
=?new?Document(newDict);
????????????????}
????????????????
else
????????????????{
????????????????????document[
"Value"]?=?SerializeHelper.BinarySerialize(value);
????????????????????document[
"Type"]?=?value.GetType().Name;
????????????????????document[
"Date"]?=?DateTime.Now.ToString();
????????????????}

????????????????
//?保存Document
????????????????table.Save(document);
????????????}
????????????
catch
????????????{
????????????}
????????????
finally
????????????{
????????????}
????????}

這里會將value對象序列化為字節數組,有人會問為什么不直接存儲對象呢,還需要序列化,這是由于它的存儲的數據結構決定的,它最后在數據庫中形成的結果為一個BSON結構;還有人會問可以把它序列化為JSON字符串嗎,我也做過嘗試,但是后來發現value實際上的類型是CacheItem或者CacheEntity,它們都沒有無參的構造函數,所以無法反序列化。因此,這里我采用了字節轉換的方式。

從代碼中,可以看到document包含Key,Value,Type,Date(非必須的)的字段,其中Type在獲取緩存數據(Get)的時候非常有用。

對于獲取數據:

獲取緩存數據Get ????????public?object?Get(object?key)
????????{
????????????
string?hashKey?=?GetAlternateKeyHash(key);

????????????GenerateTableName(key);

????????????Console.WriteLine(
string.Format("Get------Key:{0}",?hashKey));

????????????IMongoCollection
<Document>?table?=?db.GetCollection<Document>(TableName);

????????????IDictionary
<string,?object>?dict?=?new?Dictionary<string,?object>();
????????????dict.Add(
"Key",?hashKey);

????????????Document?query?
=?new?Document(dict);

????????????
//?查詢
????????????Document?document?=?table.FindOne(query);

????????????
if?(document?!=?null)
????????????{
????????????????
try
????????????????{
????????????????????
byte[]??bytes?=?((MongoDB.Binary)document["Value"]).Bytes;

????????????????????
#region?反序列化字節數組

????????????????????
if?(string.Equals(document["Type"].ToString(),?typeof(CacheEntry).Name,?StringComparison.InvariantCultureIgnoreCase))
????????????????????{
????????????????????????
return?SerializeHelper.BinaryDeSerialize<CacheEntry>(bytes);
????????????????????}
????????????????????
else?if?(string.Equals(document["Type"].ToString(),?typeof(CachedItem).Name,?StringComparison.InvariantCultureIgnoreCase))
????????????????????{
????????????????????????
return?SerializeHelper.BinaryDeSerialize<CachedItem>(bytes);
????????????????????}
????????????????????
else?if?(string.Equals(document["Type"].ToString(),?typeof(List<Object>).Name,?StringComparison.InvariantCultureIgnoreCase))
????????????????????{
????????????????????????
return?SerializeHelper.BinaryDeSerialize<List<Object>>(bytes);
????????????????????}
????????????????????
else?if?(string.Equals(document["Type"].ToString(),?typeof(Int64).Name,?StringComparison.InvariantCultureIgnoreCase))
????????????????????{
????????????????????????
return?SerializeHelper.BinaryDeSerialize<Int64>(bytes);
????????????????????}
????????????????????
else?if?(string.Equals(document["Type"].ToString(),?typeof(CacheLock).Name,?StringComparison.InvariantCultureIgnoreCase))
????????????????????{
????????????????????????
return?SerializeHelper.BinaryDeSerialize<CacheLock>(bytes);
????????????????????}
????????????????????
else
????????????????????{
????????????????????????
return?null;
????????????????????}

????????????????????
#endregion
????????????????}
????????????????
catch
????????????????{
????????????????????
return?null;
????????????????}
????????????}
????????????
return?null;
????????}

其中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

總結

以上是生活随笔為你收集整理的设计一套基于NHibernate二级缓存的MongoDB组件(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。