日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

优化你的DiscuzNT3.0,让它跑起来(4)asp.net 缓存和死锁

發布時間:2023/12/15 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 优化你的DiscuzNT3.0,让它跑起来(4)asp.net 缓存和死锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?注:本文僅針對 DiscuzNT3.0, sqlserver 2000版本,其他版本請勿對號入座.

?經過前面的幾次優化之后我們的論壇終于穩定了一段時間,大概半年之后我們的論壇迎來了每天大約50萬的pv,這時候論壇有開始出現了問題。癥狀是這樣的:

管理員發現,網站經常會打不開, 但是也不報錯,好像永遠一直在打開,直到瀏覽器認為它打不開了,這樣的癥狀每天會出現幾次,而且越來越頻繁。每次發生這樣的情況過后一般iis的事件查看器都會asp.net有死鎖提示,于是我知道,我終于遇上傳說中的死鎖了,每次有死鎖跡象的時候我都跟蹤了一下sqlserver,發現數據庫是正常的,那看來就是asp.net這邊的問題了。

可是DiscuzNT這么大的一個論壇,里面包含了十幾個項目,項目如此之多,代碼量如此之大,到底哪里出了問題呢,一下子還真不好定位。還好微軟給我們提供了兩個很不錯的工具,windbg 和 IIS?Diagnostics,winddbg是用來調試內存的工具,而IIS?Diagnostics則是抓取內存的好工具,我也正是借助這兩個工具才快速定位到了問題,不過很遺憾的是我抓取的dump文件由于時間太久,竟然找不到了,所以現在暫時無法一展它們的風采。(不過后續會介紹windbg的用法,因為它真的幫了我大忙)

那到底是哪里引發的死鎖呢,廢話不多說,看看下面的代碼就知道了,Discuz.Cache.DNTCache.cs 類文件

??1?????///?<summary>

?2?????????///?構造函數
?3?????????///?</summary>
?4?????????private?DNTCache()
?5?????????{
?6?????????????if(MemCachedConfigs.GetConfig()?!=?null?&&?MemCachedConfigs.GetConfig().ApplyMemCached)
?7?????????????????applyMemCached?=?true;
?8?
?9?????????????if?(applyMemCached)
10?????????????????cs?=?new?MemCachedStrategy();
11?????????????else
12?????????????{
13?????????????????cs?=?new?DefaultCacheStrategy();
14?
15?????????????????objectXmlMap?=?rootXml.CreateElement("Cache");
16?????????????????//建立內部XML文檔.
17?????????????????rootXml.AppendChild(objectXmlMap);
18?
19?????????????????//LogVisitor?clv?=?new?CacheLogVisitor();
20?????????????????//cs.Accept(clv);
21?
22?????????????????cacheConfigTimer.AutoReset?=?true;
23?????????????????cacheConfigTimer.Enabled?=?true;
24?????????????????cacheConfigTimer.Elapsed?+=?new?System.Timers.ElapsedEventHandler(Timer_Elapsed); // 重點看下這個方法
25?????????????????cacheConfigTimer.Start();
26?????????????}
27?????????}

?

看下這個方法 Timer_Elapsed?

1?????private?static?void?Timer_Elapsed(object?sender,?System.Timers.ElapsedEventArgs?e)
2?????????{
3?????????????if?(!applyMemCached)
4?????????????{
5?????????????????//檢查并移除相應的緩存項
6?????????????????instance?=?CachesFileMonitor.CheckAndRemoveCache(instance); // 這個方法里持有一個鎖
7?????????????}

8?????????}?

?

看看這個方法?CachesFileMonitor.CheckAndRemoveCache?

??1?????///?<summary>

?2?????????///?檢查和移除緩存
?3?????????///?</summary>
?4?????????///?<param?name="instance"></param>
?5?????????///?<returns></returns>
?6?????????public?static?DNTCache?CheckAndRemoveCache(DNTCache?instance)//
?7?????????{
?8?????????????//當程序運行中cache.config發生變化時則對緩存對象做刪除的操作
?9?????????????cachefilenewchange?=?System.IO.File.GetLastWriteTime(path);
10?????????????if?(cachefileoldchange?!=?cachefilenewchange)
11?????????????{????????????????
12?????????????????lock?(cachelockHelper)
13?????????????????{
14?????????????????????if?(cachefileoldchange?!=?cachefilenewchange)
15?????????????????????{
16?????????????????????????//當有要清除的項時
17?????????????????????????DataSet?dsSrc?=?new?DataSet();
18?????????????????????????dsSrc.ReadXml(path);
19?????????????????????????foreach?(DataRow?dr?in?dsSrc.Tables[0].Rows)
20?????????????????????????{
21?????????????????????????????if?(dr["xpath"].ToString().Trim()?!=?"")
22?????????????????????????????{
23?????????????????????????????????DateTime?removedatetime?=?DateTime.Now;
24?????????????????????????????????try
25?????????????????????????????????{
26?????????????????????????????????????removedatetime?=?Convert.ToDateTime(dr["removedatetime"].ToString().Trim());
27?????????????????????????????????}
28?????????????????????????????????catch
29?????????????????????????????????{
30?????????????????????????????????????;
31?????????????????????????????????}
32?
33?????????????????????????????????if?(removedatetime?>?cachefilenewchange.AddSeconds(-2))
34?????????????????????????????????{
35?????????????????????????????????????string?xpath?=?dr["xpath"].ToString().Trim();
36?????????????????????????????????????instance.RemoveObject(xpath,?false);?//?這個方法里持有第二個鎖
37?????????????????????????????????}
38?????????????????????????????}
39?????????????????????????}
40?
41?????????????????????????cachefileoldchange?=?cachefilenewchange;
42?
43?????????????????????????dsSrc.Dispose();
44?????????????????????}
45?????????????????}
46?????????????}
47?????????????return?instance;
48?????????}

?

看看?

RemoveObject?方法:

?

?1?????///?<summary>
?2?????????///?通過指定的路徑刪除緩存中的對象
?3?????????///?</summary>
?4?????????///?<param?name="xpath">分級對象的路徑</param>
?5?????????///?<param?name="writeconfig">是否寫入文件</param>
?6?????????public?virtual?void?RemoveObject(string?xpath,?bool?writeconfig)
?7?????????{
?8?????????????lock?(lockHelper)
?9?????????????{
10?????????????????try
11?????????????????{
12?????????????????????if?(applyMemCached)
13?????????????????????{
14?????????????????????????//移除相應的緩存項
15?????????????????????????cs.RemoveObject(xpath);
16?????????????????????}
17?????????????????????else
18?????????????????????{
19?????????????????????????if?(writeconfig)
20?????????????????????????{
21?????????????????????????????CachesFileMonitor.UpdateCacheItem(xpath); // 這里再次持有鎖
22?????????????????????????}
23?
24?????????????????????????XmlNode?result?=?objectXmlMap.SelectSingleNode(PrepareXpath(xpath));
25?????????????????????????//檢查路徑是否指向一個組或一個被緩存的實例元素
26?????????????????????????if?(result.HasChildNodes)
27?????????????????????????{
28?????????????????????????????//刪除所有對象和子結點的信息
29?????????????????????????????XmlNodeList?objects?=?result.SelectNodes("*[@objectId]");
30?????????????????????????????string?objectId?=?"";
31?????????????????????????????foreach?(XmlNode?node?in?objects)
32?????????????????????????????{
33?????????????????????????????????objectId?=?node.Attributes["objectId"].Value;
34?????????????????????????????????node.ParentNode.RemoveChild(node);
35?????????????????????????????????//刪除對象
36?????????????????????????????????cs.RemoveObject(objectId);
37?????????????????????????????}
38?????????????????????????}
39?????????????????????????else
40?????????????????????????{
41?????????????????????????????//刪除元素結點和相關的對象
42?????????????????????????????string?objectId?=?result.Attributes["objectId"].Value;
43?????????????????????????????result.ParentNode.RemoveChild(result);
44?????????????????????????????cs.RemoveObject(objectId);
45?????????????????????????}
46?????????????????????}
47?????????????????????
48?????????????????}
49?????????????????catch//如出錯誤表明當前路徑不存在
50?????????????????{}
51?
52?????????????}

53?????????}?

?

再來看看方法UpdateCacheItem:

?1?????///?<summary>
?2?????????///?更新或插入相應的緩存路徑
?3?????????///?</summary>
?4?????????///?<param?name="xpath"></param>
?5?????????public?static?void?UpdateCacheItem(string?xpath)
?6?????????{
?7?????????????DataTable?dt?=?new?DataTable("cachetableremove");
?8?????????????dt.Columns.Add("xpath",?System.Type.GetType("System.String"));
?9?????????????dt.Columns.Add("removedatetime",?System.Type.GetType("System.DateTime"));
10?
11?????????????//當有要清除的項時
12?????????????DataSet?dsSrc?=?new?DataSet();
13?????????????lock?(cachelockHelper)
14?????????????{
15?????????????????dsSrc.ReadXml(path);
16?
17?????????????????bool?nohasone?=?true;
18?????????????????foreach?(DataRow?dr?in?dsSrc.Tables[0].Rows)
19?????????????????{
20?????????????????????if?(dr["xpath"].ToString().Trim()?==?xpath)
21?????????????????????{
22?????????????????????????dr["removedatetime"]?=?DateTime.Now.ToString();
23?????????????????????????nohasone?=?false;
24?????????????????????????break;
25?????????????????????}
26?????????????????}
27?
28?????????????????if?(nohasone)
29?????????????????{
30?????????????????????DataRow?dr?=?dsSrc.Tables[0].NewRow();
31?????????????????????dr["xpath"]?=?xpath;
32?????????????????????dr["removedatetime"]?=?DateTime.Now.ToString();
33?????????????????????dsSrc.Tables[0].Rows.Add(dr);
34?????????????????}
35?
36?????????????????dsSrc.WriteXml(path);
37?????????????????dsSrc.Dispose();
38?????????????}

39?????????}

?

通過上面的代碼的紅字體部分我們可以看到,如果DNTCache 啟動它的定時器,它將會順序持有如下鎖

cachelockHelper —— 》?CachesFileMonitor.CheckAndRemoveCache() 持有

? ? |

? ? |

lockHelper ?——》?instance.RemoveObject()持有

?

? ? |

? ? |

cachelockHelper ?——》?CachesFileMonitor.UpdateCacheItem() 持有

?

如果剛好有一種情況持有所的順序跟上面相反,比如持有順序 lockHelper —— cachelockHelper —— lockHelper ?,而且這兩種情況同時發生了,那死鎖就這樣產生了,那有沒有這樣的情況?有!

我們來看看 Discuz.Cache.DNTCache.cs 的?GetCacheService():

?

?1?????///?<summary>
?2?????????///?單體模式返回當前類的實例
?3?????????///?</summary>
?4?????????///?<returns></returns>
?5?????????public?static?DNTCache?GetCacheService()
?6?????????{
?7?????????????if?(instance?==?null)
?8?????????????{
?9?????????????????lock?(lockHelper)
10?????????????????{
11?????????????????????if?(instance?==?null)
12?????????????????????{
13?????????????????????????instance?=?applyMemCached???new?DNTCache()?:?CachesFileMonitor.CheckAndRemoveCache(new?DNTCache());
14?????????????????????}
15?????????????????}
16?????????????}
17?
18?????????????return?instance;

19?????????}?

?

看上面的?lock (lockHelper), 是不是很眼熟啊,對了,他剛好是上面第一種持有鎖情況里面出現的第二個鎖,只要這個?

Discuz.Cache.DNTCache.GetCacheService() 和?CachesFileMonitor.CheckAndRemoveCache() 同時被啟動,那死鎖就產生了,而Discuz.Cache.DNTCache.GetCacheService()是返回當前緩存的實例,可以說他時時刻刻都在被調用,你可以嘗試搜索一下Discuz.Cache.DNTCache.GetCacheService(),你會發現他無處不在,當?Discuz.Cache.DNTCache.GetCacheService()Discuz.Cache.DNTCache.Timer_Elapsed() 同時發生,死鎖也就產生了。

?

?

既然問題找到了,那該如何解決呢,我看了一下,這個

Discuz.Cache.DNTCache里面用到的lock作用就是為了保證唯一性,但是我發現若不是唯一好像也沒什么影響,于是我把lock注釋了,試運行一段時間之后,發現并沒有什么影響,于是一直沿用至今。

?

?

本篇是本系列里針對DiscuzNT的c#代碼做出優化的第一篇文章,比較遺憾的是第一大功臣windbg未能華麗登場,不過它以后還有機會。欲知windbg是如何登場的,敬請期待下回分解。



?

? ??

?

總結

以上是生活随笔為你收集整理的优化你的DiscuzNT3.0,让它跑起来(4)asp.net 缓存和死锁的全部內容,希望文章能夠幫你解決所遇到的問題。

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