日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
關(guān)于Memcached客戶端CPU過高問題的排查

公司網(wǎng)站使用了Memcached來做分布式緩存,最近有人反映Memcached客戶端占用CPU過高,懷疑是第三方客戶端性能不佳,進(jìn)而懷疑是文本協(xié)議的問題,要求部門自己開發(fā)Memcached的客戶端,使其支持二進(jìn)制協(xié)議。因?yàn)橹匦麻_發(fā)客戶端工作量比較大,同時(shí)在日常開發(fā)中,沒有聽說過Memcached客戶端遇到瓶頸。因此對(duì)此問題進(jìn)行了排查。結(jié)果發(fā)現(xiàn)主要是由于客戶端反序列化,類設(shè)計(jì)不合理造成的。把排查過程分享下,希望對(duì)其他人有所幫助。 

創(chuàng)新互聯(lián)長(zhǎng)期為上千余家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為梁子湖企業(yè)提供專業(yè)的成都網(wǎng)站制作、成都做網(wǎng)站、外貿(mào)營(yíng)銷網(wǎng)站建設(shè),梁子湖網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

首先想到是:Memcached服務(wù)器端內(nèi)存占滿,在清理內(nèi)存中,造成客戶端socket連接不上,不斷發(fā)生異常。隨上服務(wù)器查看了Memcached的內(nèi)存占用率,連接數(shù)等,發(fā)現(xiàn)利用率均很低。暫時(shí)先排除服務(wù)器端問題。 

其次想到可能是第三方在使用socket連接池時(shí),造成資源沒有關(guān)閉,或者死鎖。隨對(duì)第三方客戶端代碼粗略讀了一遍,并搜索相關(guān)文檔。未發(fā)現(xiàn)異常代碼。暫時(shí)先排除第三方客戶端問題。 

最后想到會(huì)不會(huì)是開發(fā)人員在代碼編寫中出現(xiàn)了問題。隨對(duì)反映問題的兩個(gè)產(chǎn)品進(jìn)行了排查。發(fā)現(xiàn)了以下代碼。

 
 
 
 
  1. static Serializer ser = new Serializer(typeof(List)); 
  2. //using JsonExSerializer;  
  3. public static List GetAllUserModule(int userId)  
  4. {  
  5.     string cache = CacheManager.Current.Get(GetCacheKey(userId));  
  6.     if (!string.IsNullOrEmpty(cache))  
  7.     {  
  8.         return ser.Deserialize(cache) as List;  
  9.     }  
  10.     else 
  11.     {  
  12.         return null;  
  13.     }  
  14. }  
  15.  
  16. public static List SetAllUserModule(int userId, List modules)  
  17. {  
  18.     if (modules != null)  
  19.     {  
  20.         string cache = ser.Serialize(modules);  
  21.         CacheManager.Current.Add(GetCacheKey(userId), cache);  
  22.     }  
  23.     else 
  24.     {  
  25.         CacheManager.Current.Remove(GetCacheKey(userId));  
  26.     }  
  27.     return modules;  

代碼片段2:  

 
 
 
 
  1. ///   
  2. /// 聊天室房間  
  3. ///   
  4. [Serializable]  
  5. public class Room  
  6. {  
  7.     //房間有觀看人員數(shù)據(jù)  
  8.     List _viewers = null;  
  9.     List _blackips = null;  
  10.     List _blackviewers = null;  
  11.     List _notice = null;  
  12.     List _speakers = null;  
  13.     List _content = null;  
  14.  
  15.  
  16.     ///   
  17.     /// 添加新聊天者  
  18.     ///   
  19.     /// 返回新添加的聊天人員  
  20.     public Viewer AddViewer()  
  21.     {  
  22.         Viewer vi = new Viewer();  
  23.         //MaxViewerID += 1;  
  24.           
  25.         //int id = MaxViewerID;   
  26.         int id = GetViewerID();   
  27.         vi.Name = GetViewerName("游客" + id);  
  28.         //vi.IP = System.Web.HttpContext.Current.Request.UserHostAddress;  
  29.         vi.IP = "127.0.0.1";  
  30.         vi.ViewID = id;  
  31.         Viewers.Add(vi);  
  32.         return vi;   
  33.     }  
  34.  
  35. ///   
  36.     /// 添加聊天內(nèi)容  
  37.     ///   
  38.     /// 聊天的內(nèi)容  
  39.     /// 發(fā)言人的id  
  40.     /// 返回新添加的對(duì)象  
  41.     public Content AddContent(string content, int viewid)  
  42.     {  
  43.         MaxContentID += 1;  
  44.         Content con = new Content(DateTime.Now, content, viewid, MaxContentID);  
  45.         Contents.Add(con);  
  46.         return con;  
  47.     }  
  48.     ......  

調(diào)用代碼為:

 
 
 
 
  1. Room room = LiveSys.Get(key);  
  2. lock (room)  
  3. {  
  4.     if (room.MaxContentID == 0)  
  5.     {  
  6.         //ChatContentOp cpo = new ChatContentOp();  
  7.         //room.MaxContentID = cpo.GetMaxContentID();  
  8.  
  9.         room.MaxContentID = 300;  
  10.     }  
  11.     int viewerID = 123124123;  
  12.     room.AddContent(chatContent, viewerID);  
  13.     //判斷內(nèi)容是否大于100條。如果大于100條,刪除最近的100條以外的數(shù)據(jù)。  
  14.     System.IO.File.AppendAllText(@"d:\haha.txt", "最大數(shù)值:" + 
  15. room.LimitContentCount + "###############聊天記錄數(shù):" + room.Contents.Count + "\r\n");  
  16.     if (room.Contents.Count > room.LimitContentCount)  
  17.     {  
  18.         room.Contents.RemoveRange(0, room.Contents.Count - room.LimitContentCount);  
  19.     }  
  20. }  
  21. LiveSys.Set(key, room); 

代碼1存在的問題是:

Cache存儲(chǔ)的參數(shù)類型為object,沒有必要先進(jìn)行一次序列化,然后再進(jìn)行存儲(chǔ)。而序列化是很消耗CPU的。

代碼2問題:

代碼2實(shí)現(xiàn)的是一個(gè)在線聊天室,聊天室本身含有訪客,發(fā)言等內(nèi)容。在發(fā)言時(shí),對(duì)聊天室內(nèi)容進(jìn)行判斷,只顯示最近30條。新進(jìn)來訪客直接加到訪客別表中。表面上是沒什么問題的。但是細(xì)想之下有兩個(gè)問題:

1 聊天室類設(shè)計(jì)的比較復(fù)雜,每次從Memcached服務(wù)端取得數(shù)據(jù)后,都要進(jìn)行類型轉(zhuǎn)換。

2 沒有訪客清理機(jī)制。隨著訪客的不斷進(jìn)入,對(duì)象的體積會(huì)不斷增大。

對(duì)存疑部分編寫了代碼進(jìn)行測(cè)試。測(cè)試結(jié)果果然如推測(cè)所想。測(cè)試結(jié)果如下:

場(chǎng)景

寫入

讀取

大小

(單位)

CPU

次數(shù)

時(shí)間

平均

次數(shù)

時(shí)間

平均

本地緩存

10000

0.03125

0

10000

0

0

1k

0

MemClient

10000

19.2656

0.001926

10000

22.75

0.002275

1k

 

Json1k

1000

2.8437

0.002843

1000

5.375

0.005375

1k

 

Json8k

1000

3.8593

0.003859

1000

29.0312

0.029031

8k

 

直播1000人次

1000

38.9375

0.038937

1000

 

 

50k

 

直播8000人次

100

18.25

0.1825

100

 

 

350k

 

500k

100

7.375

0.07375

100

7.09375

0.070937

500k

 

場(chǎng)景

寫入

讀取

大小

(單位)

CPU

次數(shù)

時(shí)間

平均

次數(shù)

時(shí)間

平均

本地緩存

10000

0.03125

3.125E-06

10000

0.015625

1.5625E-06

1k

0

MemClient

10000

19.78125

0.001978

10000

21.953125

0.002195

1k

 

Json1k

1000

2.03125

0.002031

1000

6.078125

0.006078

1k

 

Json8k

1000

2.765625

0.002765

1000

55.375

0.055375

8k

 

直播1000人次

1000

38.53125

0.038531

1000

  

50k

 

直播8000人次

100

17.96875

0.179687

1000

  

350k

 

500k

100

7.5

0.075

100

6.5625

0.065625

500k

 

場(chǎng)景

寫入

讀取

大小

(單位)

CPU

次數(shù)

時(shí)間

平均

次數(shù)

時(shí)間

平均

本地緩存

10000

0.015625

1.5625E-06

10000

0.015625

1.5625E-06

1k

0

MemClient

10000

18.015625

0.001801

10000

25.96875

0.002596

1k

6%

Json1k

1000

1.15625

0.001156

1000

3.078125

0.003078

1k

40%

Json8k

1000

1.859375

0.001859

1000

32.484375

0.032484

8k

50%

直播1000人次

1000

45.046875

0.045046

1000

  

50k

30-40%

直播8000人次

100

31.703125

0.317031

100

  

350k

50%

500k

100

7.0625

0.070625

100

6.421875

0.064218

500k

6%

直播1000人次(當(dāng)天一共有1000人訪問,數(shù)據(jù)來源于運(yùn)營(yíng)檢測(cè)),留言內(nèi)容為30條時(shí),Room體積大概為:57K  

直播1000人次(當(dāng)天一共有8000人訪問,數(shù)據(jù)來源于運(yùn)營(yíng)檢測(cè)),留言內(nèi)容為30條時(shí),Room體積大概為:350k

根據(jù)圖表可以看到以下情況:處理時(shí)間、CPU利用率和數(shù)據(jù)量大小,序列化,類復(fù)雜性都有關(guān)系。

序列化問題(類型轉(zhuǎn)換)對(duì)性能影響最為明顯(可在場(chǎng)景”json1k”、場(chǎng)景直播中看到)。在Json1k中,存儲(chǔ)對(duì)象和前幾個(gè)場(chǎng)景是相同的,處理時(shí)間也相差不大,較大區(qū)別是CPU利用率由5%左右增長(zhǎng)到40%左右(反序列化時(shí)尤為明顯)。在場(chǎng)景直播系統(tǒng)中,不存在序列化問題,但是其對(duì)象屬性中存在”訪客”, ”繁衍”等多個(gè)復(fù)雜對(duì)象,造成其在處理時(shí)需要處理過多的類型轉(zhuǎn)換,同時(shí)其體積不斷增大。

存儲(chǔ)對(duì)象的大小和處理時(shí)間存在一定關(guān)系,例如場(chǎng)景”500k”,其處理時(shí)間增長(zhǎng),但是其CPU利用率并未提高,其時(shí)間增長(zhǎng)是由于對(duì)象傳輸造成。

本地緩存在內(nèi)存中進(jìn)行尋址和類型轉(zhuǎn)換,涉及不到Socket連接,網(wǎng)絡(luò)傳輸,序列化操作,所以其處理相當(dāng)快。

就測(cè)試結(jié)果看:

本地緩存性能大約是分布式緩存性能的100倍左右。而出問題的聊天室除了CPU增高以外,其性能更比分布式緩存再降低40倍(直播1000人次)到200倍(直播8000人次)。綜合來看,聊天室的分布式緩存比本地緩存降了4000倍,甚至更多。

但是,還沒有完。 

對(duì)于第二個(gè)問題,更改類設(shè)計(jì),清楚無效訪客,即可解決。 

但是第一個(gè)問題,為什么用戶在存儲(chǔ)之前,先進(jìn)行json序列化呢?嗯,這是一個(gè)問題。

遂問之。

答曰,有些類直接使用第三方客戶端存儲(chǔ)時(shí),直接存儲(chǔ)報(bào)錯(cuò),所以先序列化為json類型,取值時(shí)再反序列化回來。

嗯,還有這事?

開發(fā)人員說了相關(guān)代碼。 

 
 
 
 
  1. interface IUser  
  2. {  
  3.     String UserId{ get; set;}  
  4.     String UserName{ get; set;}  
  5. }  
  6.  
  7. [Serializable]  
  8. class UserInfo : IUser  
  9. {  
  10.     String UserId{ get; set;}  
  11.     String UserName{ get; set;}  
  12. }  
  13. [Serializable]  
  14. class Game  
  15. {  
  16.     IUser User{ get; set;}  
  17.     String UserName{ get; set;}  

他說:Game對(duì)象在直接使用MemcachedClient時(shí),是不能被二進(jìn)制序列化的,因?yàn)槠銾ser屬性類型為IUser,為一個(gè)接口。因此想了一個(gè)解決方法,即先將Game對(duì)象進(jìn)行 json序列化將其變?yōu)樽址?,然后將字符串存?chǔ)到Memcached。 

原來是這樣。

接著又查看了MemcachedClient源代碼,其需要將對(duì)象進(jìn)行二進(jìn)制序列化,然后進(jìn)行存儲(chǔ)。接口屬性不能被序列化,遂又對(duì)序列化問題進(jìn)行了測(cè)試(見附件)。測(cè)試結(jié)果顯示上述代碼直接進(jìn)行二進(jìn)制序列化是可以的,同時(shí)直接使用第三方客戶端也是可以可行的。 

問題出在哪?難道是沒有加[Serializable]。

一查果然:一個(gè)Serializable引發(fā)的血案。

記得有人說過,慎用分布式,能不用盡量不用。

一方面在性能上確實(shí)下降很多,分布式存儲(chǔ)主要性能消耗在以下幾個(gè)方面:協(xié)議解析,Socket連接,數(shù)據(jù)傳輸,序列化/類型轉(zhuǎn)換。

一方面在使用場(chǎng)景和類設(shè)計(jì)上要求也更加嚴(yán)格。個(gè)人認(rèn)為Memcached是不太適合存儲(chǔ)特別大的文件的。雖然有人說網(wǎng)上已經(jīng)有用來存儲(chǔ)視頻的。

還有幾個(gè)問題希望知道的朋友回答下:

1 有沒有.Net方面的Memcached客戶端支持二進(jìn)制協(xié)議和一致性的?

2 測(cè)試中發(fā)現(xiàn),當(dāng)Memcached設(shè)置緩存過小時(shí)(例如64M),當(dāng)其內(nèi)存使用已經(jīng)到62M時(shí),再進(jìn)行存儲(chǔ),新存儲(chǔ)的內(nèi)容再取出來就是空值,不知道是什么原因。


當(dāng)前名稱:關(guān)于Memcached客戶端CPU過高問題的排查
文章出自:http://m.5511xx.com/article/dhshghe.html