memcached的程序,陸陸續(xù)續(xù)碰到一些問題
用C++寫過一些服務(wù)程序,也和memcached有關(guān),用到的是網(wǎng)上的開源memcachedclient靜態(tài)庫。
按照當(dāng)時(shí)系統(tǒng)的邏輯,memcached的讀寫,都是用C++完成,所以基本上很順利。
最近,把一些服務(wù)接口重構(gòu),用的是C#,于是乎需要對C++服務(wù)程序存儲在memecached里面的key-value進(jìn)行讀取。
本著不重復(fù)制造輪子的想法,在網(wǎng)上找了個(gè)Enyim.Caching.dll。結(jié)果問題就來了……
問題1:網(wǎng)上找過好多有關(guān)Enyim.Caching.dll使用的說明,基本上都是在webconfig里面需要增加配置節(jié)點(diǎn),里面配置一些memcached的端口列表之類的…… 大概是因?yàn)閙emcached的分布式功能,本身需要在客戶端實(shí)現(xiàn),所以是配置IP
ort的一個(gè)列表,Enyim.Caching.dll內(nèi)部自動(dòng)實(shí)現(xiàn)路由功能(只是估計(jì),不太確定)。

但是,按照本人的實(shí)際系統(tǒng)需要,某些key value是在A服務(wù)器上,另外的一些是在B服務(wù)器上,不同類型的key-value,是保存在不同的服務(wù)器上的。所以無法統(tǒng)一的按照網(wǎng)上的這種方式配置。
問題2:當(dāng)實(shí)例化MemcachedClient對象時(shí),發(fā)生異常錯(cuò)誤。
問題3:解決問題2后發(fā)現(xiàn):即便是只有一個(gè)memcached的服務(wù),測試發(fā)現(xiàn):用C#無法讀取memcached服務(wù)端的key-value(C++的程序存儲的)。
沒辦法,只得找Enyim.Caching.dll的源碼。
svn下載地址:http://nuxleus.googlecode.com/sv ... or/Enyim.Memcached/
問題1的解決:
MemcachedClient類的另一個(gè)構(gòu)造函數(shù),支持自定義配置(不使用webconfig的section配置)
具體用法:
private MemcachedClient _client;
private MemcachedClientConfiguration _config;
_config = new MemcachedClientConfiguration();
IPHostEntry ipEntry = Dns.GetHostEntry("域名");
IPAddress ipAddr = ipEntry.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, iPort);
_config.Servers.Add(ipEndPoint);
_config.NodeLocator = typeof(DefaultNodeLocator);
_config.KeyTransformer = typeof(DefaultKeyTransformer);
_config.Transcoder = typeof(DefaultTranscoder);
_config.SocketPool.MinPoolSize = 10;
_config.SocketPool.MaxPoolSize = 100;
_config.SocketPool.ConnectionTimeout = new TimeSpan(0, 0, 10);
_config.SocketPool.DeadTimeout = new TimeSpan(0, 0, 30);
? ?? ?? ?? ?_client = new MemcachedClient(_config);
復(fù)制代碼
問題2的解決:
報(bào)出的異常,是關(guān)于性能計(jì)數(shù)方面的,Enyim.Caching.dll內(nèi)部會(huì)用到windows系統(tǒng)的性能計(jì)數(shù)功能,在windows系統(tǒng)性能窗口輸出性能曲線圖。具體是神馬異常描述,現(xiàn)在已經(jīng)忘了,好像是說使用系統(tǒng)本身的計(jì)數(shù)器,需要設(shè)置為readonly。想想,本人的程序的memcached的客戶端,并不太關(guān)心性能指標(biāo)。而且平時(shí)也不會(huì)去看這個(gè)東西,于是,把Enyim.Caching.dll源碼中的兩個(gè)有關(guān)性能計(jì)數(shù)的文件刪掉了,分別是:
InstancePerformanceCounters.cs
InstancePerformanceCountersInstaller.cs
當(dāng)然,其他地方要用到這兩個(gè)類的地方,也要對應(yīng)的給它注釋掉。
問題3的解決:
memcached底層說起來,也是TCP通訊。
結(jié)合源碼,通過對實(shí)際讀取請求的跟蹤,發(fā)現(xiàn):請求指令 get mykey 其實(shí)已經(jīng)發(fā)過去了,并且也收到了服務(wù)器的應(yīng)答,而且應(yīng)答中的數(shù)據(jù)(C++的結(jié)構(gòu)體)長度是正確的。
問題出在Enyim.Caching.dll中,DefaultTranscoder類的反序列化函數(shù)中。
具體的函數(shù)為:object ITranscoder.Deserialize(CacheItem item)
相應(yīng)的位置是:
if (code == TypeCode.Empty)
? ?return null;
復(fù)制代碼
條件滿足,返回null了,這就是造成外層邏輯無法讀取key-value的原因。
讀了相關(guān)源碼,才明白這個(gè)判斷里面的code,是來自memcached服務(wù)端返回?cái)?shù)據(jù)中的一個(gè)flag標(biāo)志。
于是網(wǎng)上找了下Memcached的協(xié)議:
===========================================
<上面省略...>
讀取命令如下所示:
get *"r"n
*表示一個(gè)或多個(gè)使用空格分割的關(guān)鍵字字符串。
發(fā)送命令后,客戶端等待返回一個(gè)或多個(gè)數(shù)據(jù)項(xiàng),每個(gè)數(shù)據(jù)項(xiàng)的格式是一個(gè)文本行,后跟著一個(gè)數(shù)據(jù)塊。當(dāng)所有的數(shù)據(jù)項(xiàng)發(fā)送完畢后,服務(wù)器發(fā)送字符串”END"r"n”表示服務(wù)器反饋數(shù)據(jù)的結(jié)束。
返回?cái)?shù)據(jù)項(xiàng)的格式如下:
VALUE "r"n
"r"n
是發(fā)生數(shù)據(jù)項(xiàng)的關(guān)鍵字。
是存儲該數(shù)據(jù)項(xiàng)時(shí),客戶端命令中的標(biāo)志字段。
是緊跟文本行后數(shù)據(jù)塊的長度,不包括終結(jié)符”"r"n”。
是數(shù)據(jù)項(xiàng)的數(shù)據(jù)部分。
<下面省略....>
全部協(xié)議內(nèi)容參見:http://www.cnblogs.com/kevintian ... .html#_Toc198607274
==============================================
我跟蹤到的通信數(shù)據(jù):
memcached的客戶端向服務(wù)器發(fā)送讀取請求
get mykey[回車換行]
隨后,服務(wù)器返回:
VALUE mykey 0 15[回車換行]
<具體的數(shù)據(jù)內(nèi)容>
END
上面說的那個(gè)判斷中的flag,就是服務(wù)器返回?cái)?shù)據(jù)第一行中的:0
為什么會(huì)返回0,導(dǎo)致C#無法正確的返回?cái)?shù)據(jù)?
協(xié)議中說了:是存儲該數(shù)據(jù)項(xiàng)時(shí),客戶端命令中的標(biāo)志字段。
原來是key value存儲的時(shí)候,就已經(jīng)由客戶端指定了。那看來是C++的程序,給了這個(gè)flag一個(gè)零值。
繼續(xù)分析源碼,才知道,C#的Enyim.Caching.dll是拿這個(gè)flag來標(biāo)識value的數(shù)據(jù)類型。。。
value的數(shù)據(jù)類型,可能是int,可能是string,可能是byte[].....
存儲時(shí),就給它標(biāo)識好了,然后讀取時(shí),就知道value是啥類型的東西了。
這么一來,假設(shè):存儲時(shí)數(shù)據(jù)是int類型,然后,讀取時(shí),MemcachedClient返回的其實(shí)是一個(gè)object
如果強(qiáng)轉(zhuǎn)為string,估計(jì)會(huì)報(bào)錯(cuò)吧……,這也避免了數(shù)據(jù)發(fā)生錯(cuò)亂。
C++存儲時(shí),指定flag為0,C#讀取時(shí),不知道0代表是什么類型(剛好沒有0值表示的類型),于是就發(fā)生了問題3.
解決方法:
出問題代碼,是在DefaultTranscoder類中,從名字來看,是一個(gè)默認(rèn)的緩沖區(qū)數(shù)據(jù)轉(zhuǎn)換的功能類。
其實(shí)也就是做key-value存儲和讀取的序列化和反序列化用的。
對照看之前的問題1的解決代碼中:
_config.Transcoder = typeof(DefaultTranscoder);
配置對象可以指定一個(gè)用戶自定義的序列化和反序列化的類,這個(gè)類必須實(shí)現(xiàn)ITranscoder接口。
于是乎,就在自己的代碼中,建立了一個(gè)MyTranscoder的類。
實(shí)際上就是:把DefaultTranscoder的代碼copy一份,類名改為:MyTranscoder。
同時(shí)之前出問題的地方,修改為:
//if (code == TypeCode.Empty)
//? ? return null;
if (code == TypeCode.Empty)
? ?? ?? ?? ?? ?? ???return item.Data.Array;
復(fù)制代碼
然后,外層就能獲取到byte[]類型的value數(shù)據(jù)