分布式緩存系統(tǒng)Memcached實踐

Memcached簡介

Memcached 是一個高性能的分布式內(nèi)存對象緩存系統(tǒng)郊霎,用于動態(tài)Web應(yīng)用以減輕數(shù)據(jù)庫負(fù)載。它通過在內(nèi)存中緩存數(shù)據(jù)和對象來減少讀取數(shù)據(jù)庫的次數(shù)渠抹,從而提高動態(tài)慨代、數(shù)據(jù)庫驅(qū)動網(wǎng)站的速度疚鲤。Memcached基于一個存儲鍵/值對的hashmap。其守護(hù)進(jìn)程(daemon )是用C寫的才沧,但是客戶端可以用任何語言來編寫迈喉,并通過memcached協(xié)議與守護(hù)進(jìn)程通信。天生支持集群糜工。
目前有多種平臺的Memcached版本弊添,比如Linux、FreeBSD捌木、Solaris 油坝、Mac OS X及Windows平臺。
官方網(wǎng)站:http://memcached.org/
我們安裝Windows版本來演示刨裆。
32bit:下載 memcached-win32-1.4.4-14.zip
64bit:如果需要win64版澈圈,下載 memcached-win64-1.4.4-14.zip

Memcached 內(nèi)存管理機(jī)制
Menceched 通過預(yù)分配指定的內(nèi)存空間來存取數(shù)據(jù),所有的數(shù)據(jù)都保存在 memcached 內(nèi)置的內(nèi)存中帆啃。利用 Slab Allocation 機(jī)制來分配和管理內(nèi)存瞬女。按照預(yù)先規(guī)定的大小,將分配的內(nèi)存分割成特定長度的內(nèi)存塊努潘,再把尺寸相同的內(nèi)存塊分成組诽偷,這些內(nèi)存塊不會釋放,可以重復(fù)利用疯坤。當(dāng)存入的數(shù)據(jù)占滿內(nèi)存空間時报慕,Memcached 使用 LRU 算法自動刪除不是用的緩存數(shù)據(jù),即重用過期數(shù)據(jù)的內(nèi)存空間压怠。Memcached 是為緩存系統(tǒng)設(shè)計的眠冈,因此沒有考慮數(shù)據(jù)的容災(zāi)問題,和機(jī)器的內(nèi)存一樣菌瘫,重啟機(jī)器將會丟失蜗顽,如果希望服務(wù)重啟數(shù)據(jù)依然能保留,那么就需要 sina 網(wǎng)開發(fā)的 Memcachedb 持久性內(nèi)存緩沖系統(tǒng)雨让,當(dāng)然還有常見的 NOSQL 服務(wù)如 redis雇盖。默認(rèn)監(jiān)聽端口:11211

Memcached數(shù)據(jù)訪問模型

  • 添加新的鍵值對數(shù)據(jù)


    從圖中可以看出,Memcached雖然稱為“分布式”緩存服務(wù)器宫患,但服務(wù)器端并沒有“分布式”功能刊懈,而是完全由客戶端程序庫實現(xiàn)的这弧。服務(wù)端之間沒有任何聯(lián)系,數(shù)據(jù)存取都是通過客戶端的算法實現(xiàn)的虚汛。當(dāng)客戶端要存取數(shù)據(jù)時匾浪,首先會通過算法查找自己維護(hù)的服務(wù)器哈希列表,找到對應(yīng)的服務(wù)器后卷哩,再將數(shù)據(jù)存往指定服務(wù)器蛋辈。例如:上圖中應(yīng)用程序要新增一個<'tokyo',data>的鍵值對,它同過set操作提交給Memcached客戶端将谊,客戶端通過一定的哈希算法(比如:一般的求余函數(shù)或者強(qiáng)大的一致性Hash算法)從服務(wù)器列表中計算出一個要存儲的服務(wù)器地址冷溶,最后將該鍵值對存儲到計算出來的服務(wù)器里邊。

  • 獲取已存在的鍵值對數(shù)據(jù)


    上圖中應(yīng)用程序想要獲取Key為‘tokyo’的Value尊浓,于是它向Memcached客戶端提交了一個Get請求逞频,Memcached客戶端還是通過算法從服務(wù)器列表查詢哪臺服務(wù)器存有Key為‘tokyo’的Value(即選擇剛剛Set到了哪臺服務(wù)器),如果查到栋齿,則向查到的服務(wù)器請求返回Key為‘tokyo’的數(shù)據(jù)苗胀。

Memcached分布式的核心—一致性Hash算法

一致性Hash算法是分布式緩存的核心理論,我也學(xué)習(xí)得不深入瓦堵,也只是剛剛了解了一下基协,后面我有空深入學(xué)習(xí)一下。
  首先菇用,簡單的路由算法(通過使用余數(shù)Hash)無法滿足業(yè)務(wù)發(fā)展時服務(wù)器擴(kuò)容的需要:緩存命中率下降澜驮。例如:當(dāng)3臺服務(wù)器擴(kuò)容至4臺時,采用普通的余數(shù)Hash算法會導(dǎo)致大約75%(3/4)被緩存了的數(shù)據(jù)無法正確命中惋鸥,隨著服務(wù)器集群規(guī)模的增大杂穷,這個比例會線性地上升。那么卦绣,可以想象亭畜,當(dāng)100臺服務(wù)器的集群中加入一臺服務(wù)器,不能命中的概率大概是99%(N/N+1)迎卤,這個結(jié)果顯然是無法接受的。那么玷坠,能否通過改進(jìn)路由算法蜗搔,使得新加入的服務(wù)器不影響大部分緩存數(shù)據(jù)的正確性呢?請看下面的一致性Hash算法八堡。
  一致性Hash算法通過一個叫做一致性Hash環(huán)的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)KEY到緩存服務(wù)器的Hash映射樟凄,如下圖所示:

具體算法過程是

  1. 先構(gòu)造一個長度為0~2^32(2的32次冪)個的整數(shù)環(huán)(又稱:一致性Hash環(huán)),根據(jù)節(jié)點名稱的Hash值將緩存服務(wù)器節(jié)點放置在這個Hash環(huán)中兄渺,如上圖中的node1缝龄,node2等;
  2. 根據(jù)需要緩存的數(shù)據(jù)的KEY值計算得到其Hash值,如上圖中右半部分的“鍵”叔壤,計算其Hash值后離node2很近瞎饲;
  3. 在Hash環(huán)上順時針查找距離這個KEY的Hash值最近的緩存服務(wù)器節(jié)點,完成KEY到服務(wù)器的Hash映射查找炼绘,如上圖中離右邊這個鍵的Hash值最近的順時針方向的服務(wù)器節(jié)點是node2嗅战,因此這個KEY會到node2中讀取數(shù)據(jù);
      當(dāng)緩存服務(wù)器集群需要擴(kuò)容的時候俺亮,只需要將新加入的節(jié)點名稱(如node5)的Hash值放入一致性Hash環(huán)中驮捍,由于KEY總是順時針查找距離其最近的節(jié)點,因此新加入的節(jié)點只影響整個環(huán)中的一部分脚曾。如下圖中所示东且,添加node5后,只影響右邊逆時針方向的三個Key/Value對數(shù)據(jù)本讥,只占整個Hash環(huán)中的一小部分珊泳。


因此,我們可以與之前的普通余數(shù)Hash作對比:采用一致性Hash算法時囤踩,當(dāng)3臺服務(wù)器擴(kuò)容到4臺時旨椒,可以繼續(xù)命中原有緩存數(shù)據(jù)的概率為75%,遠(yuǎn)高于普通余數(shù)Hash的25%堵漱,而且隨著集群規(guī)模越大综慎,繼續(xù)命中原有緩存數(shù)據(jù)的概率也會隨之增大。當(dāng)100臺服務(wù)器增加1臺時勤庐,繼續(xù)命中的概率是99%示惊。雖然,仍有小部分?jǐn)?shù)據(jù)緩存在服務(wù)器中無法被讀取到愉镰,但是這個比例足夠小米罚,通過訪問數(shù)據(jù)庫也不會對數(shù)據(jù)庫造成致命的負(fù)載壓力

Memcached與Redis的對比

  • 沒有必要過多的關(guān)心性能丈探,因為二者的性能都已經(jīng)足夠高了录择。由于Redis只使用單核,而Memcached可以使用多核碗降,所以在比較上隘竭,平均每一個核上Redis在存儲小數(shù)據(jù)時比Memcached性能更高。而在100k以上的數(shù)據(jù)中讼渊,Memcached性能要高于Redis动看,雖然Redis最近也在存儲大數(shù)據(jù)的性能上進(jìn)行優(yōu)化,但是比起Memcached爪幻,還是稍有遜色菱皆。說了這么多须误,結(jié)論是,無論你使用哪一個仇轻,每秒處理請求的次數(shù)都不會成為瓶頸京痢。(比如瓶頸可能會在網(wǎng)卡)
  • 如果要說內(nèi)存使用效率,使用簡單的key-value存儲的話拯田,Memcached的內(nèi)存利用率更高历造,而如果Redis采用hash結(jié)構(gòu)來做key-value存儲,由于其組合式的壓縮船庇,其內(nèi)存利用率會高于Memcached吭产。當(dāng)然,這和你的應(yīng)用場景和數(shù)據(jù)特性有關(guān)鸭轮。
  • 如果你對數(shù)據(jù)持久化和數(shù)據(jù)同步有所要求臣淤,那么推薦你選擇Redis,因為這兩個特性Memcached都不具備窃爷。即使你只是希望在升級或者重啟系統(tǒng)后緩存數(shù)據(jù)不會丟失邑蒋,選擇Redis也是明智的。

因此按厘,我們可以得出一個結(jié)論:在簡單的Key/Value應(yīng)用場景(例如緩存)医吊,Memcached擁有更高的讀寫性能;而在數(shù)據(jù)持久化和數(shù)據(jù)同步場景逮京,Redis擁有更加強(qiáng)大的功能和更為豐富的數(shù)據(jù)類型卿堂;

需要慎重考慮的部分

  • Memcached單個key-value大小有限,一個value最大只支持1MB懒棉,而Redis最大支持512MB
  • Memcached只是個內(nèi)存緩存草描,對可靠性無要求;而Redis更傾向于內(nèi)存數(shù)據(jù)庫策严,因此對對可靠性方面要求比較高
  • 從本質(zhì)上講穗慕,Memcached只是一個單一key-value內(nèi)存Cache;而Redis則是一個數(shù)據(jù)結(jié)構(gòu)內(nèi)存數(shù)據(jù)庫妻导,支持五種數(shù)據(jù)類型逛绵,因此Redis除單純緩存作用外,還可以處理一些簡單的邏輯運(yùn)算倔韭,Redis不僅可以緩存暑脆,而且還可以作為數(shù)據(jù)庫用
  • 新版本(3.0)的Redis是指集群分布式,也就是說集群本身均衡客戶端請求狐肢,各個節(jié)點可以交流,可拓展行迹辐、可維護(hù)性更強(qiáng)大哟旗。
  • MongoDB不支持事務(wù)。

安裝和啟動

  • install

memcached.exe -d install

  • start

memcached.exe -d start

  • stop

memcached.exe -d stop

  • unInstall

memcached.exe -d uninstall

以上的安裝和啟動都是在默認(rèn)環(huán)境下進(jìn)行的坐慰,在安裝時可設(shè)置如下參數(shù):

Memcached默認(rèn)使用端口是11211
默認(rèn)最大連接數(shù)是1024個
默認(rèn)最大使用內(nèi)存是64M
默認(rèn)每個鍵值對僵腺,值存儲空間為1M

-p 監(jiān)聽的端口
-l 連接的IP地址, 默認(rèn)是本機(jī)
-d start 啟動memcached服務(wù)
-d restart 重起memcached服務(wù)
-d stop|shutdown 關(guān)閉正在運(yùn)行的memcached服務(wù)
-d install 安裝memcached服務(wù)
-d uninstall 卸載memcached服務(wù)
-u 以身份運(yùn)行 (僅在以root運(yùn)行的時候有效)
-m 最大內(nèi)存使用鲤孵,單位MB。默認(rèn)64MB **
-M 內(nèi)存耗盡時返回錯誤辰如,而不是刪除項
-c 最大同時連接數(shù)普监,
默認(rèn)是1024 **
-f 塊大小增長因子,默認(rèn)是1.25
-n 最小分配空間琉兜,key+value+flags默認(rèn)是48
-h 顯示幫助

OK,命令咱們已經(jīng)執(zhí)行完了.怎么才知道Memcached服務(wù)已經(jīng)安裝成功并啟動了呢?
cmd 命令 services.msc

檢測Memcached服務(wù)是否成功啟動:

使用telnet命令連接到登錄臺:telnet 服務(wù)器IP地址 11211(11211是默認(rèn)的Memcached服務(wù)端口號)
打印當(dāng)前Memcache服務(wù)器狀態(tài):stats

stats.png

可以看到凯正,通過stats命令列出了一系列的Memcached服務(wù)狀態(tài)信息,那么這些信息又代表什么意思呢豌蟋?我們可以通過下圖來知道:

先把memcached用起來

下載客戶端的4個dll廊散,
ICSharpCode.SharpZipLib.dll,log4net.dll梧疲,Memcached.ClientLibrary.dll允睹,Commons.dll
跟著我新建一個簡單控制臺應(yīng)用程序

using Memcached.ClientLibrary;
using System;

namespace Tdf.RedisCacheTest
{
    class AMemcached
    {
        public static MemcachedClient cache;
        static AMemcached()
        {
            string[] servers = { "127.0.0.1:11211" };
            // 初始化池
            SockIOPool pool = SockIOPool.GetInstance();
            // 設(shè)置服務(wù)器列表
            pool.SetServers(servers);
            // 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
            pool.SetWeights(new int[] { 1 });
            // 初始化時創(chuàng)建連接數(shù)
            pool.InitConnections = 3;
            // 最小連接數(shù)
            pool.MinConnections = 3;
            // 最大連接數(shù)
            pool.MaxConnections = 5;
            // 連接的最大空閑時間,下面設(shè)置為6個小時(單位ms)幌氮,超過這個設(shè)置時間缭受,連接會被釋放掉
            pool.MaxIdle = 1000 * 60 * 60 * 6;
            // socket連接的超時時間,下面設(shè)置表示不超時(單位ms)该互,即一直保持鏈接狀態(tài)
            pool.SocketConnectTimeout = 0;
            // 通訊的超時時間米者,下面設(shè)置為3秒(單位ms),.Net版本沒有實現(xiàn)
            pool.SocketTimeout = 1000 * 3;
            // 維護(hù)線程的間隔激活時間慢洋,下面設(shè)置為30秒(單位s)塘雳,設(shè)置為0時表示不啟用維護(hù)線程
            pool.MaintenanceSleep = 30;
            // 設(shè)置SocktIO池的故障標(biāo)志
            pool.Failover = true;
            // 是否對TCP/IP通訊使用nalgle算法,.net版本沒有實現(xiàn)
            pool.Nagle = false;
            // socket單次任務(wù)的最大時間(單位ms)普筹,超過這個時間socket會被強(qiáng)行中端掉败明,當(dāng)前任務(wù)失敗。
            pool.MaxBusy = 1000 * 10;
            pool.Initialize();
            cache = new MemcachedClient();
            // 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮太防,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
            cache.EnableCompression = false;
            // 壓縮設(shè)置妻顶,超過指定大小的都壓縮 
            cache.CompressionThreshold = 1024 * 1024;           
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 存入key為userName,value為Bobby的一個緩存
            AMemcached.cache.Add("userName", "Bobby");
            // 讀出key為a的緩存值
            var s = AMemcached.cache.Get("userName");
            // 輸出
            Console.WriteLine(s);
            Console.Read();

        }
    }
}

細(xì)品 .NET Memcached.ClientLibrary

說說memcached分布式緩存的設(shè)置與應(yīng)用

string[] servers = { "127.0.0.1:11211", "192.168.2.100:11211" };
// 初始化池
SockIOPool pool = SockIOPool.GetInstance();
// 設(shè)置服務(wù)器列表
pool.SetServers(servers);
// 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
pool.SetWeights(new int[] { 1, 10 });

Note:

  • 在172.18.5.66蜒车,與192.168.10.121兩臺機(jī)器上裝memcached服務(wù)端讳嘱。
  • pool.SetWeights這里的1跟10意思是,負(fù)載均衡比例酿愧,假如11000條數(shù)據(jù)沥潭,大致數(shù)據(jù)分布為:172.18.5.66分布1000條數(shù)據(jù)左右。另外一臺為10000條左右嬉挡。
  • memcached服務(wù)端并不具備負(fù)載均衡的能力钝鸽,而是memcachedClient實現(xiàn)的汇恤,具體存取數(shù)據(jù)實現(xiàn)的核心是采用一致性Hash算法,把key-value分布到某一臺服務(wù)器中里邊拔恰。

說說memcached的數(shù)據(jù)壓縮機(jī)制

// 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮因谎,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
cache.EnableCompression = false;
// 壓縮設(shè)置,超過指定大小的都壓縮 
cache.CompressionThreshold = 1024 * 1024;

Note:

  • 這個處理是在MemcachedClient對象中颜懊,設(shè)置這個EnableCompression屬性财岔,是否使用壓縮的意思,如果啟用啦壓縮功能 ,則ICSharpCode.SharpZipLib類庫會在數(shù)據(jù)超過預(yù)設(shè)大小時河爹,進(jìn)行數(shù)據(jù)壓縮處理匠璧。
  • CompressionThreshold這個屬性是壓縮的閥值,默認(rèn)是15K昌抠,如果超過設(shè)定的閥值則使用memcached的通訊協(xié)議患朱,存數(shù)據(jù)時給每個數(shù)據(jù)項分配一個16為的flag表示,用作記錄是否有壓縮炊苫,如果有壓縮則提取數(shù)據(jù)是進(jìn)行解壓裁厅。如果沒有超過閥值則不壓縮,直接存儲侨艾。

說說怎么使用客戶端多個SocketIO池

using Memcached.ClientLibrary;
using System;

namespace Tdf.RedisCacheTest
{
    class AMemcached
    {
        public MemcachedClient cache;
        public AMemcached(string poolName)
        {
            string[] servers = { "127.0.0.1:11211", "192.168.2.100:11211" };
            // 初始化池
            SockIOPool pool = SockIOPool.GetInstance();
            // 設(shè)置服務(wù)器列表
            pool.SetServers(servers);
            // 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
            pool.SetWeights(new int[] { 1, 10 });
            // 初始化時創(chuàng)建連接數(shù)
            pool.InitConnections = 3;
            // 最小連接數(shù)
            pool.MinConnections = 3;
            // 最大連接數(shù)
            pool.MaxConnections = 5;
            // 連接的最大空閑時間执虹,下面設(shè)置為6個小時(單位ms),超過這個設(shè)置時間唠梨,連接會被釋放掉
            pool.MaxIdle = 1000 * 60 * 60 * 6;
            // socket連接的超時時間袋励,下面設(shè)置表示不超時(單位ms),即一直保持鏈接狀態(tài)
            pool.SocketConnectTimeout = 0;
            // 通訊的超時時間当叭,下面設(shè)置為3秒(單位ms)茬故,.Net版本沒有實現(xiàn)
            pool.SocketTimeout = 1000 * 3;
            // 維護(hù)線程的間隔激活時間,下面設(shè)置為30秒(單位s)蚁鳖,設(shè)置為0時表示不啟用維護(hù)線程
            pool.MaintenanceSleep = 30;
            // 設(shè)置SocktIO池的故障標(biāo)志
            pool.Failover = true;
            // 是否對TCP/IP通訊使用nalgle算法磺芭,.net版本沒有實現(xiàn)
            pool.Nagle = false;
            // socket單次任務(wù)的最大時間(單位ms),超過這個時間socket會被強(qiáng)行中端掉醉箕,當(dāng)前任務(wù)失敗钾腺。
            pool.MaxBusy = 1000 * 10;
            pool.Initialize();
            cache = new MemcachedClient();
            // 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
            cache.EnableCompression = false;
            // 壓縮設(shè)置讥裤,超過指定大小的都壓縮 
            cache.CompressionThreshold = 1024 * 1024;           
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 存入key為userName放棒,value為Bobby的一個緩存
            new AMemcached("poolName").cache.Add("b", 123);
            // AMemcached.cache.Add("userName", "Bobby");
            // 讀出key為a的緩存值
            var s = new AMemcached("poolName").cache.Get("userName");
            // 輸出
            Console.WriteLine(s);
            Console.Read();

        }
    }
}

說說memcached的故障轉(zhuǎn)移處理

// 設(shè)置SocktIO池的故障標(biāo)志
pool.Failover = true;

Note:memcached的鼓掌轉(zhuǎn)移是一套正常節(jié)點發(fā)生故障變?yōu)樗拦?jié)點時的處理機(jī)制。

  • 開啟故障轉(zhuǎn)移:如果發(fā)生socket異常己英,則該節(jié)點被添加到存放死節(jié)點屬性的_hostDead中间螟,新請求被映射到dead server,檢測嘗試連接死節(jié)點的時間間隔屬性_hostDeadDuration(默認(rèn)設(shè)置為100ms),如果沒有達(dá)到設(shè)定的間隔時間則key會被映射到可用的server處理寒亥,如果達(dá)到了時間間隔邮府,則嘗試重新鏈接,連接成功將此節(jié)點從_hostDead中去除溉奕,連接失敗則間隔時間翻倍存放,下次重新連接時間會被拉長忍啤。
  • 不開啟故障轉(zhuǎn)移:新的請求都會被映射到dead server上加勤,嘗試重新建立socket鏈接,如果連接失敗同波,返回null或者操作失敗鳄梅。

說說key-value中的key與value

  • key在服務(wù)端的長度限制為250個字符,建議使用較短的key但不要重復(fù)未檩。
  • value的大小限制為1mb戴尸,如果大拉,可以使用壓縮冤狡,如果還大孙蒙,那可能拆分到多個key中。

Memcached 客戶端使用封裝

抽象出來了一個接口:

using System;

namespace Tdf.Memcached
{
    public interface IMemcached
    {
        void Add(string key, object value);
        void Add(string key, object value, DateTime expiredDateTime);
        void Update(string key, object value);
        void Update(string key, object value, DateTime expiredDateTime);
        void Set(string key, object value);
        void Set(string key, object value, DateTime expiredTime);
        void Delete(string key);
        object Get(string key);
        bool KeyExists(string key);
    }
}

MemcacheHelper.cs

using Memcached.ClientLibrary;
using System;
using System.Configuration;

namespace Tdf.Memcached
{
    /// <summary>
    /// 基于Memcached.ClientLibrary封裝使用Memchached信息
    /// 讀取緩存存放在服務(wù)器
    /// </summary>
    public class MemcacheHelper
    {
        /// <summary>
        /// 字段_instance,存放注冊的緩存信息
        /// </summary>
        private static MemcacheHelper _instance;

        /// <summary>
        /// 緩存客戶端
        /// </summary>
        private readonly MemcachedClient _client;

        /// <summary>
        /// 受保護(hù)類型的緩存對象悲雳,初始化一個新的緩存對象
        /// </summary>
        protected MemcacheHelper()
        {
            // 讀取app.Config中需要緩存的服務(wù)器地址信息挎峦,可以傳遞多個地址,使用";"分隔
            string[] serverList = ConfigurationManager.AppSettings["readWriteHosts"].Split(new char[] { ';' });
            
            try
            {
                // 初始化池
                var sockIoPool = SockIOPool.GetInstance();
                // 設(shè)置服務(wù)器列表
                sockIoPool.SetServers(serverList);
                // 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
                sockIoPool.SetWeights(new int[] { 1 });
                // 初始化時創(chuàng)建連接數(shù)
                sockIoPool.InitConnections = 3;
                // 最小連接數(shù)
                sockIoPool.MinConnections = 3;
                // 最大連接數(shù)
                sockIoPool.MaxConnections = 5;
                // 連接的最大空閑時間合瓢,下面設(shè)置為6個小時(單位ms)坦胶,超過這個設(shè)置時間,連接會被釋放掉
                sockIoPool.MaxIdle = 1000 * 60 * 60 * 6;
                // socket連接的超時時間晴楔,下面設(shè)置表示不超時(單位ms)顿苇,即一直保持鏈接狀態(tài)
                sockIoPool.SocketConnectTimeout = 0;
                // 通訊的超時時間,下面設(shè)置為3秒(單位ms),.Net版本沒有實現(xiàn)
                sockIoPool.SocketTimeout = 1000 * 3;
                // 維護(hù)線程的間隔激活時間税弃,下面設(shè)置為30秒(單位s)纪岁,設(shè)置為0時表示不啟用維護(hù)線程
                sockIoPool.MaintenanceSleep = 30;
                // 設(shè)置SocktIO池的故障標(biāo)志
                sockIoPool.Failover = true;
                // 是否對TCP/IP通訊使用nalgle算法,.net版本沒有實現(xiàn)
                sockIoPool.Nagle = false;
                // socket單次任務(wù)的最大時間(單位ms)钙皮,超過這個時間socket會被強(qiáng)行中端掉蜂科,當(dāng)前任務(wù)失敗。
                sockIoPool.MaxBusy = 1000 * 10;
                sockIoPool.Initialize();
                // 實例化緩存對象
                _client = new MemcachedClient();
                // 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮短条,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
                _client.EnableCompression = false;
                // 壓縮設(shè)置导匣,超過指定大小的都壓縮 
                _client.CompressionThreshold = 1024 * 1024;
            }
            catch (Exception ex)
            {
                // 錯誤信息寫入事務(wù)日志
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 獲取緩存的實例對象,方法調(diào)用的時候使用
        /// </summary>
        /// <returns></returns>
        public static MemcacheHelper GetInstance()
        {
            return _instance;
        }

        /// <summary>
        /// 添加緩存信息(如果存在緩存信息則直接重寫設(shè)置茸时,否則添加)
        /// 使用:MemcacheHelper.GetInstance().Add(key,value)
        /// </summary>
        /// <param name="key">需要緩存的鍵</param>
        /// <param name="value">需要緩存的值</param>
        public void Add(string key, object value)
        {
            if (_client.KeyExists(key))
            {
                _client.Set(key, value);
            }
            _client.Add(key, value);
        }

        /// <summary>
        /// 添加緩存信息
        /// 使用:MemcacheHelper.GetInstance().Add(key,value,Datetime.Now())
        /// </summary>
        /// <param name="key">需要緩存的鍵</param>
        /// <param name="value">需要緩存的值</param>
        /// <param name="expiredDateTime">設(shè)置的緩存的過時時間</param>
        public void Add(string key, object value, DateTime expiredDateTime)
        {
            _client.Add(key, value, expiredDateTime);
        }

        /// <summary>
        /// 修改緩存的值
        /// 使用:MemcacheHelper.GetInstance().Update(key,value)
        /// </summary>
        /// <param name="key">需要修改的鍵</param>
        /// <param name="value">需要修改的值</param>
        public void Update(string key, object value)
        {
            _client.Replace(key, value);
        }

        /// <summary>
        /// 修改緩存的值
        /// 使用:MemcacheHelper.GetInstance().Update(key,value,Datetime.Now())
        /// </summary>
        /// <param name="key">需要修改的鍵</param>
        /// <param name="value">需要修改的值</param>
        /// <param name="expiredDateTime">設(shè)置的緩存的過時時間</param>
        public void Update(string key, object value, DateTime expiredDateTime)
        {
            _client.Replace(key, value, expiredDateTime);
        }

        /// <summary>
        /// 設(shè)置緩存
        /// 使用:MemcacheHelper.GetInstance().Set(key,value)
        /// </summary>
        /// <param name="key">設(shè)置緩存的鍵</param>
        /// <param name="value">設(shè)置緩存的值</param>
        public void Set(string key, object value)
        {
            _client.Set(key, value);
        }

        /// <summary>
        /// 設(shè)置緩存贡定,并修改過期時間
        /// 使用:MemcacheHelper.GetInstance().Set(key,value,Datetime.Now())
        /// </summary>
        /// <param name="key">設(shè)置緩存的鍵</param>
        /// <param name="value">設(shè)置緩存的值</param>
        /// <param name="expiredTime">設(shè)置緩存過期的時間</param>
        public void Set(string key, object value, DateTime expiredTime)
        {
            _client.Set(key, value, expiredTime);
        }

        /// <summary>
        /// 刪除緩存
        /// 使用:MemcacheHelper.GetInstance().Delete(key)
        /// </summary>
        /// <param name="key">需要刪除的緩存的鍵</param>
        public void Delete(string key)
        {
            _client.Delete(key);
        }

        /// <summary>
        /// 獲取緩存的值
        /// 使用:MemcacheHelper.GetInstance().Get(key)
        /// </summary>
        /// <param name="key">傳遞緩存中的鍵</param>
        /// <returns>返回緩存在緩存中的信息</returns>
        public object Get(string key)
        {
            return _client.Get(key);
        }

        /// <summary>
        /// 緩存是否存在
        /// 使用:MemcacheHelper.GetInstance().KeyExists(key)
        /// </summary>
        /// <param name="key">傳遞緩存中的鍵</param>
        /// <returns>如果為true,則表示存在此緩存可都,否則比表示不存在</returns>
        public bool KeyExists(string key)
        {
            return _client.KeyExists(key);
        }

        /// <summary>
        /// 注冊Memcache緩存(在Global.asax的Application_Start方法中注冊)
        /// 使用:MemcacheHelper.RegisterMemcache();
        /// </summary>
        public static void RegisterMemcache()
        {
            if (_instance == null)
            {
                _instance = new MemcacheHelper();
            }
        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <appSettings>
      <add key="readWriteHosts" value="127.0.0.1:11211" />
    </appSettings>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-0.84.0.0" newVersion="0.84.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Program.cs

using System;
using Tdf.Memcached;

namespace Tdf.MemcachedTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // 注冊Memcache緩存
            MemcacheHelper.RegisterMemcache();

            // 添加緩存信息(如果存在緩存信息則直接重寫設(shè)置缓待,否則添加)
            MemcacheHelper.GetInstance().Add("userName", "Bobby");

            // 緩存是否存在
            var tf = MemcacheHelper.GetInstance().KeyExists("userName");
            Console.WriteLine(tf);

            // 獲取緩存的值
            var s = MemcacheHelper.GetInstance().Get("userName");
            Console.WriteLine(s);
            Console.Read();

        }
    }
}

到此蚓耽,我們已經(jīng)完成了一個最小化的memcached集群讀寫測試Demo。但是旋炒,在實際的開發(fā)場景中步悠,遠(yuǎn)不僅僅是存儲一個字符串,更多的是存儲一個自定義的類的實例對象瘫镇。這就需要使用到序列化鼎兽,下面我們來新加一個類Claim,讓其作為可序列化的對象來存儲進(jìn)Memcached中铣除。注意:需要為該類加上[Serializable]的特性谚咬!

using System;
using Tdf.Memcached;

namespace Tdf.MemcachedTest
{
    [Serializable]
    public class Claim
    {
        public int UserId { get; set; }
        public string UserName { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 注冊Memcache緩存
            MemcacheHelper.RegisterMemcache();

            // 自定義對象存儲
            Claim claim = new Claim();
            claim.UserId = 694802856;
            claim.UserName = "難念的經(jīng)";

            MemcacheHelper.GetInstance().Add("Claim", claim);

            Claim newMyObj = MemcacheHelper.GetInstance().Get("Claim") as Claim;
            Console.WriteLine("Hello,My UserId is {0} and UserName is {1}", newMyObj.UserId, newMyObj.UserName);

            Console.Read();

        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尚粘,隨后出現(xiàn)的幾起案子择卦,更是在濱河造成了極大的恐慌,老刑警劉巖郎嫁,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉继,死亡現(xiàn)場離奇詭異,居然都是意外死亡行剂,警方通過查閱死者的電腦和手機(jī)秕噪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厚宰,“玉大人腌巾,你說我怎么就攤上這事〔酰” “怎么了澈蝙?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撵幽。 經(jīng)常有香客問我灯荧,道長,這世上最難降的妖魔是什么盐杂? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任逗载,我火速辦了婚禮,結(jié)果婚禮上链烈,老公的妹妹穿的比我還像新娘厉斟。我一直安慰自己,他們只是感情好强衡,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布擦秽。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪感挥。 梳的紋絲不亂的頭發(fā)上缩搅,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機(jī)與錄音触幼,去河邊找鬼硼瓣。 笑死,一個胖子當(dāng)著我的面吹牛置谦,可吹牛的內(nèi)容都是我干的巨双。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼霉祸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了袱蜡?” 一聲冷哼從身側(cè)響起丝蹭,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坪蚁,沒想到半個月后奔穿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敏晤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年贱田,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘴脾。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡男摧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出译打,到底是詐尸還是另有隱情耗拓,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布奏司,位于F島的核電站乔询,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏韵洋。R本人自食惡果不足惜竿刁,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搪缨。 院中可真熱鬧食拜,春花似錦、人聲如沸勉吻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惑惶,卻和暖如春煮盼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背带污。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工僵控, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鱼冀。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓报破,卻偏偏與公主長得像,于是被迫代替她去往敵國和親千绪。 傳聞我的和親對象是個殘疾皇子充易,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容