zoukankan      html  css  js  c++  java
  • 从爬网时候遇到的一个问题看如何找到错误的根源

    前一阵遇到的一个爬网错误,从爬网日志中看到的,结果就是这个网站集几乎所有内容都没有被爬下来。

    后来经分析发现这应该算是一个SharePoint的Bug……

    当然本身不是什么大Bug,知道了之后也很容易规避。这个Bug我后来也发到微博上了,这里主要介绍一下到底如何从一个错误找出具体的原因,也算是授人以渔。

    (如果是你自己的代码出的错,直接附加进程调试就好了,这个不在本文所涉及的范围内)

    涉及到的工具有两种,一个是日志查看工具(我用的是最土的记事本……比较专业一点的可以用微软出的SharePoint Admin Toolkit中的日志查看器),另一个就是反编译工具(我用的JustDecompile,一个免费的反编译工具,Slogan很有意思,叫May the source be with you)。

    我找到这个Bug的具体过程如下:

    表现现象:完全爬网之后搜不到某个网站集的内容(这个网站集是用代码创建的)

    第1步:这种搜不到东西的问题,能直接想到的就是去检查管理中心搜索服务中的爬网日志,果然看到在爬这个网站集的时候遇到了一个错误:

    image

    第2步:这个时候你可以尝试去搜一下这个问题(是的,不要从一开始就尝试自己找到错误根源,除非你能确定这个错误一定是你自己的程序造成的问题,或者你心里有一点模模糊糊感觉到问题的所在,否则合理的利用搜索引擎绝对是个高效率的办法),当然,建议是用英文搜。这个时候用英文版SharePoint的好处就体现出来了,不过我们一般用的都是中文版,这个时候可以先尝试自己把中文给翻译回去,只要翻译得差不太多的、并且这个错误在网上提及比较多的时候,都是能通过搜索找到准确的翻译的(另一种方法是在SharePoint的资源文件里面搜报错的字符串,不过这个比较费时费力,还不一定能找到……)。对于这个问题来说,很遗憾,我木有搜到……

    第3步:查SharePoint日志,一般我都偷懒先找事件查看器,有些问题可能会记录到事件查看器里,可惜这个问题没有。然后就是去找14\LOGS中的日志文件,之所以没一开始就找这个地方,是因为日志是分散在很多个文本文件中的,找起来不是太方便,格式也不是很容易看(微软那个日志查看工具,说实话还是挺慢的……)。

    如果不用现成工具的话,就要先定位到到底是哪个日志文件,方法也比较简单,如果是刚刚发生的错误,那就去找最新的那个日志文件,否则的话根据文件的修改时间间隔,也很容易定位到错误可能会被记录到的那个文件里。

    第4步:找到具体错误。用记事本或者别的什么文本查看工具打开日志文件(好歹这个日志还是纯文本的),找到具体错误位置的方法无非就那么几种:

    最笨的,按照出错时间找,不过如果日志级别设置的比较高的话,一分钟之内会有上百条日志,找起来也不太方便;

    最精确的,按照Correlation ID(中文翻译叫互联ID)搜索,这个玩意儿一般会出现在下面这种SharePoint典型出错页面中:

    image

    如果像本文涉及的错误那样,没有互联ID的话,可以直接搜索错误信息,比如“数据为空。不能对空值调用此方法或属性。”。因为日志中如果涉及到Exception的话,会把完整的错误信息和Call Stack都记录下来,因此也可以找到问题,对于这次的这个问题,就能找到如下信息:

    image

    第5步:再搜索一次(是的,你没看错),因为这个时候我们可以看到更具体的错误信息了。一般我的做法,就是直接搜索那个出错的方法名字,如果错误比较常见的话,也能比较容易找到(也有可能你只能找到有人遇到了同样的问题,但是没人解答,哈哈)。不过,这次搜索依然没找到原因(我不记得我这一步到底干没干了,就暂且假设没找到吧……)

    第6步:定位错误的根源。从上面这个日志中,可以看到整个CallStack里面都没有自己的代码,都是SharePoint自身代码造成的问题。具体再结合同一个Correlation Id(就是日志最后的那个GUID)往前找的话,可以看到错误的起点:

    image

    可以看到,是调用sitedata这个Web Service的时候抛出的一个异常。

    那么到底怎么知道为什么会发生这个异常呢?既然日志中已经给出了完整的Call Stack,那么我们只需要找到出错那个方法,也许就能大概猜测到错误的原因了。

    从上面的日志中可以看到,错误中最后一个和SharePoint相关的方法是Microsoft.SharePoint.SoapServer.SiteDataImpl.GetSiteGroupsXml这个方法,结合前面的起点,可以确定这个就是SiteData那个Web Service中调用的方法。

    第7步:进一步定位。这个时候就要祭出大杀器:反编译器了。为了能够看到具体的原因,我们需要深入到SharePoint的源代码中,一个好消息是,SharePoint 2010的几乎所有dll都是未经混淆的,很容易解读。

    但是,应该反编译哪个文件呢?

    这个时候就需要你对SharePoint稍微低一点的层次有一定了解了,你需要知道Web Service的那个目录“_vti_bin”是一个虚拟目录(如果你不知道的话,你能想起来去IIS里看一眼也行),从IIS里可以很容易看到这个sitedata.asmx的具体路径,是在14\ISAPI目录中。asmx文件中一般都是只记录了一个程序集的信息:

    image

    这个信息虽然很简短,但是也足够我们知道,这个程序集的信息了,程序集的名字叫stssoap。一般情况下,程序集的名称和dll的名称是一致性的,因此我们的目的就变成了找到一个名字叫stssoap.dll的文件。

    到这一步的时候,就你对SharePoint有一些更深入的了解——你需要知道SharePoint的那些dll都分布在哪些地方:大多数dll可以在GAC里找到,当然也有一些其他地方,比如这个stssoap.dll,是在IIS路径(也就是所谓的“80”目录中的_app_bin里面,对了,这个目录中还有一个我经常会看的dll,就是Microsoft.SharePoint.ApplicationPages.dll,也就是layouts中那些配置页面的后台文件)。

    当然,如果你没有这个知识的话,还有一个笨办法:按照文件名做全盘搜索……

    第8步:反编译。用任何一种反编译工具(我用的是JustDecompile)打开这个stssoap.dll,可以很容易看到那个Web Service的后台类,以及出错的那个类:

    image

    然后就看一下具体的出错方法的代码,也就是那个GetSiteGroupsXml:

    image

    方法有点长(也不算太长),我没有贴全,结合日志中具体的错误信息(一个数据库操作的xxx.get_String()方法),我们可以有目的的注意到两个地方:首先,就是调用的那个存储过程和参数(我们很幸运,这个方法是直接用ADO.NET操作的数据库,SharePoint很多底层的方法追踪到最后都是COM+的非托管方法,我们就无能为力了);其次,就是可能出错的那两句话,sqlDataReader.GetString()。

    如果你有经验的话,到这一步已经可以八九不离十地猜到原因了:在GetString的时候,数据库里的那个值是NULL,转换到C#中就是DBNull类型,当你直接GetString的时候,就会发生之前的那个错误。这里面GetString的有两个(当然方法后面还有几个GetString没有贴出来),一个是Name,一个是Description。首先从方法名字和属性名字我们可以猜出来这个方法是获取SharePoint用户组的,用户组的Name和Description有可能是null么?对于我这个情况来说,还真是(因为这个用户组是我用代码创建的,创建的时候,那个Description属性我还真用的是null……)

    谜底揭开!!

    当然,一个好的技术人员都是有好奇心的,既然已经走到这里了,何不接着走下去呢?

    第9步:定位到数据库级别。在内容数据库中,找到那个proc_SecGetAllGroupsAndMembershipInfo存储过程,先来执行一下看看……

    等等,这里面有个参数SiteID,怎么填?回到最早的那个爬网日志,记得那个出错的网站集么……你问我怎么得到网站集的ID?方法太多了……你可以在管理中心跟网站集相关的各种设置页面的Url参数中找到;可以用PowerShell找到;可以写个Console程序找到;可以直接从数据库里找到……随你选择。

    存储过程执行之后的结果:

    image

    啊哈!

    第10步:再深入一点。看看这个存储过程是怎么写的?

    image

    看到了一个叫TVF_Groups_Site的函数,继续看这个函数:

    image

    来自Groups这个表,而这个表:

    image

    嗯,就没什么神秘的了……就是SharePoint存储用户组的表了。

    到这个时候,你就可以摇摇尾巴,考虑要不要把这个像解密一样的过程写成一篇Blog共享出来了。

  • 相关阅读:
    21.Merge Two Sorted Lists 、23. Merge k Sorted Lists
    34. Find First and Last Position of Element in Sorted Array
    leetcode 20. Valid Parentheses 、32. Longest Valid Parentheses 、301. Remove Invalid Parentheses
    31. Next Permutation
    17. Letter Combinations of a Phone Number
    android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事项
    oc 异常处理
    oc 类型判断
    oc Delegate
    oc 协议
  • 原文地址:https://www.cnblogs.com/erucy/p/2686476.html
Copyright © 2011-2022 走看看