zoukankan      html  css  js  c++  java
  • 优化你的DiscuzNT3.0,让它跑起来(5)缓存和高cpu

    注:本文仅针对 DiscuzNT3.0, sqlserver 2000版本,其他版本请勿对号入座。

    在本系列的第(4)篇中,我们提到过windbg,上次没能华丽登场的他,今天终于要一展才华了。解决问题是windbg与生俱来的功夫,他今天将怎么样来解决我们的问题,静听分解。

    经过第(4)篇的优化之后,我们的论坛迎来了每日100万pv的突破性指标,相对于我们其他项目来说,这个量虽然不值一提,但毕竟这个项目是一切从0开始,能达到这个量也算是一个小突破,值得小庆祝一下。不过问题也随之而来了。我们的iis服务器的cpu占用率从之前的约30%,上升到了80%,按照我们的经验,这是不合理的,80%已经是一个危险的信号,随时都有可能引发server too busy。

    从公司的利益角度来考虑,为了节约服务器资源和成本,当然要把cpu降下来;从技术人员的角度来考虑,如果你之前没解决过,这将是对自己的一个挑战,如果你解决了,就刚好是你价值的体现,正所谓乱世出英雄,这是一样的道理。好了,废话不多说,有请我们的今日之星windbg。  

    关于windbg,网上有很多介绍,我就不再累述了。我只发表自己对他的两点认识,比较通俗:

    1)可以抓取内存包,即dump文件;

    2)通过命令输出内存中正在执行的动作;

    第3)步是技术人员自己要做的事情,通过命令输出的信息,分析找出问题的症结。这一步windbg是帮不了你的。

    网上关于windbg的最常解决的问题一般是

    1)程序崩溃;

    2)程序挂起,比如死锁,超时;

    3)高cpu;

    4)内存泄露;

    我们今天就来看看他是怎么找到 3)高cpu 的问题的。

    windbg定位高cpu一般常用的方法和步骤是:

    1)在高cpu的时候间隔约5秒钟抓一个包,抓3个左右,用adplus 命令;

    2)通过 !runaway 命令找出每个包的前几个线程(约10个);

    3)然后找出在每次的前10个线程里都出现的线程id;

    4)通过 !clrstack 命名输出该线程的执行动作;

    5)通过 第 4) 输出的信息找出问题的症结 

    (这是网上很多文章的说法,我觉得说的很模糊);

    关于第5)点,我提点自己的看法,我一般就看这些个线程id都在干什么,如果他们10个里面有8个都停留在一个方法,那这个方法就很值得怀疑,是需要重点检查的,那到底dnt3.0情况如何,我们一步一步来看看。

    1)首先抓包,在cmd下 通过adplus抓包,间隔10秒连抓3个(windbg命令的用法,不是本文的重点,google可以有一箩筐);

     

    2)用windbg分别打开3个dump文件,运行!runaway命令,找到耗时的线程;

     3个文件的截图如下:



    第1张

     

    第2张



    第3张

    3)找到10个最耗时的线程:11,12,21,22,34,38,40,46,47,48 。

    4)通过!clrstack命令输出他们都在干什么(因为太多,这里只选几个代表性的) 

    线程 11 :

    ---------------- 

     0:000> ~11s

    eax=02d25354 ebx=001410a8 ecx=02d253ec edx=02d2541c esi=00000360 edi=00000000
    eip=7c95847c esp=020bf15c ebp=020bf1cc iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    ntdll!KiFastSystemCallRet:
    7c95847c c3              ret
    0:011> !clrstack
    OS Thread Id: 0x143c (11)
    ESP       EIP     
    020bf2cc 7c95847c [GCFrame: 020bf2cc] 
    020bf2e8 7c95847c [HijackFrame: 020bf2e8] 
    020bf32c 69c7bc58 System.Xml.XmlElement.get_XPLocalName()
    020bf330 69c6ea0d System.Xml.DocumentXPathNavigator.get_LocalName()
    020bf334 69ca30c5 MS.Internal.Xml.XPath.XPathAxisIterator.get_Matches()
    020bf344 69ca37a8 MS.Internal.Xml.XPath.XPathChildIterator.MoveNext()
    020bf34c 69c99ce1 MS.Internal.Xml.XPath.ChildrenQuery.Advance()
    020bf35c 69ca6fcc MS.Internal.Xml.XPath.XPathSelectionIterator.MoveNext()
    020bf368 69c83b07 System.Xml.XPathNodeList.ReadUntil(Int32)
    020bf378 69c83ac1 System.Xml.XPathNodeList.get_Count()
    020bf380 69c72736 System.Xml.XmlNode.SelectSingleNode(System.String)
    020bf390 1b9ebe3f Discuz.Cache.DNTCache.RetrieveOriginObject(System.String)
    020bf398 1b9ebde3 Discuz.Cache.DNTCache.RetrieveObject(System.String)
    020bf3c4 1b9ee906 Discuz.Forum.UserGroups.GetUserGroupList()
    020bf3d0 1b9ee853 Discuz.Forum.UserGroups.GetUserGroupInfo(Int32)
    020bf3e0 1b9ea7dc Discuz.Forum.PageBase..ctor()
    020bf484 1b9e9e60 Discuz.Web.UI.Avatar..ctor()
    020bf490 1b9e9deb ASP.tools_avatar_aspx..ctor()
    020bf498 1b9e9dc5 __ASP.FastObjectFactory_app_web_idnpx4j6.Create_ASP_tools_avatar_aspx()

    。。。。。。这里省略若干字。

    ---------------------------

    线程 12 :

    ----------

    0:012> !clrstack
    OS Thread Id: 0x10d0 (12)
    ESP       EIP     
    0213f3b4 7c95847c [HijackFrame: 0213f3b4] 
    0213f3f4 69c6f8be System.Xml.DocumentXPathNavigator.MoveToNext()
    0213f404 69ca37c8 MS.Internal.Xml.XPath.XPathChildIterator.MoveNext()
    0213f40c 69c99ce1 MS.Internal.Xml.XPath.ChildrenQuery.Advance()
    0213f41c 69ca6fcc MS.Internal.Xml.XPath.XPathSelectionIterator.MoveNext()
    0213f428 69c83b07 System.Xml.XPathNodeList.ReadUntil(Int32)
    0213f438 69c83ac1 System.Xml.XPathNodeList.get_Count()
    0213f440 69c72736 System.Xml.XmlNode.SelectSingleNode(System.String)
    0213f450 1b9ebe3f Discuz.Cache.DNTCache.RetrieveOriginObject(System.String)
    0213f458 1b9ebde3 Discuz.Cache.DNTCache.RetrieveObject(System.String)
    0213f484 1c8d2242 Discuz.Forum.Templates.GetValidTemplateList()
    0213f4b8 1c8d2020 Discuz.Forum.Templates.GetTemplateItem(Int32)
    0213f4d4 1b9ea9c2 Discuz.Forum.PageBase..ctor()
    0213f578 1b9efa4a Discuz.Web.UI.TopicPage..ctor()
    0213f584 1b9ef8ca Discuz.Web.showtopic..ctor()
    0213f590 1b9ef7e3 ASP.aspx_2_showtopic_aspx..ctor()
    0213f598 1b9ef7bd __ASP.FastObjectFactory_app_web_ompjorep.Create_ASP_aspx_2_showtopic_aspx()

    。。。。。。这里省略若干字。

    --------------------------- 

    线程 22:

    --------

    0:022> !clrstack
    OS Thread Id: 0x11e8 (22)
    ESP       EIP     
    1c27f1f0 7c95847c [HijackFrame: 1c27f1f0] 
    1c27f230 69ca37c8 MS.Internal.Xml.XPath.XPathChildIterator.MoveNext()
    1c27f238 69c99ce1 MS.Internal.Xml.XPath.ChildrenQuery.Advance()
    1c27f248 69ca6fcc MS.Internal.Xml.XPath.XPathSelectionIterator.MoveNext()
    1c27f254 69c83b07 System.Xml.XPathNodeList.ReadUntil(Int32)
    1c27f264 69c83ac1 System.Xml.XPathNodeList.get_Count()
    1c27f26c 69c72736 System.Xml.XmlNode.SelectSingleNode(System.String)
    1c27f27c 1b9ebe3f Discuz.Cache.DNTCache.RetrieveOriginObject(System.String)
    1c27f284 1b9ebde3 Discuz.Cache.DNTCache.RetrieveObject(System.String)
    1c27f2b0 1c8d2c7b Discuz.Forum.OnlineUsers.GetOnlineAllUserCount()
    1c27f2c4 1c8d1683 Discuz.Forum.OnlineUsers.GetCacheOnlineAllUserCount()
    1c27f2d4 1b9ea930 Discuz.Forum.PageBase..ctor()
    1c27f378 1b9efa4a Discuz.Web.UI.TopicPage..ctor()
    1c27f384 1b9ef8ca Discuz.Web.showtopic..ctor()
    1c27f390 1b9ef7e3 ASP.aspx_2_showtopic_aspx..ctor()
    1c27f398 1b9ef7bd __ASP.FastObjectFactory_app_web_ompjorep.Create_ASP_aspx_2_showtopic_aspx()
    。。。。。。这两省略若干字。

    ---------------------------

    线程 48:

    -------- 

    0:048> !clrstack
    OS Thread Id: 0x1620 (48)
    ESP       EIP     
    1f11eb54 7c95847c [HelperMethodFrame: 1f11eb54] 
    1f11ebac 79357d11 System.String.ConcatArray(System.String[], Int32)
    1f11ebc0 79371853 System.String.Concat(System.String[])
    1f11ebd8 7a4aec86 System.Text.RegularExpressions.Regex..ctor(System.String, System.Text.RegularExpressions.RegexOptions, Boolean)
    1f11ec0c 7a4bf93a System.Text.RegularExpressions.Regex.Split(System.String, System.String, System.Text.RegularExpressions.RegexOptions)
    1f11ec20 1b9ed247 Discuz.Common.Utils.SplitString(System.String, System.String)
    1f11ec2c 1c8d1952 Discuz.Common.Utils.InIPArray(System.String, System.String[])
    1f11ec48 1c8d1869 Discuz.Forum.PageBase.ValidateUserPermission()
    1f11ec54 1b9ea950 Discuz.Forum.PageBase..ctor()
    1f11ecf8 1b9efa4a Discuz.Web.UI.TopicPage..ctor()
    1f11ed04 1b9ef8ca Discuz.Web.showtopic..ctor()
    1f11ed10 1b9ef7e3 ASP.aspx_2_showtopic_aspx..ctor()
    1f11ed18 1b9ef7bd __ASP.FastObjectFactory_app_web_ompjorep.Create_ASP_aspx_2_showtopic_aspx()
    。。。。。。这里省略若干字。

    --------------------------- 


    看上面几个线程,虽然代码不完全相同,但是有几个地方是一样的,标红的地方就是, 上面标红的几个线程都执行到了 

    Discuz.Cache.DNTCache.RetrieveObject 这里面来,类似这样的代码,这10个线程里面有6个,不再一一列出,有2个没有跟踪到执行动作,另外2个线程在做其他事情(上面的48就是其中一个),我们现在只重点来关注这6个线程,因为她们都同时执行到了 
    Discuz.Cache.DNTCache.RetrieveObject ,我认为这里是存在性能问题的,来看看他的源码是怎么样的。
     1         /// <summary>
     2         /// 返回指定XML路径下的数据项
     3         /// </summary>
     4         /// <param name="xpath">分级对象的路径</param>
     5         /// <returns></returns>
     6         public virtual object RetrieveOriginObject(string xpath)
     7         {
     8             if (applyMemCached)
     9             {
    10                 //向缓存加入新的对象
    11                 return cs.RetrieveObject(xpath);
    12             }
    13             else
    14             {
    15                 XmlNode node = objectXmlMap.SelectSingleNode(PrepareXpath(xpath));
    16                 if (node != null)
    17                 {
    18                     string objectId = node.Attributes["objectId"].Value;
    19 
    20                     return cs.RetrieveObject(objectId);
    21                 }
    22                 return null;
    23             }

    24         } 

    再看看 

    objectXmlMap.SelectSingleNode 这里是怎么实现的, 请出 reflector
    public XmlNode SelectSingleNode(string xpath)
    {
        XmlNodeList list 
    = this.SelectNodes(xpath);
    if ((list != null&& (list.Count > 0))
        {
            
    return list[0];
        }
        
    return null;
    }

      

    再看 

    this.SelectNodes(xpath);
    public XmlNodeList SelectNodes(string xpath)
    {
        XPathNavigator navigator 
    = this.CreateNavigator();
    if (navigator == null)
        {
            
    return null;
        }
        
    return new XPathNodeList(navigator.Select(xpath));
    }

     

      

    挺复杂的,再看 

    CreateNavigator
    public virtual XPathNavigator CreateNavigator()
    {
        XmlDocument document 
    = this as XmlDocument;
        
    if (document != null)
        {
            
    return document.CreateNavigator(this);
        }
        
    return this.OwnerDocument.CreateNavigator(this);
    }



     

    哦,到这里看明白了, SelectSingleNode 经过了如下流程最终得到了结果: 

    创建 XmlDocument ——》 创建 XPathNavigator ——》 获取所有的节点Nodes ——》 返回第一个节点 

    这样一个流程下来会耗多少性能呢,这个每个人的测试方式不一样,所以结果可能也会不一样,感兴趣的童鞋可以自己测试一下,因为我不熟悉这种缓存方案,无法预知他的性能,我只好改成自己熟悉的,最后用的系统自带的HttpRuntime.Cache来做缓存。 

    改了缓存策略的效果还不错,cpu从之前的 >80% 降到了 <30%,抓个图:

     

  • 相关阅读:
    Java 开发必装的 IntelliJ IDEA 插件
    paoding-rose 之 maven配置
    jetty之maven配置
    paoding rose controller包及文件名命名规则
    PowerMock进行mock测试
    谈谈我的windows服务器运维管理
    谈谈RPC中的异步调用设计
    系统:WSL(win10的子系统Linux)
    信息安全攻防基础
    线性规划计算工具Lingo
  • 原文地址:https://www.cnblogs.com/gezifeiyang/p/2084228.html
Copyright © 2011-2022 走看看