原文名称:User Experience, Not Metrics
原文作者:Scott Barber
原文出处:http://www.perftestplus.com/resources/UENM2.pdf
译文名称:基于用户体验的性能测试
翻译:pent
译文地址:http://www.cnblogs.com/pent/archive/2007/07/01/802117.html
第二章 模拟个别的用户延迟
网站的访问者们以不同的速度进行思考、阅读和打字,作为测试过程的一部分,你的责任是解决如何模拟并脚本化这些不同的速度。这些在Rational TestStudio中有许多方法可以实现,你将在《User Experience, Not Metrics》系列的第二章中学习到其中的一些。本系列文章的关注点是将客户满意度和外部用户的性能体验关联起来,这已经在本文的第一章解释过。本文及后续文章将提供必要理论,包括关于如何根据测试目的模拟实际用户,并根据多年的性能测试经验以录制或修改脚本作为例子来论证这些原理。
一些业内的专家称用户延迟的变化和实际测试结果并不相关,当用户众多时即使每个用户活动之间有20秒的延迟也会得到相同的结果。我会争辩说,如果是多年前单服务器架构、静态HTML脚本的情况下我可能会同意,但对于当今多层次、多功能的网站来说这是不可能的了。《科学的网站负载测试》(The Science of Web-site Load Testing)的作者Alberto Savoia对为什么20秒的理论并非事实进行了相当详细解释并提供了真实世界的实例和数学证明。
本文对各级别的TestStudio用户均有帮助,但对中高级用户尤佳。这里我介绍了脚本化不同的数学分布的方法,但你必须自己决定哪种才能最精确地匹配特定的场景。统计学的精确度在最终用户群的模拟上,对于每个网站来说是重要而独特的。
1. 确定用户延迟
用户登录,浏览主页,填写表格等需要多长时间?通过几种方法并结合网站上的用户活动能够大概获取用户延迟时间。最好的方法当然是使用生产现场上收集到的真实数据,但这几乎是不可能的,因为测试通常发生在网站发布投入使用之前。正因为如此,有时需要根据网站的行为做出有根据的推测。最能接受的四种方法如下:
- l 当测试的网站已经投入生产时,你可以从每个页面的日志文件中知道浏览(或打字)的时间,并从中提取出平均差和标准差,并确定实际值和分布状态。有了这个信息,每个页面的用户延迟时间就可轻易确定。你的生产现场也可能拥有能直接提供这类信息的网站流量监测软件,例如WebTrends或LiveStat等。
- l 在日志文件或时间、资源记录缺乏的情况下无法进行详细的日志分析时,你可以利用一些(调查)公司已经收集到的度量和统计数字。这些统计数据来自于用户或站点的客观抽样,提供了平均页面浏览时间和用户会话持续时间。尽管这些数字不是来源于你的特定站点,但是作为近似值来说已经很不错了。图1是来自NetRatings的一个样本数据。
图1 NetRatings提供的样本数据
- 如你所见,这是非常高层次的信息,对模拟实际用户或许有用,也或许没用。图中最有用的信息是“观看页面的持续时间”(Duration of a Page Viewed),报告了用户观看所有类型网页的平均时间。其余的统计信息由浏览网站的庞大的用户群组合统一汇总而成。
- l 假如你没有日志文件,或者认为来自其它公司的度量指标不具备代表性的话,你可以通过员工、客户、朋友或家庭成员们运用简单的内部实验来进行,例如,新用户和老用户的页面观看时间是不同的。这种方法也被认为是“剪贴板和秒表法”。对于还没有投入运行的网站,或者通过其它方法收集到的数据进行验证,我觉得这是一种非常有效的数据收集方法。
- l 最后的手段是通过你的直觉或猜测来评估这些延迟的范围。对于逼真的负载测试来说,即使是这种最后的方法也要比忽略用户延迟的变化好得多。每个用户在每个页面消耗同样时间,这样的负载测试虽然简单,但不逼真,并且会产生令人误解的结果。
2. 了解延迟的范围和分布情况
现在你已经知道了用户在页面上所花的时间及其范围,但还没有最终完成对用户延迟的模拟。你必须使用户的延迟时间多样化,否则服务器将会呈现如图2的负载情况。
图2 条纹式的响应图
在上面的响应图中,每个红点都代表一个用户活动(在这里是页面请求),水平轴是时间,以秒为单位, 从测试开始时计算,垂直轴列出了每个虚拟用户。这个响应图是一个典型的“条纹式”(Striping)请求,这必须在负载测试中尽量避免的。上图是一个压力测试的好例子,但非负载测试。如果你拿一个尺子垂直地放在图上并从左到右缓慢移动,在垂直方向上,你会看到这些代表了用户活动的红点几乎是在同一线上,这也表示了服务器在同一时间接受到的用户请求。这是对实际用户群的非常差的刻划。现在看看图3。
图3 非条纹式的响应图
如果你同样地用尺子来量一量,你会发现这次红点分布得更加均匀一些。虽然还不完善,但明显地更加贴近实际用户的情况。这两个测试运行唯一的区别是后者有了一定的随机延迟,而不是绝对的延迟。
在对上面的两个例子进行有计划地调整之前,有3点信息是必须知道的。在第一个例子中,我只是使用了脚本录制时的平均延迟时间。第二个中,我加入了最小和最大的延迟值以及分布。有很多这种分布的数学模型,我将重点介绍最常用的两个:均匀分布(uniform distributions)和正态分布(normal distributions),为了完整起见同样介绍了负指数分布。这个分布并不经常用到但也不是没用,高级程序员可以将这种分布和其它数学函数结合起来进行某些用户模式的模拟。
介于最小值和最大值之间的平均分布是最容易模拟的。这种分布模型只是简单的从上下边界中选择随机的数字并构成均匀分布,这意味着生成的数据并不会只在数据范围的中间或两边。图4显示了1000个0到25之间的平均分布情况。
图4 最大和最小值之间的均匀分布
正态分布,也称为贝尔曲线,更难以模拟但也更加准确。这种分布模型随机地选择数字,并通过对中心值或平均值的选择频率进行相应的加权。图5展示了在0到25之间产生的1000个数字的正态分布情况(平均值为12.5,标准差为3.2)。在没有可用的实际数据的情况下,正态分布一般都被认为是最准确的数学模型。
图5 理想的正态分布
负指数模型的分布情况如图6所示,这种模型延迟时间的频率明显地向末端歪斜。我觉得这种分布很少有用,但出于完整性考虑这里还是包括进来了。图6显示了在0到25之间产生1000个数字的负指数分布情况。
图6 负指数分布
3. 在TestStudio中生成时间延迟
使用Rational TestStudio的VuC语言,有很多方法可以生成时间延迟。对于如何使用Think_avg及相关的命令和参数来实现,Rational的文档和培训资料已经做了详细的说明,这里不做深入讨论,只做简要介绍。这个方法只有在Think_avg命令不在计时器(Timer)里面或附近时才有效。我发现Think_avg命令更容易代表客户端的处理过程,但对用户模型来说并不合适。关于计时器和客户端处理时间模型将在后续的文章中详细讨论。
当脚本中的用户延迟不包含在计数器中的时候使用delay命令。下面是使用delay命令创建静态值以及均匀、负指数、正态分布以实现用户延迟的讨论。
如果已经明确某些活动消耗了所有用户8秒的操作时间(很少,但有可能),可用以下命令:
delay(8000);
括号中的数值是静态延迟值,以毫秒为单位。此命令的语法是delay(value);,静态延迟一般只用在精确模型中。
如果确定用户在网站上执行活动的时间在6到12秒之间,那么均匀分布能准确地代表实际用户。命令如下:
delay(uniform(6000,12000));
在这个例子中,6000是最小值而12000是最大值,以毫秒为单位。此命令的语法是delay(uniform(min_value, max_value))。
如果确定用户在网站上活动的时间在9到15秒之间,但大部分用户接近9秒而不是15秒,同时没有用户少于9秒,这时使用负指数分布将最准确。典型的例子比方是只有当9秒的短片播放完后,下一步的按钮才会显示。命令如下:
delay(negexp(9000,15000));
在这个例子中,9000是最小值而15000是最大值,以毫秒为单位。此命令的语法是delay(negexp(min_value, max_value))。
在VuC函数中并没有创建正态分布的延迟命令。为此,我们用下列的函数以及delay命令的组合构成了正态分布。注意,normdist函数在每个脚本中必须直接放在#include命令下面。
int func normdist(min, max, stdev) /* specifies input values
for normdist function */
/* min: Minimum value; max: Maximum value;
stdev: degree of deviation */
int min, max, stdev;
{
/* declare range, iterate and result as integers -VuC
does not support floating point math */
int range, iterate, result;
/* range of possible values is the difference between the
max and min values */
range = max -min;
/* this number of iterations ensures the proper shape of
the resulting curve */
iterate = range / stdev;
/* integers are not automatically initialized to 0
upon declaration */
result = 0;
/* compensation for integer vs. floating point math */
stdev += 1;
for (c = iterate; c != 0; c--) /* loop through iterations */
result += (uniform (1, 100) * stdev) / 100;
/* calculate and tally result */
return result + min; /* send final result back */
}
将这个函数以0毫秒为最小值,25000毫秒为最大值,3200毫秒为标准差,执行1000次产生的正态分布如图7所示。需要注意的是,这里使用了与理想正态曲线相同的参数,只是把单位由秒替换成毫秒。大家可以看到,这个曲线图与图5的理想正态分布几乎是一样的。
图7 用normdist函数生成的正态分布
delay函数在正态分布中使用频率最高。可用以下命令来模拟一个10秒到35秒之间,标准差为3.2秒,呈正态分布的用户延迟(对于上面的例子而言,只是将时间右移了10秒):
delay(normdist(10000,25000,3200));
这里10000是最小值而25000是最大值,3200是标准差,均以毫秒为单位。此命令的语法为:delay(normdist(min_value, max_value, std_deviation))。记住,如果脚本里面没有normdist函数的代码将会报错。
4. Now You Try IT
为了证明这些概念的简单和有效,我建议大家跟着下面的练习一起做,前提是大家已经知道如何录制和回放VU脚本,以及如何在录制过程中插入timer。
4.1. 确定用户延迟和分布情况的练习
选择一个完全静态的网站,因为每次都变化的网站只会妨碍学习过程。接着确定一个导航的路径,比如在onblestat.com网站的首页上,点击About Us,然后点击Essentials,最后点击Heritage。首先在记事本上记下你认为每个页面可能的用户分布和延迟时间,然后找一些同事根据纸上的指示进行操作,并记录下他们在这些页面的停留时间。看看实际的时间与分布是否接近于记事本上的记录。
4.2. 在VuC的练习中模拟用户延迟和分布情况
在这个网站的不同测试时间,我用尽了4个方法来确定用户延迟和分布情况。在所有的案例中,根据正态分布曲线获得不同的时间值。出于对于这个例子的考虑,我们假设并非总是如此,这样你才能看到如何使用内置的C函数和我之前介绍的新的normdist函数来产生时间延迟。
我重申一点,这个例子中的延迟和分布并不能很好地代表实际网站交互。我会在这篇文章地讨论中创建练习来论证所有的主题。
首先基于一个静态网站录制一个简单的VU脚本(我用noblestat.com来录制)。录制时用timer将每个页面包括起来。(你也可以用timer block,但录制出来的原始脚本有点差别,我会在第5章详细介绍timer block)。录制3个页面,Home Page、Page1、Page2。
录制完成之后用1个虚拟用户回放一下,确保没有修改之前的脚本回放成功。然后再用Robot打开脚本并找到第一个stop_time命令。这部分的脚本如下:
http_nrecv ["RDN_on_~234"] 100 %% ; /* 238 bytes -From Cache */
stop_time ["Home Page"];
start_time ["Page1"];
set Think_avg = 12342;
/* Keep-Alive request over connection www_noblestar_com */
http_request ["RDN_on_~235"]
这部分代码停止了计时器对加载主页的时间统计,并开始了对加载Page1的计时器统计,再等待了差不多12秒后才开始对Page1的下载。实际上,你并不想将12秒的延迟时间也算在计时器里面的,还有并不希望12秒是静态值,而是一个6秒到18秒延迟范围的正态分布。你可以通过删除或注释掉start_time命令后的set Think_avg这一行,同时在stop_time和start_time命令之间加入delay(uniform(6000,18000))来实现。这段代码显示如下:
http_nrecv ["RDN_on_~234"] 100 %% ; /* 238 bytes -From Cache */
stop_time ["Home Page"];
delay(uniform(6000,18000)); /* added to replace Think_avg below */
start_time ["Page1"];
/* set Think_avg = 12342; -replaced by delay above*/
/* Keep-Alive request over connection www_noblestar_com */
http_request ["RDN_on_~235"]
现在这段代码将统计主页和Page1的真正加载时间,等到用户阅读完主页之后才访问Page1页面,并且等待时间是一个6到18秒正态分布下的随机时间。
找到Page1的stop_time命令,原始代码如下:
http_nrecv ["RDN_on_~243"] 100 %% ; /* 9997 bytes */
stop_time ["Page1"];
start_time ["Page2"];
set Think_avg = 8536;
set Server_connection = www_noblestar_com_1;
/* Keep-Alive request over connection www_noblestar_com_1 */
http_request ["RDN_on_~244"]
和上面一样,这段脚本停止了Page1的计时器,开始了Page2的计时器,并延迟了8.5秒左右的时间。这里,你希望把8秒的静态延迟改为最小值6秒、最大值14秒、标准差2秒的正态分布延迟。如果你没有计算延迟范围标准差的方法,那么将最大延迟时间减去最小延迟时间再乘以25%是一个比较精确的、可以接受的方式。
将normdist函数拷贝到脚本中#include命令的下面,代码如下:
int func normdist(min, max, stdev)
int min, max, stdev; // min: Minimum value; max: Maximum value;
stdev: degree of deviation allowed
{
int range, iterate, result;
range = max ? min;
iterate = range / stdev;
result = 0;
stdev += 1;
for (c = iterate; c != 0; c--)
result += (uniform (1, 100) * stdev) / 100;
return result + min;
}
{
push Http_control = HTTP_PARTIAL_OK | HTTP_CACHE_OK | HTTP_REDIRECT_OK;
然后删除或注释掉start_time命令下面的set Think_avg这一行,并在stop_time和start_time命令之间添加delay(normdist(6000,14000,2000))。这段代码如下:
http_nrecv ["RDN_on_~243"] 100 %% ; /* 9997 bytes */
stop_time ["Page1"];
delay(normdist(6000, 14000, 2000)); /* added to replace Think_avg below */
start_time ["Page2"];
/* set Think_avg = 8536; -replaced by delay above*/
set Server_connection = www_noblestar_com_1;
/* Keep-Alive request over connection www_noblestar_com_1 */
http_request ["RDN_on_~244"]
再次强调,没有normdist函数的话脚本执行将会出错。现在这段代码也能正确的统计Page1、Page2的加载时间了,并且在用户阅读Page1之时有一个随机的正态分布时间。如果你愿意的话,可以检查一下全部修改后的脚本。
5. 高级用户:双峰正态分布
为了应用这种分布,你应该有Rational TestStudio的性能测试经验以及C语言的基本了解。我测试过的很多网站就有些这样的页面,它们的用户访问呈双峰正态分布。想象一个有很多文字的页面,首次访问时用户可能会详细地阅读这些文字,下一次你就会直接的跳过这个页面到下一个页面了。这种页面的用户延迟时间如图8所示。
图8 文字性网页所展现的双峰正态分布
在这个例子中,假设60%的用户花了8秒的时间浏览了这个页面并寻找下一个链接,而其它40%的用户花了45秒的时间详细的阅读了整个页面。你会发现这是两个有着不同的最小值、最大值、标准差的正态分布。在脚本中模拟这种分布的方法生成一个0到10的随机数,并创建一个控制逻辑,当随机数字落在1~6之间时实现左边的延迟,在7~10时实现右边的延迟。
between 1 and 10 */
if (y<=6) /* if 1 through 6 */
{
delay(normdist(0,16000,2000)); /* Delay based on left side hump */
}
else /* if 7 through 10 */
{
delay(normdist(34000,64000,6000)); /* Delay based on right side hump */
}
start_time ["Page2"];
这段代码执行1000次后将呈现图9的数据情况。
图9 我们的脚本生成的双峰正态分布
如你所见,使用同样的方法,利用这段代码就可以轻松的改变分布类型和数值,以及控制每个峰值中不同的用户比例。把这些原理放在一起形成各种不同的组合,就有可能模拟几乎所有用户的延迟分布,并达到相当级别的精确度。举例来说,还可以再添加一个10秒到2分钟的均匀分布来模拟其它15%的用户,而这种可能性是无止境的。
6. 小结
这篇文章给你带来的关键点很简单:模拟的用户越精确,性能测试结果的可信度就越高。精确用户模拟的第一步就是模拟用户延迟。本文讨论了如何确定用户延迟时间,以及如何使用Rational TestStudio把它应用到你的虚拟用户脚本中。接下来的2篇文章将论述如何确定和模拟个别用户网站使用模式,以及如何将这些合并到单个用户组模型中。
7. 附录(单词)
It goes without saying:不言而喻,不用说
make educated guesses:做出有根据的推测
in-house experiments:内部实验
in case:万一
realistic:逼真的
significantly:意味深长地,值得注目地
bell curve:贝尔曲线
negative exponential:负指数
skew:歪斜的
programmatic:有纲领性的,计划性的
in-depth discussion:深入讨论
represent:代表,表示
parenthesis/parentheses:圆括号
jot down:草草记下,摘要记载下来,
co-worker:合作者,同事
come up with:提出,拿出
built-in:内置的,内置
reiterate:重申,重做
bracket:括在一起,括弧,支架
proportion:比例,均衡,面积,部分
返回主题:[翻译]基于用户体验的性能测试