使用 RRDtool 揭示 Web 性能问题
2008-11-13 08:29:17 来源:WEB开发网开始之前
了解本教程中包含的内容以及如何最好地利用本教程。
关于本教程
本文有两方面的目的。首先,它介绍了如何使用 RRDtool 来收集和显示数据。其次,它介绍了如何度量一个基于 Web 的应用程序的性能。虽然这两个概念相互独立,但是同时介绍这两个概念能够帮助您更好地掌握它们。
目标
在本教程中,您将了解如何在 Round Robin Databases (RRD) 中存储数据,以及如何用图形的方式显示这些数据。另外,您将了解如何度量基于 Web 的应用程序的性能,以及如何发现这些性能问题的根源。
先决条件
本教程适合于对 UNIX® 命令行具有基本认识、具有基本的 Shell 脚本知识并且了解基本统计数据(平均值、最小值、最大值)的读者。
系统要求
要学习本教程中的内容,您需要一台运行 UNIX 的计算机和一台 Web 服务器。(这两者可以位于同一台计算机。)另外,您必须安装下列工具:
RRDtool 1.2.12
cURL 7.15.1
这两个程序都遵循标准的编译和安装过程:
在 UNIX 命令行中,运行 tar -xzf filename.tar.gz。
最后这个命令在解压缩的过程中创建了相应的目录。使用 cd dirname 切换到该目录。
运行 ./configure 以创建生成指令。
运行 make 命令来编译源文件。
运行 make install 命令来安装该软件。
您可能还会找到由您的供应商或第三方提供的这些程序的二进制文件。在本教程中,您并不一定需要使用这些软件的最新版本。
解释 Web 的性能
度量和适当地显示这些度量值,对于任何系统的运行都很重要。只有通过客观的度量,您才能够量化改进工作的效果或发现所报告的性能问题的本质。对于 Web 应用程序,可以通过几个因素来控制用户请求的实现速度,并且必须对这些因素进行度量以避免影响结果。
对请求进行剖析
在进行度量工作时,如果能够了解正在发生的事情,并在此基础上确定必须进行度量的对象,那将是很有帮助的。对于一项 Web 请求,会发生下面的处理过程:
用户在浏览器(客户端)中输入统一资源定位符 (URL)、单击链接或提交表单。
客户端查询全球域名系统 (DNS),将服务器名称映射为 Internet 协议 (IP) 地址。对该映射关系进行缓存供以后的请求使用,但是在用户会话期间,它可能经过一段时间后便会过期,并必须重新获取。
客户端建立到远程服务器的传输控制协议 (TCP) 连接,并完成三向握手过程。
客户端向远程服务器发送对特定 Web 页面的请求命令,包括所请求的页面和任何表单数据。
服务器处理该信息并准备进行响应。该响应可以返回一个文件的内容,或运行进行数据库调用或建立到其他系统的连接的程序。
服务器通过 Internet 向客户端发送数据。
确定度量的对象
因为度量 Web 性能的目的是确定客户端对请求的响应度,所以应该从客户端的角度出发来进行度量。要完成该任务,最简单的方法是模拟客户端的请求并度量其结果。另一种替代方法是被动地监视客户端请求,并以这种方式进行度量。尽管后面这种方法显示了真正的客户端体验,但是客户端或者客户端网络连接之间的差异可能会降低度量的客观程度。
从客户端的角度出发,下列的度量是非常有用的:
解析服务器的 DNS 名称所花费的时间。
连接到服务器所花费的时间。
发送请求所花费的时间。
从服务器接收到第一个字节所花费的时间。
从服务器接收到最后一个字节所花费的时间。
您将在本文后面的内容中看到,这些度量中的每一项都会受到不同因素的影响。例如,第四项和第五项的时间差更像是网络速度的一个函数,而第三项和第四项的时间差则通常对应于 Web 应用程序或后端数据库。
进行度量工作
您可以使用 UNIX 的 cURL 工具从脚本或命令行获取 Web 页面。cURL 的特性是,以易于阅读的脚本形式提供关于前面讨论的几种度量的报告。cURL 工具还可以接受命令行中的表单数据,这样就可以更容易地管理动态站点的测试工作。
使用 cURL 可以很容易地生成所需的统计数据,如清单 1 所示。
清单 1. 使用 cURL 来显示 developerWorks 站点的响应度
$ curl -m 60 -w %{time_total}:%{time_namelookup}:
%{time_connect}:%{time_pretransfer}:%{time_starttransfer}
-o /dev/null -s http://www-128.ibm.com/developerworks
1.067:0.388:0.447:0.447:0.691$
在这个示例中,通过使用反斜杠来结束每一行,将长命令分解为若干个较短的行。不同的数字之间使用冒号进行分隔,并且不以一个新行作为结束,即最后的货币符号 ($),它是 Shell 提示符。
可以通过 cURL 的命令行选项改变其行为,以适应该应用程序的需要。-m 60 选项将命令的执行时间限制为 60 秒,这样可以避免脚本挂起的情况。-w %{time_total}:... 选项指定了页面下载完成后打印的消息。扩展表单 %{keyword} 中的项目,以显示前面介绍的度量。-o /dev/null -s 这几个选项的组合,确保除了 -w 选项所指定的数据之外,不会将其他输出结果发送到终端。最后,该命令以一个 URL 的列表作为结束。
清单 1 的输出显示出整个请求花费了 1.067 秒。所有其他的数值都是相对于请求的开始时间。也就是说,DNS 名称解析花了 0.388 秒,建立连接花了 0.447-0.388 = 0.059 秒,而第一个字节到达的时间为 0.691-0.447 = 0.244 秒。connect 和 pretransfer 的值相同,这是因为无法度量发送 Header 所花费的时间。对于 GET 请求,这种行为是很常见的,因为 Header 的尺寸太小了。
RRDtool
要跟踪数据并生成相应的图形,可以使用称为 RRDtool 的工具包。RRDtool 是 Multi Router Traffic Grapher (MRTG) 包中的一部分,而 MRTG 是用于图形化网络流量模式的一种常用的程序。
数据存储方式
RRDtool 使用 RRD 以所谓循环 的格式来存储时间序列数据。换句话说,将对较早数据求平均值,并在有新的数据插入时将其淘汰掉。这种行为的思想是,当查看最近的事件时,这种细粒度的数据(如以五分钟为间隔获取的数据)很有帮助;但当查看较长的历史情况时(如过去六个月获取的数据),那么使用两小时的平均值更合适。RRD 的另一个优点是,持续不断地删除旧的数据。RRD 的大小并没有发生变化,这也意味着,RRD 中的每项度量都具有与之关联的时间戳。不能删除旧项,并且也不能输入超出序列的项目。
您需要使用 rrdtool create filename -s seconds 命令来创建这些 RRD。在创建 RRD 时,您必须对要存储到该数据库中的数据进行描述,而这个数据库涉及到数据源和存档。您还必须提供输出文件的名称以及相继的度量之间的秒数。以后将使用该信息来计算旧数据的合并结果以及掌握缺少数据的情况。
数据源
每个 RRD 都由描述需要存储的特定数据类型的一个或多个数据源组成。例如,DNS 解析时间 就是一个合适的数据源。数据源不应该与特定的实例相关,换句话说,IBM.com 的 DNS 解析 就不是一个合适的数据源。每个 RRD 文件都描述了对应的实例,所以每个被测试的站点都有它自己的 RRD。在经过这样的处理之后,将很难添加新的数据源,所以您需要问问自己,“如果从我的设备列表或被度量的站点添加或删除数据源,那么我需要对 RRD 进行调整吗?”如果答案为“是”,那么您的数据源就可能是实例特定的,并且需要对其进行抽象。所度量的新的项目应该位于不同的 RRD,而不是相同 RRD 中新的数据源。
在定义数据源时,您需要作出的第一个决定是它的名称,其长度为 1 到 19 个字符,可以包括字母数字的混合或者下划线。另外,名称应该描述度量本身,而不是实例。
最重要的决定也许是数据源的类型。每次当您向 RRD 中添加一项度量时,可能需要对相应的值进行一些预处理。例如,路由器上用来度量已传输的字节数的计数器。如果不知道五分钟之前的值为 300,那么当前值 600 本身并没有什么意义。考虑到这一点,那么存储变化率则更有价值,该变化率为 (600-300)/(5*60) = 1/sec。COUNTER 类型的作用正在于此,它可用于连续递增计数的数据源。
另一种数据源的类型是 GAUGE,当您打算存储值而不是比率的时候,可以使用这种类型。这种类型的例子包括当前温度,或在本教程的示例中的,某个特定操作所花费的时间。还有其他几种数据源类型,但是 GAUGE 和 COUNTER 可以处理大部分情况。
定义数据源的语法是 DS:name:type:heartbeat:min:max 。heartbeat 定义了在使用特殊值 UNKNOWN 填充中间数据前,两个度量之间相隔多少秒的时间。对于大部分的使用情况,可以使用度量间隔两倍的时间长度。使用 min 和 max 作为检查输入数据的界限。如果数据超过了这个时间间隔,那么它将存储为 UNKNOWN。对于这两者中的任何一个,在不需要限制的情况下,可以指定为 U。
循环存档
我曾在前面提到过 RRD 平均时间序列数据。例如,查看一个月的数据时将显示两小时的平均值。循环存档 (RRA) 定义了如何对不同的数据源进行平均值计算并保存。每个 RRD 可以定义多个 RRA,并且它们不是数据源特定的。也就是说,如果您定义了一个 RRA 用来存储一个月的两小时平均值,那么对于每一个数据源,都创建了一个 RRA。在显示图形时,RRDtool 将判断必须根据所提供的时间框考虑哪些 RRA。另外,在构建 RRA 时,您必须做出下列决定:
在聚合数据的时候,需要保存平均值、最小值、最大值或最后一个值吗?通过使用单独的 RRA,您可以完成比这些合并函数 (CF) 其中之一更多的工作。
在您声明聚合为 UNKNOWN 之前,可以容忍序列中出现多少个 UNKNOWN 值?这个值可以表示为 UNKNOWN 值与全体值的比率。大致地说,.1 表示每个小时可以丢失一个度量值。这个值称为 XFiles Factor 或简称为 xff。
您打算合并多少数据?这个合并范围可以表示为度量间隔的倍数。对于间隔为五分钟的情况,1 表示存储原始数据,12 表示存储每小时的数据,依此类推。这种行为称为步长。
您打算保留多少步(行)数据?这个值表示为前面定义的步的数目。也就是说,如果步长为 12(每小时的数据),那么 24 行则表示一天的历史数据。
尽管可以(并且很吸引人)保存时间间隔为五分钟的整年的数据,但这并不是 RRD 的真正目的,并且大部分人根本不需要使用这么多的信息。在 Web 性能的示例中,人们很可能只对保留一天的细粒度的数据感兴趣,然后每个星期对数据按照小时进行平均值计算,随着时间的推移逐步地计算数据的平均值。除了平均值之外,还保留了最佳和最差度量,以赋予平均值更多的意义。
定义 RRA 的语法是 RRA:cf:xff:steps:rows , 其中 cf 是合并函数(AVERAGE、MIN、MAX 或 LAST),并且 xff、steps 和 rows 如前所述。
所有这些内容看起来可能很容易混淆,所以图 1 对通过 RRD 的数据流进行了图解说明。
图 1. 通过 RRD 的数据流
上面一行方框代表了数据源中的数据。数据点向右移动,为进入的数据点腾出空间。保存足够的数据点以满足 RRD 中所有存档的需要。RRA 数据点符合类似的模式,不同之处在于,通过对数据源中的数据点应用所选择的 CF,计算得到最左边的条目。数据点的数目,以及数据从主数据点合并到存档的频率,可以通过步长来定义。RRA 中方框的数目对应于 rows 参数。当 RRA 或数据源中的项从右端移出,它们将被永久地删除。
导入 RRD
在创建了 RRD 之后,通过比较,可以很容易地导入数据。命令 rrdtool update filename timestamp:datapoints 向 RRD 中添加了数据点。timestamp 参数可以是以秒为单位指定的标准 UNIX 时间,因为时间戳或值 N 表示了当前时间。数据点是以冒号分隔的数据列表,每个已定义的数据源中的数据点按照定义的顺序排列。也可以使用 -t 选项来指定映射到您正添加的数据点的以冒号分隔的数据源名称列表。
完整的度量脚本
您将创建一个脚本来自动进行度量工作。然而,在进行度量工作之前,您必须确定 RRD 的结构。表 1 显示了要收集的数据源。
表 1. Web 性能收集的数据源定义
名称 | 类型 | Heartbeat | Min/Max |
Total | GAUGE | 600 | 0/U |
DNS | GAUGE | 600 | 0/U |
Connect | GAUGE | 600 | 0/U |
Pretransfer | GAUGE | 600 | 0/U |
Start transfer | GAUGE | 600 | 0/U |
表 2 显示了 RRA。
表 2. Web 性能收集的 RRA 定义
类型 | XFiles Factor | 步长(时间) | 行(时间) |
AVERAGE | 0.5 | 1(5 分钟) | 288(1 天) |
LAST | 0.5 | 1(5 分钟) | 288(1 天) |
MIN | 0.5 | 1(5 分钟) | 288(1 天) |
MAX | 0.5 | 1(5 分钟) | 288(1 天) |
AVERAGE | 0.5 | 6(30 分钟) | 336(1 周) |
MIN | 0.5 | 6(30 分钟) | 336(1 周) |
MAX | 0.5 | 6(30 分钟) | 336(1 周) |
AVERAGE | 0.5 | 24(2 小时) | 372(31 天) |
MIN | 0.5 | 24(2 小时) | 372(31 天) |
MAX | 0.5 | 24(2 小时) | 372(31 天) |
AVERAGE | 0.5 | 144(12 小时) | 730 (365 天) |
MIN | 0.5 | 144(12 小时) | 730(365 天) |
MAX | 0.5 | 144(12 小时) | 730(365 天) |
清单 2 显示了进行度量工作的 Shell 代码。
清单 2. 度量代码
#!/bin/bash
# Paths... adjust as necessary
BASE="/home/sean"
CONFIG="config"
CURL="/usr/bin/curl"
RRDTOOL="/usr/bin/rrdtool"
# Given a URL, return a file name
makefile () {
# ensure we have the needed parameters
[ -z "$1" ] && exit 2
# Strip out any non alphas, replace with underscores,
# and add a host name
FILE=`echo $1 | sed 's/[^a-zA-Z0-9_]/_/g'`
FILE="`/bin/hostname`-${FILE}.rrd"
# Can only return numeric results, so global $FILE
# has the result
}
# Called to create the RRD
create_rrd () {
# ensure we have the needed parameters
[ -z "$1" ] && exit 2
# Convert the URL to a name and build the RRD
makefile $1
$RRDTOOL create "$BASE/$FILE" -s 300
DS:total:GAUGE:600:0:U DS:dns:GAUGE:600:0:U DS:connect:GAUGE:600:0:U
DS:pretransfer:GAUGE:600:0:U DS:starttransfer:GAUGE:600:0:U
RRA:AVERAGE:0.5:1:288 RRA:AVERAGE:0.5:6:336 RRA:AVERAGE:0.5:24:372
RRA:AVERAGE:0.5:144:730 RRA:MIN:0.5:1:288 RRA:MIN:0.5:6:336:
RRA:MIN:0.5:24:372 RRA:MIN:0.5:144:730 RRA:MAX:0.5:1:288
RRA:MAX:0.5:6:336 RRA:MAX:0.5:24:372 RRA:MAX:0.5:144:730
I RRA:LAST:0.5:1:288
}
# Loop through config file
cat "$BASE/$CONFIG" | while read LINE; do
# For later expansion, the URL is the last field
URL=`echo $LINE | awk '{print $NF}'`
# If the RRD doesn't exist, create it
makefile $URL
if [ ! -f "$BASE/$FILE" ]; then
create_rrd $URL
fi
# take the measurement and dump to RRD with current timestamp
OUT=`$CURL -m 60 -w %{time_total}:%{time_namelookup}:%{time_connect}:
%{time_pretransfer}:%{time_starttransfer} -o /dev/null -s $LINE`
$RRDTOOL update
"$BASE/$FILE" -t "total:dns:connect:pretransfer:starttransfer" N:$OUT
done
在导入配置文件并为其指定了要进行监视的 URL 后,您可以从 cron 中每五分钟运行一次这个脚本,以开始捕获数据。
RRDtool 图形
既然 RRD 中填充了数据,现在是介绍如何生成图形的时候了。rrdtool graph outputfile 命令根据输入 RRD 和您在命令行中传递的参数来生成相应的图形。
指定数据源
首先,需要引用要图形化的 RRD 文件和所使用的数据源。可以使用 DEF 关键字来完成该任务。在定义要使用的数据时,需要指明 RRD 的名称、RRD 中的数据源和 CF(AVERAGE、MIN、MAX 或 LAT)。该定义的格式是 DEF:defname=rrdfile:datasource:ConsolidationFunction 。这个命令从 RRD 中取出特定的数据集,并将其分配给通过 defname 参数所指定的名称。这个名称可以与数据源的名称相同,但正如您将在后面看到的,您可以使用多个文件,而这将引起名称冲突。对于单个 Web 站点,清单 3 显示了一个导入请求的平均总计时间的简单命令。
清单 3. 具有一个 DEF 的 RRDtool 图形命令
rrdtool graph sample1.png
DEF:total=bob.ertw.com-http___ertw_com.rrd:total:AVERAGE
该命令的运行向终端返回了 0x0。graph 命令的输出是所生成的图像的大小。尽管该命令对数据进行了定义,但它并没有说明对数据进行何种操作。因此,不会生成相应的图形。
绘制线条
您可以使用 LINE 命令来绘制线条,需要向该命令传递线条的宽度、数据源、线条的颜色(可选的)以及要显示在图形底部的标签。清单 4 在前面示例的基础上绘制了总计请求时间。
清单 4. 具有单个数据源的图形
rrdtool graph sample2.png
DEF:total=bob.ertw.com-http___ertw_com.rrd:total:AVERAGE
LINE1:total#FF0000:"Total Request time"
图 2 显示了该命令生成的图形。
图 2. 简单线条图形
图形选项和大小
除了总计请求时间在某个给定点大约为 2.5 外,前面的这个示例并没有告诉观察者什么有用的信息。那么究竟是 2.5 什么呢?分钟、秒还是小时?并且,正在度量的对象是什么呢?这正是标题和图注的作用所在。rrdtool graph 命令可以接受几个改变输出的命令行选项。下面是一些较为常用的选项:
-t:在顶部给出图形的标题。
-v:指定 Y 轴的名称,它将垂直地显示在图形的左侧。
-w 和 -h:分别指定图形部分的以像素为单位的宽度和高度。(因为显示了标签,所以得到的图像将变得更大。)
-u、-l 和 -r:在缺省情况下,RRDtool 将尝试确定如何缩放图形。-u 和 -l 选项指定了缺省情况下使用的上下边界(以正在度量的对象为单位)。如果数据超过了这些值,那么 RRDtool 将忽略输入,除非您指定了 -r 选项。
清单 5 在清单 4 的基础上指定了标题并更改了缺省大小。
清单 5. 具有标签的简单图形
rrrdtool graph sample3.png
-w 450 -h 120
-t "Performance of http://ertw.com from inside" -v "Seconds"
DEF:total=bob.ertw.com-http___ertw_com.rrd:total:AVERAGE
LINE1:total#FF0000:"Total Request Time"
图 3 显示了该命令生成的图形。
图 3. 具有标签的简单线条图形
指定时间范围
在缺省情况下,图形显示了最近 24 小时的数据。通过使用指定开始和结束时间的 -s 和 -e 参数,可以显示不同的时间段。这些参数所使用的值可以是传统的 UNIX 的“自新纪元(1970 年 1 月 1 日午夜)开始的秒数”或者使用 at 风格的语法。下面的示例说明了它们的作用:
-s end-1week:因为缺省结束时间是当前时间,所以该命令将开始时间设置为一周以前。
-s end-1week -e "January 31, 2006":该命令显示了 2006 年 1 月最后一周的数据。
-s "February 7,2006" -e "February 8, 2006 23:59":该命令显示了 2 月 7 日和 8 日的数据。其中没有指定具体时间,那么就认为是午夜。这里省略了 23:59,将只显示 2 月 7 日的数据,这是因为假设条件是从第一天的午夜到第二天的午夜,正好是 24 小时。
除了周之外,您还可以指定月、日、小时、分钟或秒。也可以使用相应的复数形式。您可以指定从新纪元开始的任意秒数,如 1139378400 表示 2006 年 2 月 8 日午夜。您可以将这种格式与 at 样式的命令混合使用,如 -e 1139378400 -s end-2weeks 表示 2 月 8 日之前的两个星期。通常很少将时间表示为整数,但是出于编写脚本的目的,这种形式是相当方便的。
向图形上放置文本
除了线条之外,您可以向图形上放置文本,以帮助读者进行阅读。这些文本可以包括来自数据源的数据、或者是静态的字符串。清单 6 和图 4 清楚地说明了标签的用法。
清单 6. 以标签的形式显示数据
rrdtool graph sample5.JPG
-s "February 7,2006" -e "February 8, 2006 23:59"
-t "Performance of http://ertw.com from inside" -v "Seconds"
DEF:total=bob.ertw.com-http___ertw_com.rrd:total:AVERAGE
LINE1:total#FF0000:"Total Request Time"
COMMENT:"n"
GPRINT:total:AVERAGE:"Average request time %0.2lf %Ss"
COMMENT:"n"
GPRINT:total:LAST:"Current request time %0.2lf %Ss"
图 4. 具有附加数据标签的图形
可以把图形下方的文本区看作一台打字机。上一部分文本结束后,即可以开始下一部分文本。在本例中,第一部分文本是 Total Request Time。COMMENT:text 命令将静态文本放置到当前位置。其中,n 表示开始新的一行。与此相反,GPRINT 命令使用了来自前面的 DEF 命令定义的数据源的数据。
GPRINT 命令接受一项值数据源 定义,这是一个返回单个值而不是时间序列的数据源。您可以使用 VDEF 命令来定义该数据源,但是它也可以以数据源:合并函数 的形式出现。在本例中,第一个 GPRINT 命令返回了 total 数据源的平均值,并且第二个命令从序列中提取了最后一个元素。与数据源的定义一样,在这里您还可以使用 MAX 和 MIN。
格式字符串是 GPRINT 命令中的最后一部分,可以使用格式化字符串。与 C printf() 函数一样,在对表达式进行计算后,会使用相应的变量的值来替换这些以百分号开头的字符串。%0.2lf 值显示了一个小数位数为 2 (0.2) 的长浮点数 (lf),并且没有指定字段的宽度。%S 将由前一项值的数量单位来替换,如使用 K 表示千或者使用 m 表示千分之一,但是大写字母 S 表示所有的数量都是相同的(这使得图形更容易理解)。对于数值的显示,使用 %lf 和 %S 可以处理大部分的情况。rrdgraph_rrdgraph 的 man 页面对其所有选项进行了概述。
CDEF:计算数据源
到目前为止,您重点关注了如何精确地对度量的数据进行显示。实际上,通常还需要进行进一步的数据操作。例如,网络设备通常返回字节数值,但出于规划的目的,网络工程师则将其乘以 8 以计算每秒的比特位数。CDEF 命令用来创建作为其他数据源计算结果的新的数据源。
CDEF 命令使用了逆波兰式 (RPN),而不是您所熟悉的中辍表示法。RPN 使用了一个堆栈。在推入结果之前,先要推入数值,并且操作符会从堆栈中取出所需的数值。(关于更详细的信息,请参见侧栏 RPN。)
在捕获 curl 的输出时,所有的时间都是相对于开始时刻的。CDEF 命令可以轻松地计算某个组件的时间,如连接时间。因为在 DNS 请求返回时立即开始进行 TCP 连接,所以连接本身所花费的时间应该是连接时间减去 DNS 时间(因为所报告的这两个时间都是相对于整个处理过程的开始时刻的)。在 RPN 中,这种形式可以简单地表示为 connection,dns,-,如清单 7 和图 5 所示。
清单 7. 演示 CDEF 的基本图形
rrdtool graph sample6.JPG
DEF:dns=bob.ertw.com-http___canoe_ca.rrd:dns:AVERAGE
DEF:connect=bob.ertw.com-http___canoe_ca.rrd:connect:AVERAGE
CDEF:connect_only=connect,dns,-
LINE1:connect_only#FF0000:"Connection Time"
图 5. 使用 CDEF 命令的图形
RRDtool 图形的总结
这一部分内容介绍了 RRDtool 的许多图形功能。很明显,它可以用来完成各种各样的任务,这也引出了一则有用的建议:仅仅是因为您可以这样做,但并不意味着您应该这样做。这意味着,可能不应该去考虑那些并没有为图形添加新功能的老套的特性(比如大部分情况下使用的背景特性)。
要得到令人满意的图形,请遵循下列提示:
每个数值都应该具有度量单位,如秒、连接/秒、或其他单位。使用数量时,保持图注沿着短轴的方向,并且在使用 GPRINT 显示数值时,请用 %S 来代替 %s,以告知 RRDtool 对所有的显示行使用相同的数量。
在处理不同数量的数据集时,要么将它们分开放到不同的图中,要么创建一个 CDEF 命令将其数量转换为能够与其他数据集进行比较的数量。例如,如果您正在用千作为单位度量每秒的连接数,并且同时度量每秒内存分配失败次数(可能小于 1),那么可以考虑将第一个数据集按比例缩小 1000 倍。否则,内存分配失败次数的细节信息相比之下将显得非常短小。在进行按比例伸缩时,请确保对所有的标签进行更新,以反映该变化。
图形很适合于用来表示一段时间内的变化情况,或用来显示数据集之间的相关性。同样地,图形中的数据源应该与所描述的情况具有一定的关联。使用图形方式同时表示田纳西州孟菲斯市的路由器的数据变化率,以及伦敦的服务器的内存使用情况,这并没有什么意义,除非您是在尝试找出或显示它们之间的关系。
不要在一副图形中放入过多的线条。最好不要超过四或五条。
使用颜色时应当小心。对于线条而言,所使用的颜色应该使得它们在图形中容易辨认。六条线,分别使用不同深浅的红色,这并不是个好主意。相反地,当使用堆叠区域(显示许多组件中的同一个操作时,如 Web 请求)时,这些颜色之间应该是可以可辨别的,但要避免过于刺眼。在本解决方案中,使用了对比色以及从底部深色到顶部浅色的渐近。
如果对于单个值来说,精确度很重要,那么请确保在图形底部显示相应的数值。例如,您可以绘制一条水平线条来表示特定数据序列的平均值。因为该线条的目的很可能是比较数据与平均值,所以在图形底部显示该平均值将使得它们之间的对比更清晰。
对于数据序列,始终使用标题和标签。
汇总结果
了解如何启用数据收集、图形化显示单个 Web 站点以及比较来自多个 RRD 的信息。
启用数据收集
清单 2 显示了对任意数目站点进行性能度量工作的完整脚本。使用 crontab -e 选项,从 cron 中运行该代码。更改脚本开始处 CONFIG 的值,以反映存放配置文件和 RRD 文件的目录。清单 8 显示了启用五分钟数据样本的 crontab 条目,假设所运行的这个脚本来自 /home/sean 目录。
清单 8. 启用周期性数据收集的 Crontab 条目
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /home/sean/measure.sh
因为这些度量代码将进行收集工作的计算机的名称插入到 RRD 文件的名称中,所以多台计算机可以对同一个站点的性能进行度量。该功能屏蔽了本地带宽或 DNS 解析速度的变化
图形化显示单个 Web 站点
最基本且最简单的方式是显示所有的连接组件(DNS 请求、连接、第一个字节、总计),同时显示一些基本的统计数据(请参见 清单 9)。请注意,我省去了 pretransfer 属性,因为仅在向服务器传输数据以便为查询做准备时,它才有意义。我所查看的请求是简单的 GET 请求,因而做了这样的省略。
清单 9. 显示连接组件的区域图形
#!/bin/sh
MEASUREMENT="bob.ertw.com"
SITE="http://canoe.ca"
makefile () {
# ensure we have the needed parameters
[ -z "$1" ] && exit 2
# Strip out any non alphas, replace with underscores,
# and add the host name
FILE=`echo $1 | sed 's/[^a-zA-Z0-9_]/_/g'`
# Can only return numeric results, so global $FILE
# has the result
}
makefile $SITE
DEFS=" DEF:total=$MEASUREMENT-$FILE.rrd:total:AVERAGE
DEF:dns=$MEASUREMENT-$FILE.rrd:dns:AVERAGE
DEF:connect=$MEASUREMENT-$FILE.rrd:connect:AVERAGE
DEF:pretransfer=$MEASUREMENT-$FILE.rrd:pretransfer:AVERAGE
DEF:starttransfer=$MEASUREMENT-$FILE.rrd:starttransfer:AVERAGE"
rrdtool graph $FILE-daily.png
-u 3 -r
-t "Performance of http://canoe.ca from $MEASUREMENT" -v "Seconds"
$DEFS
AREA:total#DADAAA:"Data Transfer"
AREA:starttransfer#CAAA00:"First Byte"
AREA:connect#555555:"Connection"
AREA:dns#222222:"DNS Lookup"
COMMENT:"n" COMMENT:"n"
GPRINT:total:AVERAGE:"Average request time %0.2lf %Ss"
GPRINT:total:MIN:"Min/Max %0.2lf %Ss/"
GPRINT:total:MAX:"%0.2lf %Ss"
COMMENT:"n"
GPRINT:total:LAST:"Current request time %0.2lf %Ss"
rrdtool graph $FILE-monthly.png
-u 3 -r -s end-1month
-t "Performance of http://canoe.ca from $MEASUREMENT" -v "Seconds"
$DEFS
AREA:total#DADAAA:"Data Transfer"
AREA:starttransfer#CAAA00:"First Byte"
AREA:connect#555555:"Connection"
AREA:dns#222222:"DNS Lookup"
COMMENT:"n" COMMENT:"n"
GPRINT:total:AVERAGE:"Average request time %0.2lf %Ss"
GPRINT:total:MIN:"Min/Max %0.2lf %Ss/"
GPRINT:total:MAX:"%0.2lf %Ss"
COMMENT:"n"
GPRINT:total:LAST:"Current request time %0.2lf %Ss"
该代码生成了两幅图形:一副图形显示了过去 24 小时的数据,而另一幅显示了过去一个月的数据。图 6 显示了每天的图形。
图 6. 基本连接图形
当显示不同时间段的相同数据时,您可能会发现其中的一些差异。我选择在每月的图形中省去 Current request time,因为已经对 24 个不同的数据点进行平均值计算得出了 2 小时的合并数据点。与之类似,图形底部报告的平均值有可能比较低,因为通过平均计算去掉了异常值。
比较来自多个 RRD 的数据
可能在有些情况下,您需要比较多个站点的性能,或者比较同一个站点的来自不同监视点的度量。在尝试建立事件之间的关联(如当多个宿主运行于同一台服务器时)以查找本地性能问题(使用多个度量点)或者同时显示多个站点的长期趋势时,这种能力是非常有用的。
无论在何种情况下,其解决方案都涉及到引用不同 RRD 文件的多个 DEF 命令。在清单 10 中,显示了三个不同的 Web 站点及其平均值。
清单 10. 比较三个 Web 站点并显示平均值
rrdtool graph multiple-year.png
-u 5 -r -s end-1year
-t "Web performance trending" -v "Seconds"
DEF:total1=bob.ertw.com-http___canoe_ca.rrd:total:AVERAGE
DEF:total2=bob.ertw.com-http___cnn_com.rrd:total:AVERAGE
DEF:total3=bob.ertw.com-http___ertw_com.rrd:total:AVERAGE
CDEF:avg=total1,total2,total3,+,+,3,/
LINE1:total1#FF0000:"Canoe.ca"
LINE1:total2#00FF00:"CNN.com"
LINE1:total3#0000FF:"ERTW.COM"
LINE1:avg#000000:"Average"
GPRINT:avg:AVERAGE:"Average request time %0.2lf %Ss"
对结果进行解释
到此为止,您已经在本教程中了解了用于度量和显示数据的工具。有了这些工具,您可以绘制相应的图形以指出存在问题的地方。例如,如果通常需要五秒的特定查询的执行时间少于三秒,那么应该从何处入手进行检查呢?另外,如果您接收到了中午的时候性能低下的抱怨,那么您必须在解决该问题之前确定其原因。可能根本就没有什么问题,这些抱怨只不过是主观臆断。
返回到对请求进行剖析部分,我们对一些关键的领域进行了度量:
解析服务器的 DNS 名称所花费的时间。
连接到服务器所花费的时间。
发送请求所花费的时间。
从服务器接收到第一个字节所花费的时间。
从服务器接收到最后一个字节所花费的时间。
其中每个事件都依次地发生,并且它们的执行时间依赖于系统中不同的部分。
DNS 解析
DNS 解析 是将名称(如 www.ibm.com)转换为 IP 地址(如 1.1.1.1)的过程。在大部分情况下,客户端对它的名称服务器提出请求,而后者在最终返回结果之前要访问一系列的名称服务器。所有的延迟都来自于该客户端的名称服务器或名称服务器链中最后的服务器,而后者通常属于某个公司或者宿主提供商。
度量 DNS 响应时间的目的是发现 DNS 服务器中的问题,并在比较多个度量站点时对数据进行规范化。例如,如果从 ISP A 解析一个名称需要 0.5 秒,而从 ISP B 需要 1 秒,那么来自 ISP A 的度量要比来自 ISP B 的度量快半秒钟。DNS 可能运行正常,或者仅仅是 ISP B 有点问题,而它并不在该公司的控制范围之内,但重要的是要认识到,即使 Web 应用程序和网络运行正常,度量值也可能存在差异。
连接
建立 TCP 连接的时间在一定程度上依赖于中间网络的速度,但是更主要因素的是 Web 服务器自身的运行状况。回到前面的内容,TCP 连接由三次握手过程形成:
客户端发送设置了 SYN 位的数据包。
服务器以 SYN 和 ACK 作为应答。
客户端以 ACK 作为确认。
这个过程中暗示了,服务器具有足够的资源以接受该连接。如果这方面存在问题,那么连接时间就会变长。
大部分 Web 服务器使用多个工作线程或进程来处理请求。主进程接受连接并将其分派给一个工作线程。UNIX 内核对一定数量的进入的连接请求进行排队,但是主进程必须适当地处理这些请求并将它们从队列中取出分派给相应的工作线程。在高负载的情况下,服务器在生成新的工作线程或分派工作时可能会很慢,这将导致较长的连接时间。
导致较长的连接时间的其他原因还包括不正确地配置负载平衡器或其他网络设备。有可能因为客户端和服务器之间的网络速度过慢,从而导致握手过程比正常情况花费更长的时间,这种延迟还会在最后一个字节时间中重复出现。
图 7 显示了 Web 站点在午夜出现连接速度降低的两种情况。
图 7. 在午夜出现连接速度降低的情况
尽管该图形没有反映出长期的问题,但它的确指出了值得运行该站点的人关注的问题。如果给定了问题出现的时间,那么它可能恰好与预先计划的备份操作和其他维护工作相重叠。如果是这个原因,那么可以将这些工作分散到更长的时间范围内,通常这样就可以解决问题了。如果这种延迟出现在一天中更长的时间段内,那么可能就应该考虑服务器负载的因素了。
在大部分情况下,请求本身很小。如果请求中包含了要上载的文件,那么请求时间可能变得更长。然而通常情况下,发送请求的时间并不重要。
第一个字节
第一个字节 是指在发出请求后返回数据所花费的时间。对于静态站点,这种延迟是由 Web 服务器必须处理的规则和必须发生的磁盘活动所导致的。
比静态站点更有趣的是动态站点。一项请求可能需要对数据库进行查询或者查阅其他的计算机。设想一个搜索引擎必须解析查询计划并访问索引以确定最佳匹配,或者一个联机数据库必须读取记录并可能还要执行相应的事务。那么从请求到返回第一个字节就正好表示了 Web 服务器的性能,尤其是 Web 应用程序的性能。
Web 应用程序性能降低的原因非常之多。查看一段时间内的数据可以帮助确定其原因。如果问题依然存在,这可能是因为所编写的程序效率低下或者是没有对数据库的性能进行正确的优化。如果还有其他的数据,如并发连接,那么可以寻找应用程序性能低下和系统负载之间的关系。
最后一个字节
第一个和最后一个字节时间之间的差是通过 Internet 向目标发送 HTML 所花费的时间。在有些情况下,Web 应用程序可能会将 Web 服务器的数据转换为数据流,所以该度量也可能包括了 Web 应用程序性能的成分。然而大部分情况下,最后一个字节与网络的运行状况有着密切的关系。
而网络的运行状况可能与原始带宽(如途中某处繁忙的 Internet 连接)或客户端和服务器之间的延迟有关。在这两种情况下,对 Internet 中不同部分的度量将帮助定位其原因。
总结
要确定导致 Web 性能问题的根本原因,需要了解如何进行请求以及如何获取相关的数据,这样才能得出相应的判断。更重要的是,您必须弄清楚该问题的确存在。通过适当地监视和显示数据,您就可以确定这些问题。
本教程中所介绍的这些图形和技巧,构成了诊断问题和采取了相应补救措施后证明其结果的基础。您有可能将使用本文中介绍的这些工具来创建自定义的图形,并真正地去了解这些问题。使用 RRDtool 来度量数据,包括 Web 应用程序的其他组件,其实是非常容易的。该工具的循环特性及其非常方便的图形化功能,可以确保您更有效地对各种数据进行解释,而不再需要去构建那些用完后就丢弃的一次性工具。
更多精彩
赞助商链接