分类 技巧 下的文章

通过FiddlerScript实现根据条件重发请求

Fiddler是个强大的Web调试工具,具体的功能不在此多述,可以参考后面的链接以及Fiddler官网的手册。本文主要介绍Fiddler的重发请求功能,并通过自定义脚本实现根据条件来重发请求。 在进行Web调试时,经常会遇到浏览器请求正常但是程序请求异常的情况,这时我们常常需要使用Fiddler对比这两个请求的异同,然后将一个请求改变参数或HTTP头进行重发来查看返回结果的差异,这样可以确定哪个参数或哪个HTTP头导致的问题。如下图重发可以有多种不同的选择,常用的有三个:

  1. Reissue Requests: 直接重发选定请求
  2. Reissue and Edit: 重发选定请求,并在请求之前断点,可以对请求进行修改
  3. Reissue from Composer: 将选定请求送到Composer窗口,和将请求拖拽到Composer效果是一样的,在Composer窗口中可以对请求有更精确的控制

replay

只简单的重发指定请求,或在指定请求上进行编辑往往是不够的,在项目中我们偶尔会遇到这样的情形:先发送请求A,然后根据请求A结果中的某个值来发送请求B,譬如有这样的两个接口:get_random_server.php接口通过接收的数据随机返回一个服务器ID,get_data.php接口则根据刚刚的服务器ID来获取数据。下面是一个示例:

  1. localhost/get_random_server.php?data=Hello -> 返回JSON结果:{ success: true, sid: 2 }
  2. localhost/get_data.php?sid=2

这个时候Fiddler的可扩展性就能大显神威了,可以通过两种方式实现Fiddler的扩展:FiddlerScript和插件机制,这里使用FiddlerScript就足够应付了。在Fiddler的菜单项Rules中选择Customize Rules...就可以打开Fiddler的自定义脚本文件CustomRules.js,该脚本一般保存在\Documents\Fiddler2\Scripts目录下。我推荐使用Fidder ScriptEditor进行脚本的编辑,Fidder ScriptEditor具有语法高亮和语法检查的功能,并在右侧面板提供了Fiddler内置的函数列表。 通过展开浏览右侧的函数列表,就基本上可以大概的了解到几个可能会用到的函数了:

  1. FiddlerApplication.oProxy.SendRequest
  2. FiddlerApplication.oProxy.SendRequestAndWait
  3. FiddlerObject.utilIssueRequest

我们先通过下面的代码来练练手,将下面的代码拷贝到CustomRules.js中并保存,Fidder ScriptEditor会自动检查语法错误,并重新加载脚本,无需重启Fiddler脚本即可生效。CustomRules.js使用的是JScript.Net语法,对于Javascipt或.C#程序员应该可以很快上手。这时在Fiddler中随便选择一条请求,点击右键,会发现最上面多了一个选择项Test Send Request,选择该项可以达到和Reissue Requests同样的功能,重发指定请求。

    public static ContextAction("Test Send Request")
    function SendRequest(oSessions: Session[]) {
        
        if (oSessions.Length == 0) return;
        FiddlerApplication.Log.LogString("Sending...");
        
        var selected: Session = oSessions[0];
        
        var oSD = new System.Collections.Specialized.StringDictionary();
        var res = FiddlerApplication.oProxy.SendRequestAndWait(selected.oRequest.headers, selected.RequestBody, oSD, null);
        FiddlerApplication.Log.LogString("Request has been Send.");
        FiddlerApplication.Log.LogString(res.GetResponseBodyAsString());
    }

SendRequest/SendRequestAndWait函数有一个不方便之处,他的两个参数oHeadersarrRequestBodyBytes分别是HTTPRequestHeadersByte[]类型,为了调用这个方法必须将HTTP的header和body转换为这两个类型,不如字符串来的简便。这个时候utilIssueRequest函数正好满足我们的定制需要,可以精确的控制一个请求的细节,类似于Composer中的Raw。下面的代码是一个使用utilIssueRequest函数的实例,具体的HTTP请求以字符串的形式拼接起来。

    public static ContextAction("Probe this!")
    function ProbeSession(oSessions: Session[]) {
        
        if (oSessions.Length == 0) return;
        FiddlerApplication.Log.LogString("Probing...");
        
        var selected: Session = oSessions[0];
        var raw = "";
        
        // methods
        var method:String = selected.RequestMethod;
        var url:String = selected.fullUrl;
        var protocol = "HTTP/1.1";
        FiddlerApplication.Log.LogString(method + " " + url + " " + protocol);
        raw += method + " " + url + " " + protocol + "\r\n";
        
        // headers
        for (var i:int = 0; i < selected.oRequest.headers.Count(); i++) {
            var header = selected.oRequest.headers[i];
            FiddlerApplication.Log.LogString(header);
            raw += header + "\r\n";
        }
        
        // body
        FiddlerApplication.Log.LogString("---");
        var body = selected.GetRequestBodyAsString();
        FiddlerApplication.Log.LogString(body);
        raw += "\r\n" + body;
        
        FiddlerObject.utilIssueRequest(raw);
        FiddlerApplication.Log.LogString("Request has been Send.");
    }

HTTP请求的格式如下:

POST http://localhost/get_random_server.php HTTP/1.1
Host: localhost
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

data=%E4%BD%A0%E5%A5%BD

后面的工作就水到渠成了,通过SendRequestAndWait获取请求A的结果,解析请求A结果获取sid参数,然后拼接HTTP请求调用utilIssueRequest函数,此处从略。

参考

  1. FiddlerScript Editor
  2. Search Sequential Pages for Target String
  3. HTTP协议详解
  4. Fiddler 教程
扫描二维码,在手机上阅读!

WebRequest.Create报异常:The specified registry key does not exist

在Visual Studio中调试网络程序时经常报下面这样的异常,异常的类型是IOException,内容为The specified registry key does not exist,展开里面的详情更是一头雾水,不知道到底是什么错误,而且这个异常只会报一次,后面的请求都是正常。

io_exception

今天忍不住Google了一把,才明白原来是.Net Framework的一次安全更新(MS12-074)导致的,要避免每次都无厘头的抛这样的异常也很简单,向注册表中添加一条记录LegacyWPADSupport即可。 32位版本:

Registry location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework  
DWORD (32-bit) Value name: LegacyWPADSupport
Value data: 0

64位版本:

Registry location: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework
DWORD (32-bit) Value name: LegacyWPADSupport
Value data: 0

reg

参考:

HttpClient request throws IOException

扫描二维码,在手机上阅读!

WordPress打开速度巨卡的解决方法

自从搭建完了这个WordPress博客站点后就一直觉得首页加载好慢,没怎么仔细深究,毕竟自己一个人写着玩的,大多数都是在后台操作,后台的速度还可以。今天想着把一篇博客分享给同学看看,突然发现页面加载超过了1分钟,实在是让人忍无可忍了。 起先以为是服务器问题,后来在百度上搜索“WordPress打开速度慢”关键词,才发现原来并不是我才遇到这个问题,相当多的WordPress站点都变慢了。而这个问题原因竟然是Google在大天朝被封导致的。由于WordPress使用了Google的公共库和字体库,所有对*.googleapis.com的请求都超时了。 解决方法有两个:

  • 禁用Google字体
  • 使用其他的公共库和字体库

我使用的是360的服务,搜索全站,把googleapis.com全部替换为useso.com,问题解决。

参考

  1. 360网站卫士推出google字体加速方案
  2. wordpress突然变慢,都是googleapi字体惹的祸
  3. 解决 Google 被封导致公共库和字体库无法访问
扫描二维码,在手机上阅读!

Unable to start debugging on the web server 错误

今天在用Visual Studio调试Web程序时,突然出现下面这个错误:

Unable to start debugging on the web server. The Microsoft Visual Studio Remote Debugging Moniter (MSVSMON.EXE) does not appear to be running on the remote computer...

如下图所示:

msvsmon_err

起先以为是VS的问题,重启了好几次,但是点击调试按钮的时候仍然是这个提示信息。百思不得其解,因为这个项目下午的时候还可以调试,一直没有人碰过电脑,没改过代码也没装过软件,到晚上的时候就报错了。
不得己Google之,发现下面的解决方案:修改注册表中的DisableLoopbackCheck项,修改方法见参考链接。 重启电脑后问题依旧,最后努力思索从下午到晚上这段时间都做过哪些操作可能会导致影响。看错误信息,感觉是服务端口被占用之类的问题,但是这段时间确实没有安装或启动过什么服务。然后想到可能是配置文件的错误导致,继而突然意识到晚上在写代码之前,用SVN Update了下代码,查看SVN日志,发现有人提交了*.csproj.user文件,查看这个文件的变更。
老的配置文件如下:

<UseIIS>True</UseIIS>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl></CustomServerUrl>

修改后的代码如下:

<UseIIS>False</UseIIS> 
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://www.test.com/</CustomServerUrl>

原来是因为团队内有人不喜欢VS自带的http://localhost:4738/这个URL,便使用修改hosts文件的方式,将www.test.com指向127.0.0.1,并在VS的Web配置中选择了"Use Custom Web Server",这样URL确实好看了不少。 但是问题是:这个文件的提交直接导致了我浪费了一晚上的时间。
最后在VS的Web配置中改回默认的"Use IIS Express",问题解决(其实参考链接中也有人回答了该解决方法,只是我一开始没有注意到...),如下图所示:

web_server_config

教训总结:

  1. 任何一点小小的操作都可能导致意想不到的问题,要留心自己的每一个操作,并注意检查每个操作可能带来的影响;
  2. 绝对不要提交私人的配置文件到SVN服务器,譬如这里的*.csproj.user文件,应该加到ignore列表中。

参考

VS2010 error: Unable to start debugging on the web server

扫描二维码,在手机上阅读!

使用SVN的8个技巧

1. 使用SVN钩子强制提交注释

一个好的SVN实践是文件提交时要求必须填写注释,并注明相关修改信息,如bug号、任务描述等,内容按照约定编写。这样在后期的代码审核和回溯过程中会非常方便,可以更快的定位到具体代码的修改记录。

所谓SVN钩子就是一些与版本库事件发生时触发的程序,例如新修订版本的创建,或者是未版本化属性的修改。目前subversion提供了如下几种钩子:start-commitpre-commitpost-commitpre-unlockpost-unlockpre-lockpost-lockpre-revprop-changepost-revprop-change,其中我们修改post-commit脚本即可实现强制提交注释的功能。

如下是一个实现强制提交注释的post-commit脚本例子:

REPOS="$1"
TXN="$2"

# Make sure that the log message contains some text.
SVNLOOK=/usr/bin/svnlook
LOGMSG=`$SVNLOOK log -t "$TXN" "$REPOS" | wc -m`

if [ "$LOGMSG" -lt 4 ]
then
    echo "拜托,写点注释吧!" 1>&2
    exit 1
fi

exit 0

2. 创建本地仓库

有时候我们在没有网络的情况下无法连接外网上的SVN仓库,或者没有条件搭建SVN服务,这时我们同样可以使用SVN管理我们自己的代码。我们可以使用TortoiseSVN在本地创建代码仓库,并进行代码的版本管理。具体步骤如下:

  1. 新建一个空文件夹,用于存放本地的代码仓库;
  2. 在这个空文件夹上点击右键 -> TortoiseSVN -> Create Respository here,创建仓库;
  3. 在另一个目录Checkout,本地SVN路径格式类似于:file:///C:Repo

3. SVN命令行操作

在Windows系统中TortoiseSVN是进行SVN代码管理的最佳利器,操作也非常简单方便。但是在一些特殊环境下,熟悉SVN命令行操作也是必须的,譬如想在一些自动化脚本程序中使用SVN的功能。 这里总结一些常用命令如下,更多命令请访问后面的参考链接:

(1) 从版本库获取信息

  • svn help
  • svn info $url
  • svn list
  • svn log
  • svn diff

(2) 从版本库到本地

  • svn [co|checkout] $url $local
  • svn export $url $local
  • svn [up|update]

(3) 从本地到版本库

  • svn import $local $url -m "some comments..."
  • svn add $file
  • svn delete $file
  • svn mv $oldfile $newfile
  • svn rm $url
  • svn [ci|commit]
  • svn revert $file 和 svn revert -R $dir

4. 更换版本比较工具

在进行SVN提交时需要非常谨慎,每次提交之前应先做SVN更新或与资源库同步,要特别注意SVN关于冲突和错误的提示信息。对每个提交的文件进行多次检查,确认它们是不是你真正想要提交的。

在提交的文件上选择Compare with base可以将本地代码和版本库中的代码进行比较,确保修改的内容无误。TortoiseSVN默认使用自带的TortoiseMerge工具进行代码比较,也可以更换其他的代码比较工具:在TortoiseSVN -> Settings -> Diff Viewer选项中找到Configure the program used for comparing different revisions of files,选择External,然后选择你喜欢的比较工具,如:Beyond Compare。

5. SVN服务器迁移

这条其实并不算SVN技巧,但是在我们日常工作中确实经常会遇到这样的情况,想将一台SVN服务器上的仓库迁移到另一台服务器。如果SVN服务器是一台Windows服务器,可以直接将SVN仓库目录复制到新服务器上,然后在新服务器上重启SVN服务即可。如果SVN服务器是linux服务器,可以通过下面的命令操作,将一台服务器上的目录拷贝到另一台服务器。

$ scp -r root@x.x.x.x:/home/svn /home/svn
$ svnserve -d -r /home/svn

在TortoiseSVN客户端,需要更新SVN地址:右键 -> TortoiseSVN -> Relocate...

6. svn:ignore

我们在用SVN提交代码时,常常有一些文件未版本化并且也不想提交,所以在提交时根本不想看到这些文件,譬如类似于Visual Studio工程的bin obj目录。

可以使用 svn propset svn:ignore 命令来将某个文件或目录添加到忽略列表中。可以在下面的链接中找到一些常见的ignore文件:Best general SVN Ignore Pattern?

7. SVN目录结构

Subversion有一个标准的目录结构,如下所示:

svn://projects/
|
+- trunck
+- branches
+- tags

其中,trunk为主开发目录,branches为分支开发目录,tags为tag存档目录(不允许修改)。这几个目录具体怎么使用,svn并没有明确规范,一般有两种方式:trunk作为主开发目录或者trunk作为发布目录。

8. SVN使用原则

  • 代码变更及时提交,避免本地修改后无法修复;
  • 提交前确认代码可编译通过,保证新增的文件同时被提交;
  • 不要将格式修正和代码修改混合提交。修正格式包括增加缩进、减少空格等,如果把这些和代码修改一起提交,很难从日志信息中发现代码修正记录;
  • 每次提交尽量是一个最小粒度的修改,如果一次提交涉及到两个完全不同的功能,那么分两次提交,并在注释中写清楚提交内容;
  • 所有代码文件使用UTF-8格式;
  • 提交的文件必须是开发者公用的程序文件,不要提交私人测试程序、程序缓存、图片缓存、自动生成的文件等。

参考

  1. SVN之使用原则
  2. svn强制写日志和可以修改历史日志(svn钩子的应用)
  3. SVN搭建本地文件版本管理
  4. SVN 命令参考
  5. 关于SVN 目录结构
扫描二维码,在手机上阅读!

Windows+IIS+ASP.NET+SQL SERVER 出现50x错误的解决思路

最近一段时间服务器老是出现500、502或者503错误,由于数据量大了,访问用户增多,这类错误越来越影响正常业务。花了无数的时间排查问题,现做简单总结如下。如果你有新的建议,我会后续补上。 首先需要明确的是出现这类问题肯定是服务器错误,有可能是程序问题,也有可能是系统问题,要根据实际情况进行排查。在我遇到的情形中,我是按下列步骤进行排查的:

1. 首先打开任务管理器,查看内存占用

如果内存占用始终在95%上下,有可能是服务器上某个程序太耗内存导致内存不足。内存问题的排查情况比较多,可能是网站程序有内存泄露,也有可能是某个操作非常吃内存。
在我的服务器上,出现的问题是任务管理器里所有进程占用内存都不多,但是显示内存占用100%,在SQL SERVER中执行下面的SQL,确定是数据库缓存的问题。重启数据库,并为数据库设置最大占用内存可以解决这个问题。

select counter_name, ltrim(cntr_value*1.0/1024/1024)+'G' as memoryGB
from master.sys.dm_os_performance_counters
where counter_name like '%target%server%memory%'
    or counter_name like '%total%memory%'

-- Target Server Memory (KB) 服务器能够使用的动态内存总量。
-- Total Server Memory (KB) 从缓冲池提交的内存 (KB)。注意:这不是 SQL Server 使用的总内存。

2. 查看CPU占用

CPU占用100%同样可能会导致服务器出现50x错误,CPU问题的排查和内存问题一样,需要根据具体进程进行排查。在我的一次排查中,发现sqlservr.exe进程一直占用95%以上的CPU。进入SQL SERVER执行sp_who可以看到连接数很多,而且大多数状态都是suspended。使用下面的SQL语句可以查看当前正在执行的具体SQL。

SELECT
    [Spid] = session_Id,
    ecid,
    [Database] = DB_NAME(sp.dbid),
    [User] = nt_username,
    [Status] = er.status,
    [Wait] = wait_type,
    [Individual Query] = SUBSTRING(qt.text, er.statement_start_offset / 2,
        (CASE WHEN er.statement_end_offset = - 1
              THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
              ELSE er.statement_end_offset
         END - er.statement_start_offset) / 2),
    [Parent Query] = qt.text,
    Program = program_name,
    Hostname,
    nt_domain,
    start_time
FROM sys.dm_exec_requests er
INNER JOIN  sys.sysprocesses sp ON er.session_id = sp.spid
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS qt
WHERE session_Id > 50 /* Ignore system spids.*/ AND session_Id NOT IN (@@SPID)

结果显示正在执行很多相同的SQL,而且都是suspended状态。定位到具体的SQL语句,然后就是优化工作了,为该SQL添加索引后问题解决。

3. 检查IIS应用程序池是否正常运行

有时候IIS的应用程序池会异常退出,也会导致50x错误。应用程序池的退出很大一部分原因都是由于上面两个原因导致,要么是内存爆掉了,要么是CPU爆掉了,从而导致应用程序池崩溃这样的连锁反应。进入IIS->应用程序池,启动相应的程序池问题解决。

4. 检查数据库死锁

通过下面的SQL可以查看设置的死锁超时时间,如果LOCK_TIMEOUT值是-1,意味着如果遇到死锁则进程一直等待资源释放,这样就可能导致50x问题。可以将死锁的超时时间设置为3秒,则可以解决这类问题。具体的死锁原因还需要进一步分析。

SELECT @@LOCK_TIMEOUT
SET LOCK_TIMEOUT 3000

注意:LOCK_TIMEOUT值是程序级的设置(application-level setting),只对当前连接有效。在连接打开后,可以使用SET LOCK_TIMEOUT设置死锁超时时间。不能设置成全局的,如果想设置全局,可以使用CommandTimeout值代替。

参考链接:

1. 查询sqlserver 正在执行的sql语句的详细信息
2.SQL Server 性能调优(cpu)
3. 快速搞懂 SQL Server 的锁定和阻塞
4. SQL Server性能调优:资源管理之内存管理篇(上)

扫描二维码,在手机上阅读!