漫游测试之性能测试(4.8通过监控发现的一个案例)

很早以前在《51测试天地》发表的一篇关于windows平台上面C#的性能问题分析的文章:

前端时间测试一个系统的性能状况,其主要业务的HTTP请求内容在Loadrunner中的代码为:

web_url("Index_3",

"URL=http://192.168.102.43:8003/Home/Index?token={token}",

"TargetFrame=",

"Resource=0",

"RecContentType=text/html",

"Referer=http://192.168.102.43:8001/home/UserIndex?token={token}",

"Snapshot=t12.inf",

"Mode=HTML",

EXTRARES,

"URL=GetUserData/?opkey=GetTypeList", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=GetUserData?info=from+shortcutBar&opkey=GetToolBarRes", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=GetUserData?pageIndex=1&pageSize=32&info=from+loadScrnData&opkey=GetResListByUser", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon_seek.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon_subscription.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon_resManage.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon_delete.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/toolbar_repeat.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/toolbar_left.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/toolbar_right.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=http://192.168.102.43:9090/20131218/Thumbnail/docx_ec030663-c741-4067-9929-1bc5e8898d60/thumb.jpg", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/bg108.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=GetUserData?pageIndex=2&pageSize=32&info=from+loadScrnData&opkey=GetResListByUser", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

"URL=../imgs/icon_corner.png", "Referer=http://192.168.102.43:8003/Home/Index?token={token}", ENDITEM,

LAST);

在100并发下持续略5分钟时,Loadrunner出现以下错误提示,随后应用服务器崩溃:

Action2.c(11): Error -26612: HTTP Status-Code=500 (Internal Server Error) for http://192.168.102.43:8003/Home/Index?token=66e279bb2fc34c28ae119cdb26abab0b

查看Errors Per Second可以看到在所有用户加载后,约5分钟出现了大量的错误。

查看Throughput从06分钟后,几乎服务器不再处理请求了。

查看服务器的CPU使用资源情况在05:30的时候资源使用达到了一个峰值,随后降低了下来。

查看可用物理内存,压力开始后内存持续下降,崩溃后内存进行了回收,内存最小的时候仍有10592MB,内存资源充足。

综上,怀疑是CPU方面的峰值时产生了连锁影响,导致出现了相关的问题。

出现CPU问题时,首先考虑本程序是否为强算法型应用。本程序从业务实现的技术角度来看属于纯内存型应用,并且相关的数据操作都存在内存中,所以首先可以排除是CPU本身的问题。

其次考虑是否为内存不足引起。从上图的数据来看,内存是充足的,所以内存方面不是引起该问题的主要原因。

那么剩下的就只有可能是由于IO或者Thread方面引起的现象,从上面的介绍可知,本程序是内存型应用,所以IO可以排除,目前最有可能是Thread方面的原因引起,添加性能计数器,进行再次测试监控,发现Thread是如下发展:

从05:30至07:30这2分钟内,线程出现急降急升的状态,线程使用和回收不正常。通常来说,线程使用正常的情况下应该如下所示。

从上面的系列信息,我们目前所知的问题的初步线索为:

结论:100并发访问业务请求5分钟后出现崩溃,系统的稳定性不足。

出错请求:http://192.168.102.43:8003/Home/Index?token={token}

出错原因:Thread的使用和回收极有可能存在重大嫌疑。

 

至此,Loadrunner能告诉我们的只有这么多,但是具体的原因倒底是什么呢?我们还能够继续么,做为测试人员我们被告诫需要尽最大的努力找到程序出现错误的最短路径,所以让我们继续,查看原代码吧。一路追查,发现发出请求的主要是这个方法的这部分,但这个方法似乎很简单,里面只使用了一个方法client.GetAsync(apiurl)。

public void OnAuthorization(AuthorizationContext filterContext)

{

            //略

            HttpClient client = new HttpClient();

            string apiurl = ConfigHelper.MasterSiteUrl + "/api/User?token=" + (!string.IsNullOrEmpty(token) ? token : user.Token);

            client.GetAsync(apiurl);

}

查看MSDN中对于HttpClient相关方法的解释,见下:

Name

Description

GetAsync(String)

Send a GET request to the specified Uri as an asynchronous operation.

GetAsync(Uri)

Send a GET request to the specified Uri as an asynchronous operation.

GetAsync(String, HttpCompletionOption)

Send a GET request to the specified Uri with an HTTP completion option as an asynchronous operation.

GetAsync(String, CancellationToken)

Send a GET request to the specified Uri with a cancellation token as an asynchronous operation.

GetAsync(Uri, HttpCompletionOption)

Send a GET request to the specified Uri with an HTTP completion option as an asynchronous operation.

GetAsync(Uri, CancellationToken)

Send a GET request to the specified Uri with a cancellation token as an asynchronous operation.

GetAsync(String, HttpCompletionOption, CancellationToken)

Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation.

GetAsync(Uri, HttpCompletionOption, CancellationToken)

Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation.

Asynchronous中文含义,异步。即GetAsync(uri)方法是一个发送异步请求的方法,显示异步将会使用到Thread来进行,难道是这个方法本身出了问题么?

先不要着急,我们继续看MSDN对于HttpClient的介绍,我们可以发现这么一段示例代码:

static async void Main()

{

      // Create a New HttpClient object.

      HttpClient client = new HttpClient();

      // Call asynchronous network methods in a try/catch block to handle exceptions 

      try

      {

         HttpResponseMessage response = await client.GetAsync("http://www.contoso.com/");

         response.EnsureSuccessStatusCode();

         string responseBody = await response.Content.ReadAsStringAsync();

         // Above three lines can be replaced with new helper method below 

         // string responseBody = await client.GetStringAsync(uri);

         Console.WriteLine(responseBody);

      }  

      catch(HttpRequestException e)

      {

         Console.WriteLine("\nException Caught!");

         Console.WriteLine("Message :{0} ",e.Message);

      }

      // Need to call dispose on the HttpClient object 

      // when done using it, so the app doesn't leak resources

      client.Dispose(true);

   }

哈哈,似乎问题找到了所在,我们的程序没有调用Dispose方法释放HttpClient对象。

// Need to call dispose on the HttpClient object 翻译:需要调用disponse方法释放HttpClient对象

// when done using it, so the app doesn't leak resources 翻译:不使用它,有一些应用不会释放资源

问题是.Net不是存在拉圾回收机制么?client对象按道理应该进行自动回收,不应该出现这个问题。

抱着这样的疑问,让开发修改代码为,并执行相同的测试。

public void OnAuthorization(AuthorizationContext filterContext)

{

       //略

HttpClient client = new HttpClient();

string apiurl = ConfigHelper.MasterSiteUrl + "/api/User?token=" + (!string.IsNullOrEmpty(token) ? token : user.Token);

client.GetAsync(apiurl);

client.Dispose();

client = null;

}

系统业务响应时间缓存加载后稳定,持续运行了1个小时。

流量也稳定,没有出现原有急速下降的现象。

查看Thread使用情况,呈波动发展,有使用也有回收,表现良好。

至此可以证明,性能问题已被解决。

那么回到为什么垃圾回收机制没有起作用呢?网上有较多文章进行了解释,其中下面的内容是主要原因:

首先,系统将托管堆内所有的对象视为可以回收的垃圾,然后系统从GCRoot开始遍历托管堆内所有的对象,将遍历到的对象标记为可达对象,在遍历完成之后,回收所有的非可达对象,完成一遍垃圾收集。

注意,托管堆的垃圾收集只会自己收集托管对象

由于在执行完垃圾收集之后,托管堆中会产生很多的内存碎片,导致内存不再连续,因此在垃圾收集完成之后,系统会执行一次内存压缩,将不连续的内存重新排列整齐,变成连续的内存。(关于垃圾收集的详细信息,大家可以参考《CLR Via C#》)

通过上面的简述,大家都知道什么样的对象不会被收集,即能从GCRoot开始遍历到的对象。

最常见的GCRoot是线程的栈,线程的栈里面通常包含方法的参数、临时变量等。另外常见的GCRoot还有静态字段、CPU寄存器以及LOH堆上 的大的集合。因此,如果想要让托管对象的内存顺利的释放,只需要断开与跟之间的联系即可。而对于非托管对象的内存,必须进行手动释放。

非托管对象无论在什么时候,都不会被垃圾收集所回收,必须手动释放

在.net内存泄露的原因当中,事件占据了非常大的一部分比例,事件是一种委托的实例,也就是与我们类中其他的字段一样,也是一个字段。

我们查看msdn可以了解到HTTPClient有这么一段解释:

By default, HttpWebRequest will be used to send requests to the server. This behavior can be modified by specifying a different channel in one of the constructor overloads taking a HttpMessageHandler instance as parameter. If features like authentication or caching are required, WebRequestHandler can be used to configure settings and the instance can be passed to the constructor. The returned handler can be passed to one of the constructor overloads taking a HttpMessageHandler parameter.

至此也就解释了为什么大并发下会出现这个问题。

 

 

 

某网站性能测试用例 某网站提供会员模板下载、上传、购买、支付等功能,目前进入性能测试阶段,通过性能需求可以了解到主要有以下几个性能指标需要进行测试:   ● 产品页面刷新性能   ● 产品上传性能   ● 产品下载性能   目前给出的指标为:   延迟:   测试项 响应时间 抖动 备注   产品页面刷新 <5秒 <2秒   产品下载相应时间 <4秒 <2秒   吞吐量:   编号 项 吞吐量   Perf.T.1 所有登录用户在线状态更改频率 每10分钟1次   Perf.T.2 每日页面平均访问量 60000次   Perf.T.3 每日下载量 50000   Perf.T.4 平均每日新增会员数量 500   Perf.T.5 高峰同一模板下载量 100用户并发下载   Perf.T.6 高峰不同模板下载量 150用户并发下载   容量:   编号 项 容量   Perf.C.1 用户数 <=100万   Perf.C.2 活动用户数 10000   Perf.C.3 模板中心总用户数 <=25万   根据如上性能需求及数据我们该如何设计性能测试用例及场景呢?(可以说给出的性能需求很垃圾,没有丝毫价值,但没办法还是点做啊)   首先,我不去在乎它要求的性能是什么,我只需要去做在一定的测试环境下对系统进行压力测试,找到各个性能指标的临界点就好了,至于是否达到性能指标,在和性能需求对照编写测试报告即可。   所以,针对这几个需要进行性能测试的页面,我们做一下分析,如何设计场景才能尽可能准确地体现出系统的性能:   先说一下搜索页面   搜索页面根据对项目的了解,搜索后,将所有符合条件的结果遍历出来,显示在前台,每页的显示数量是一定的,超出的部分分页显示。根据上面的描述我们可以看出搜索结果是在将符合条件的所有结果集均发送到前台页面,对于页面显示对性能的消耗我们可以忽略不计,主要的压力来自数据的传输、sql的执行及应用服务器的处理过程,所以我可以从两个方面设计场景:   a、虚拟用户一定,不同数据库数量级的情况下,搜索的性能   如何确定虚拟用户的数量成为一个关键,我们可以让客户提供一个常规情况下每天访问用户数(如果没有实际数据可参考,可以根据产品方案中期望的用户数来代替),我们就用这个用户数来进行测试;再来分析一下不同的数据库数量级,如果系统运营1年的产品数据量是5万条,那么我们就根据这个值分别取1W条、3W 条、5W条、10W条、20W条数据量来进行测试(具体的分法可以根据实际情况而定),所以对于这个测试目标,我们可以设计5个场景进行:   虚拟用户数 数据库数量级 录制页面 并发用户数执行时间思考时间   100 10000 搜索页面 随机产生 30分钟 加入思考时间   100 30000 搜索页面 随机产生 30分钟 加入思考时间   100 50000 搜索页面 随机产生 30分钟 加入思考时间   100 100000 搜索页面 随机产生 30分钟 加入思考时间   100 200000 搜索页面 随机产生 30分钟 加入思考时间   b、一定数据库数量级,不同量虚拟用户的情况下,搜索的性能   我们定下来一个常规的数据库数据量,在数据量不变的情况下逐步增加虚拟用户数,测试一下不同虚拟用户压力下系统的性能   虚拟用户数 数据库数量级 录制页面 并发用户数执行时间思考时间   50 50000 搜索页面 随机产生 30分钟 加入思考时间   80 50000 搜索页面 随机产生 30分钟 加入思考时间   100 50000 搜索页面 随机产生 30分钟 加入思考时间   120 50000 搜索页面 随机产生 30分钟 加入思考时间   150 50000 搜索页面 随机产生 30分钟 加入思考时间   产品上传   影响上传性能的主要因素有上传文件的大小和上传的请求数,所以我们就从这两个方面设计用例。   a、虚拟用户数一定,上传不同大小的文件   虚拟用户数 上传文件大小 录制页面 并发用户数 执行时间 思考时间   50 100k 上传页面 随机产生 30分钟 取消思考时间   50 300k 上传页面 随机产生 30分钟 取消思考时间   50 500k 上传页面 随机产生 30分钟 取消思考时间   50 800k 上传页面 随机产生 30分钟 取消思考时间   50 1M 上传页面 随机产生 30分钟 取消思考时间   b、上传文件大小一定,不同量的虚拟用户   虚拟用户数 上传文件大小 录制页面 并发用户数执行时间思考时间   20 300k 上传页面 随机产生 30分钟 取消思考时间   50 300k 上传页面 随机产生 30分钟 取消思考时间   80 300k 上传页面 随机产生 30分钟 取消思考时间   100 300k 上传页面 随机产生 30分钟 取消思考时间   产品下载   影响下载性能的主要因素有下载文件的大小和下载的请求数,所以我们就从这两个方面设计用例   a、虚拟用户数一定,下载不同大小的文件   虚拟用户数 下载文件大小 录制页面 并发用户数执行时间思考时间   50 100k 下载页面 随机产生 30分钟 取消思考时间   50 300k 下载页面 随机产生 30分钟 取消思考时间   50 500k 下载页面 随机产生 30分钟 取消思考时间   50 800k 下载页面 随机产生 30分钟 取消思考时间   50 1M 下载页面 随机产生 30分钟 取消思考时间   b、下载文件大小一定,不同量的虚拟用户   虚拟用户数 下载文件大小 录制页面 并发用户数 执行时间 思考时间   20 300k 下载页面 随机产生 30分钟 取消思考时间   50 300k 下载页面 随机产生 30分钟 取消思考时间   80 300k 下载页面 随机产生 30分钟 取消思考时间   100 300k 下载页面 随机产生 30分钟 取消思考时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那美那美

失业了,写文章求吃碗炒面

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值