关于小步前进
Sep 20, 2010
在过去的一周中,主要是跟凯哥一起做CurveBuilder这边的性能测试,同时修复已经测试出来的bug和不完善的地方.对于性能测试这一块,测试的计划是这样子,首先准备好数据(后面会讲到测试数据的这样一个台阶或者说分水岭),然后对于几个主要页面,使用Apache Benchmark来测试这几个页面的响应的时间.首先需要面对的问题是如何比较合理的划分前提准备数据的层次,在项目中我们有表EventSource它的数据量是比较稳定同时也比较接近用户真实环境中情况,同时也有Favorites,Alarms和FormulaVersions这三个表,他们的数据的层次就显得很有必要, 势必要按照这样一个基本阶梯来 : 一开始是数据量基本符合用户的正常使用的通常情况;第二是数据量是用户可能会达到,但是相对所占比列不是很大 ;最后便是认为用户基本上不会达到,或者很少很少的情况下达到,可以认为这是极限测试. 通过凯哥跟客户的交流 ,进一步确定了测试数据阶梯的这样一个划分,分别是500,1000和2000. 然后使用Apache Benchmark进行对服务器能处理请求容量或者速度的一个测试,分别有三个类似的阶梯划分,一个是50个用户30个并发程度,100个请求50个并发程度和500个请求100个并发程度.那使用ab的话,因为我们使用的是本地认证,所以我们必须绕过它的限制,所以需要有针对性的进行配置. 紧接着写了一个Ruby脚本来请求每一个页面,并对返回的结果做一个简单的处理.
一开始的时候我们并没有直接上ab ,而是现在本地上跑看看速度处理怎么样, 在Firefox下速度显示超快,但是在IE8发现显示Favorite 的页面速度超慢,不知道为什么,然后凯哥就开始一步步检查这个瓶颈在哪里出现的(你可以能会问题为什么用什么IE,不以MOSe为标准或者怎么样,我得提示说这个是客户的要求,人家就是要求说IE8,所以客户是G .O .D).首先想到的是在"" 层下的从数据库取出数据的速度很慢,然后写了测试发现速度还是可以的,证明取出数据是非常快,然后我们想说那应该就是View层render的非常慢,然后我们一点一点将显示的东西去掉,看看速度有没有提.果然我们发现这一段代码
<% bool oddEven = true; foreach (var item in presenters) { %> <tr" : "odd"%>"> ..... <td> <%using (Html.BeginForm("Delete", "Favorite", FormMethod.Post, new { @id = "favorite_form_" + item.SourceId })) { %> <%=Html.ActionLink("Remove from favorites", "#", "Curve", new { @id = "favorite_link_" + item.SourceId })%> <%=Html.Hidden("curveSourceId", item.SourceId)%> <%} %> <script language="javascript" type="text/javascript"> $('#favorite_link_<%= item.SourceId %>').click(function() { $('#favorite_form_<%= item.SourceId %>').submit(); return false; }); </script> </td> </tr> <%}%>从代码中知道这段Javascript是会将Get方法的请求转换成为一个Post的请求,但是它是位于一个大的的foreach的循环中,有问题没? 在速度上对于Firefox完全是没问题的,但是对于IE8确实非常大的问题。凯哥通过google发现说IE8的Javascript Engine是非常慢的,直接导致整个一直都处于白板状态,因为它没有解析Javascript完,所以整个文档也都没有渲染。所以改进方案是将这段javascript抽取出这样一个foreach循环,同时使用JQuery的ready函数在文档装载完成之后去把此段脚本所在函数注册到其之上。
$(document).ready(function() { $('.rm_fav_link').each( function(elem, value) { var id = $(value).attr('id') var sourceId = id.split('_')[2] $('#favorite_link_' + sourceId).click(function() { $('#favorite_form_' + sourceId).submit(); return false; }) } ); });紧接着将其他有这样问题的页面都有做一些改动,速度有了一定的改观。 接下来的遇到的问题是当我使用ab去测试(中间还有使用JMeter有测试)时,发现这个即使是一个请求没有并发时这个速度也是超慢,一个页面的响应时间在四五秒左右,有时候更高,而且ab运行的结果非常不稳定,落差非常大,可能你是上次得到是非常短的一个响应时间,但这次你在尝试时可能时间却相当的夸张。单问题是我们从本地的浏览器中访问,它的速度是不错的,所以这很困扰。于是便猜想说是不是每一个请求过来都有一个单独的session在Hibernate中了? 如果是,接下来的请求应该是越来越慢的这个事实也符合这个猜想,因为它们之间没有共享或者什么互动。如果使用二级缓存,是不是能在多个请求中共享或者cache了,这样不就可以加快响应时间。 于是就马不蹄停的开始查关于二级缓存的一些资料,这一篇文章很不错 First and Second Level caching in NHibernate , 因为我很久没有使用过hibernate,有些东西都有些生疏。 看过这些资料,接着开始加入二级缓存的这样一个支持,但是加入过程中却发现了点问题,比如NamedQuery无法被Cache,更重要的是从ab的数据来看它的效果没有多大,几乎没有。从本地测试来看,二级缓存确实有作用,因为第二个请求过来确实是比第一个节省了一百多毫秒,但是相对显示的所需要的时间,这个节省简直微不足道,而且引用了新的复杂因素,有些测试都已经失败了。权衡利弊,决定放弃使用二级缓存。 今天可算是一个突破,原来经过优化的代码,当我使用ab去测试时,这个结果都有点令人不敢相信的荒谬。然后凯哥说不可能啊,然后跑到他的机子上去试,结果发现结果比我这边可是要快很多。结果一比较,凯哥说我使用的是局域内部的无线,而且经过加密,所以这个传输中损耗有点大,但是他使用的是有线,明显快的非常多,而且客户使用是光纤,那么可以乐观的表示那个时候得测试数据比现在可是还要好。 解决这个令人头痛的问题后,便开始了使用ruby脚本来将ab测试结果进行一些格式化和处理,弄了一上午,在将近吃中午饭时搞定了。但是在我即将提交代码时,我犯了很大的低级错误。我首先看到它的名字可以取得更好点,然后我就重名了下;然后看hg st看到很多打问号的,很多都是临时数据是需要删除的,然后我稀里糊涂地运行了下hg purge. 悲剧啊,一上午的心血全空气了。只能在下午发了点时间去回想重新弄好,这个过程花了很多时间。 下午在处理数据的时候,我冒失的跑到curvebuilder那边又是改文件又是卸掉安装包又是重新打安装包,弄了好久,结果凯哥跑过来说为什么不直接修改数据库了,一两条sql就搞定了啊。我只能是发出一些"WOW",然后三两下就完成了,但是因为这两件事使得快下班时我们的性能测试报告不得不延时了。 所以这个细心很重要,下手之前先动脑,像我这种靠直觉来删除的方式是不对的。同时思维方式有时候也需要去改变,确保下手之前你的方案是简洁明了是非常重要的。