﻿# Membase Server 與 Enyim Memcached Client 使用心得筆記

[Membase Server](http://www.couchbase.com/products-and-services/membase-server) 是我目前看到支援 Memcached 最專業的一套產品，因此花了些時間研究他，除此之外 [Enyim Memcached Client](https://github.com/enyim/EnyimMemcached) 也是我目前看到與 [Membase Server](http://www.couchbase.com/products-and-services/membase-server) 整合度最高的 .NET 函式庫，以下是我這陣子的研究心得筆記。

**Membase Server 注意事項**

-   安裝時會取得當下的主機 IP 位址，如果事後發生 IP 異動會導致 Membase Server 的 Memcached 無法再寫入任何資料。
-   如果是自己的開發主機要安裝 Membase Server 以進行測試，建議執行以下指令重新註冊 Membase Server 的伺服器 IP 位址為 127.0.0.1，不過這樣設定的缺點是無法啟用自動複寫機制。如果是叢集架構下，就可以將 127.0.0.1 換成你的伺服器 IP 位址，或是不要加上 ns\_1@127.0.0.1 這個參數就代表程式會自動取得目前 IP 位址。 
    
    ```
    cd /D "C:\Program Files\Membase\Server\bin"
    service_stop.bat
    service_unregister.bat
    service_register.bat ns_1@127.0.0.1
    service_start.bat
    ```
    
-   啟用自動複寫機制後不能隨意將主機重開機，必須按以下程序循序重開機，以免叢集發生找不到對方主機的狀況（遇到這種狀況也會導致資料無法寫入）

1.  先透過 Node 1 的 Web Console 執行以下動作

-   移除 Node 2 (請使用 Remove 功能，而非 Fail-Over 喔)
-   執行 Rebalance 讓 Node 2 的資料先同步到 Node 1 來

3.  到 Node 2 移除 Membase Server 並重新安裝 Membase Server，Node 2 安裝好之後重新加入 Membase Cluster ( 加入 Node 1 )
4.  透過 Node 2 的 Web Console 執行以下動作

-   加入完成後必須重新執行 Rebalance 動作
-   移除 Node 1 (請使用 Remove 功能，而非 Fail-Over 喔)
-   執行 Rebalance 讓 Node 1 的資料先同步到 Node 2 來

6.  到 Node 1 移除 Membase Server 並重新安裝 Membase Server，Node 1 安裝好之後重新加入 Membase Cluster ( 加入 Node 2 )
7.  透過 Node 1 的 Web Console 執行以下動作

-   執行 Rebalance 讓 Node 2 的資料先同步到 Node 1 來

-   如何備份 Membase Server 的資料庫

-   cd /D "C:\\Program Files\\Membase\\Server\\bin\\ep\_engine\\management\\"
-   ".\\mbbackup.exe" "C:/Program Files/Membase/Server/data/ns\_1/default" "C:/%BackupFolderName%"

-   如何還原 Membase Server 的資料庫

-   cd /D "C:\\Program Files\\Membase\\Server\\bin\\ep\_engine\\management\\"
-   .\\mbrestore -a "C:/%BackupFolderName%/default" "C:/%BackupFolderName%/default-0.mb" "C:/%BackupFolderName%/default-1.mb" "C:/%BackupFolderName%/default-2.mb" "C:/%BackupFolderName%/default-3.mb"

****Enyim Memcached Client** 注意事項**

-   編譯 [memcached-providers](https://github.com/enyim/memcached-providers) 的步驟如下 (需先安裝 [msysgit](http://code.google.com/p/msysgit/downloads/list) 工具)：

1.  建立工作目錄 ( e.g.  G:\\MemcachedProvider )

1.  mkdir G:\\MemcachedProvider
2.  cd /D G:\\MemcachedProvider

3.  取得最新原始碼

1.  git clone https://github.com/enyim/memcached-providers.git
2.  cd memcached-providers\\lib
3.  git clone [https://github.com/enyim/EnyimMemcached.git](https://github.com/enyim/EnyimMemcached.git)

5.  啟動 Visual Studio 2010 進行編譯

1.  cd ..
2.  start MemcachedProviders.sln

-   透過原始碼自行編譯的組件版本永遠是 **2.4.6.0**，組件編號定義在 **VersionInfo.targets** 檔案裡。
-   透過原始碼自行編譯的組件版本會透過 **localbuild.snk** 強式名稱金鑰檔進行簽署 (可換成自己的)
-   在 EnyimMemcached 專案裡有個 build\\build.ps1 指令檔，可用來自動編譯新的組件 (改過才能用)  
    也可直接執行專案根目錄下的 **build.cmd** 批次檔

**如何使用 **Enyim Memcached Client 提供的** ASP.NET Session Provider ( .NET 3.5 )**

在 GitHub 上的 [enyim](https://github.com/enyim) 提供了好幾個子專案，其中有個 [memcached-providers](https://github.com/enyim/memcached-providers) 專案同時提供了 ASP.NET 的 Session Provider 與 OutputCache Provider，但由於 OutputCache Provider 是從 .NET 4.0 才提供的，因此若要使用在 ASP.NET 3.5 的專案就必須小做修改重新建置。

1\. 切換 MemcachedProviders 專案的目標 Framework 至 .NET Framework 3.5

2\. 刪除 Microsoft.CSharp 參考

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/46c8941058e0a7384833-image_684.png)

3\. 刪除MemcachedOutputCacheProvider.cs 檔案

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/126a2016bf484bfef843-image_685.png)

4\. 重新建置，完工！

**如何將 **Enyim Memcached Client 安裝至既有 ASP.NET 專案 (僅需修正 web.config 檔案)****

  加入以下宣告到 **<configSections>** 區段內

```
<sectionGroup name="enyim.com">
  <section name="memcached" 
           type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"/>
</sectionGroup>
<section name="membase" type="Membase.Configuration.MembaseClientSection, Membase"/>
```

  加入 enyim.com 區段（這段要給 [memcached-providers](https://github.com/enyim/memcached-providers) 的 **OutputCache Provider** 用的）

```
<enyim.com>
  <memcached protocol="Binary">
    <servers>
      <add address="127.0.0.1" port="11211"/>
    </servers>
  </memcached>
</enyim.com>
```

  加入 **membase** 區段，這段是讓 Session Provider 與未來在程式中使用 Membase API 所需的

```
<membase>
  <servers bucket="default">
    <add uri="http://127.0.0.1:8091/pools/default"/>
  </servers>
</membase>
```

如果要使用含密碼的 **vBucket** 的話，必須加入 **bucketPassword** 屬性 (**非預設的 bucket 都需要密碼**)

```
<membase>
  <servers bucket="BucketName" bucketPassword="BucketPassword">
    <add uri="http://127.0.0.1:8091/pools/default"/>
  </servers>
</membase>
```

加入 ASP.NET Session Provider 的宣告

```
<sessionState customProvider="Memcached" mode="Custom">
  <providers>
    <add name="Memcached" 
         type="Enyim.Caching.Web.MembaseSessionStateProvider, Enyim.Caching.Web"/>
  </providers>
</sessionState>
```

加入 ASP.NET OutputCache Provider 的宣告（僅限於 ASP.NET 4.0 可用）

```
<caching>
  <outputCache defaultProvider="Memcached">
    <providers>
      <add name="Memcached" 
           type="Enyim.Caching.Web.MemcachedOutputCacheProvider, Enyim.Caching.Web" />
    </providers>
  </outputCache>
</caching>
```

**加入 NLog 提供者的設定如下（加入 NLog 可以紀錄非常完整的 Memcached 的存取記錄）**

加入以下宣告到 **<configSections>** 區段內 (新增以下第 5 行那段)

```
<sectionGroup name="enyim.com">
  <section name="memcached" 
           type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"/>
  
  <section name="log" type="Enyim.Caching.Configuration.LoggerSection, Enyim.Caching" />
  
</sectionGroup>
<section name="membase" type="Membase.Configuration.MembaseClientSection, Membase"/>
```

加入 enyim.com 區段（這段要給 [memcached-providers](https://github.com/enyim/memcached-providers) 看的）(新增以下第 3 行那段)

```
<enyim.com>

  <log factory="Enyim.Caching.NLogFactory, Enyim.Caching.NLogAdapter" />

  <memcached protocol="Binary">
    <servers>
      <add address="127.0.0.1" port="11211"/>
    </servers>
  </memcached>
</enyim.com>
```

**如何在程式碼中使用 Membase API ( 只要以上 web.config 都設定好了，就可以很簡單的使用他 )**

```
var section = ConfigurationManager.GetSection("membase") as IMembaseClientConfiguration;
if (section == null) throw new InvalidOperationException("Invalid config section: " + section);

var client = new Membase.MembaseClient(section);

object data = client.Get("SomeKey");
```

如果直接使用 **var client = new Membase.MembaseClient();** 的話，會自動抓取 membase 這個區段的設定，不過並不會參考 **bucket** 與 **bucketPassword** 等額外的屬性。

**為什麼預設的 OutputCache Provider 會使用 Memcached 而非 Membase 呢？**

從原始碼來看，他預設的工廠類別使用 DefaultClientFactory 而該類別只會使用 MemcachedClient 而非 MembaseClient，也因此在會在 web.config 中有兩段 Section 的情況 ( 即 enyim.com/memcached 與 membase 兩種 )：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/55b7809aa34b7ae31efe-image_686.png)

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b69333b7f9f7da2b1411-image_687.png)

如果要改用 Membase 當 Client 的話，可以在 OutputCache 宣告的 Provider 地方加上 **factory** 屬性：

```
<caching>
  <outputCache defaultProvider="Memcached">
    <providers>
      <add name="Memcached"
           factory="Enyim.Caching.Web.MembaseClientFactory, Enyim.Caching.Web"
           type="Enyim.Caching.Web.MemcachedOutputCacheProvider, Enyim.Caching.Web" />
    </providers>
  </outputCache>
</caching>
```

**如何讓 Session Provider、OutputCache Provider 與 Membase API 各使用不同的 vBucket 呢？**

重新定義 configSections 區段，並新增三段 membase 的設定：

-   第一段為預設的 membase 設定，使用 **default** vBucket  
    **註:** 這個 membase 區段加上了 keyTransformer 設定是為了當 key 為中文或特殊字元時也能寫入
-   第二段專門給 OutputCache Provider 使用，我們將輸出快取資料寫入 **OutputCache** vBucket
-   第三段專門給 Session Provider 使用，我們將輸出快取資料寫入 **SessionState** vBucket

```
<configSections>

  <section name="membase" 
           type="Membase.Configuration.MembaseClientSection, Membase" />
  <section name="membaseOutputCache" 
           type="Membase.Configuration.MembaseClientSection, Membase" />
  <section name="membaseSession" 
           type="Membase.Configuration.MembaseClientSection, Membase" />

</configSections>
```

```
<membase>
  <servers bucket="default">
    <add uri="http://127.0.0.1:8091/pools/default" />
  </servers>  <keyTransformer type="Enyim.Caching.Memcached.Base64KeyTransformer, Enyim.Caching" />
</membase>
```

```
<membaseCache>
  <servers bucket="OutputCache" bucketPassword="OutputCache">
    <add uri="http://127.0.0.1:8091/pools/default" />
  </servers>
</membaseCache>
```

```
<membaseSession>
  <servers bucket="SessionState" bucketPassword="SessionState">
    <add uri="http://127.0.0.1:8091/pools/default" />
  </servers>
</membaseSession>
```

最後來定義 OutputCache Provider 與 Session Provider，加上 **section** 屬性即可 注意：OutputCache Provider 必須明確指定 **factory** 屬性才能搭配 Membase vBucket 運作。

```
<caching>
  <outputCache defaultProvider="Memcached">
    <providers>
      <add name="Memcached" section="membaseOutputCache"
           factory="Enyim.Caching.Web.MembaseClientFactory, Enyim.Caching.Web"
           type="Enyim.Caching.Web.MemcachedOutputCacheProvider, Enyim.Caching.Web" />
    </providers>
  </outputCache>
</caching>
```

以下是加上 Session Provider 並指定 **membaseSession** 區段的方法，加上 **section** 屬性即可

```
<sessionState customProvider="Memcached" mode="Custom">
  <providers>
    <add name="Memcached" section="membaseSession"
         type="Enyim.Caching.Web.MembaseSessionStateProvider, Enyim.Caching.Web" />
  </providers>
</sessionState>
```

最後在程式裡要叫用 MembaseClient 時傳入正確的 Section 名稱即可：

```
var section = ConfigurationManager.GetSection("membase") as IMembaseClientConfiguration;
if (section == null) throw new InvalidOperationException("Invalid config section: " + section);

var client = new Membase.MembaseClient(section);

object data = client.Get("SomeKey");
```

透過這種設定方式，就可以完全去除 <enyim.com> 這個區段的定義了！

**最後提醒**

如果要在 ASP.NET 中使用 MembaseClient 的話，建議將 MembaseClient 物件定義成一個靜態物件，好讓整個 ASP.NET 網站只會共用一個 MembaseClient 就好，MembaseClient 會幫你處理掉多執行緒下的鎖定與同步的問題。**註**：你每 new 一次 MembaseClient() 物件他會幫你建立起一個 SocketPool 喔，所以沒必要在程式裡創造那麼多 MembaseClient 物件。

```
public static MembaseClient client { get; private set; }static CacheHelper(){    var section =         ConfigurationManager.GetSection("membase") as IMembaseClientConfiguration;    if (section == null)       throw new InvalidOperationException("Invalid config section: " + section);    client = new Membase.MembaseClient(section);}
```

**相關連結**

-   [Enyim Memcached Client](https://github.com/enyim/EnyimMemcached)
-   [.NET SDK | Couchbase Techzone](https://docs.couchbase.com/dotnet-sdk/current/hello-world/start-using-sdk.html)
-   [Membase Server | Couchbase](http://www.couchbase.com/products-and-services/membase-server)
-   [enyim's Profile – GitHub](https://github.com/enyim)

-   [EnyimMemcached](https://github.com/enyim/EnyimMemcached)
-   [memcached-providers](https://github.com/enyim/memcached-providers)

-   [Enyim Memcached | Google Groups](http://groups.google.com/group/enyim-memcached) \[ 討論群組 \]
