本文是构建能够每秒处理 3 百万请求的高性能 Web 集群系列文章的第一篇。它记录了我使用负载生成器工具的一些经历,希望它能帮助每一个像我一样不得不使用这些工具的人节省时间。
负载生成器是一些生成用于测试的流量的程序。它们可以向你展示服务器在高负载的情况下的性能,以及让你能够找出服务器可能存在的问题。通过负载测试了解服务器的缺点,是测试服务器弹性以及未雨绸缪的好方法。
负载生成工具(Load-Generating Tools)
在进行负责测试时要牢记一件重要的事:你能在 Linux 上建立多少个 socket 连接。这个限制是硬编码在内核里的,最典型的就是临时 W 端口的限制。(在某种程度上)你可以在 /etc/sysctl.conf 里扩展它。但是基本上,一台 Linux 机器只能同时打开大约 64,000 个 socket 。因此在负载测试时,我们不得不通过在单一的连接上尽可能多地发出请求来充分利用 socket 。 除此之外,我们还需要不止一台的机器来产生负载。否则,负载生成器会把可用的 socket 占用导致不能产生足够的负载。
我一开始用的是‘ab’,Apache Bench 。它是我所知道的 http 基准测试工具中最简单、最通用的。并且它是 Apache 附带的产品,因此它可能已经存在于你的系统中。不幸的是,我在使用它的时候每秒大约只能生成 900 个请求。虽然我见过其他人使用它每秒能达到 2,000 个请求,但我可以立即告诉你,‘ab’并不适合我们的基准测试。
Httperf
接着,我尝试了 ‘httperf’。这个工具更强大,但是它依然相对简单并且功能有限。要算出每秒生产了多少个请求并不是仅传递参数那么简单。经过我的多次尝试,获取了每秒超过几百请求的结果。例如:
它以每秒 1,000 个的速率创建了 100,000 个会话(session)。每次会话发起 5 次请求,时间间隔为 2 秒。
1
|
httperf
--
hog
--
server
=
192.168.122.10
--
wsess
=
100000
,
5
,
2
--
rate
1000
--
timeout
5
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
Total
:
connections
117557
requests
219121
replies
116697
test
-
duration
111.423
s
Connection
rate
:
1055.0
conn
/
s
(
0.9
ms
/
conn
,
<=
1022
concurrent
connections
)
Connection
time
[
ms
]
:
min
0.3
avg
865.9
max
7912.5
median
459.5
stddev
993.1
Connection
time
[
ms
]
:
connect
31.1
Connection
length
[
replies
/
conn
]
:
1.000
Request
rate
:
1966.6
req
/
s
(
0.5
ms
/
req
)
Request
size
[
B
]
:
91.0
Reply
rate
[
replies
/
s
]
:
min
59.4
avg
1060.3
max
1639.7
stddev
475.2
(
22
samples
)
Reply
time
[
ms
]
:
response
56.3
transfer
0.0
Reply
size
[
B
]
:
header
267.0
content
18.0
footer
0.0
(
total
285.0
)
Reply
status
:
1xx
=
0
2xx
=
116697
3xx
=
0
4xx
=
0
5xx
=
0
CPU
time
[
s
]
:
user
9.68
system
101.72
(
user
8.7
%
system
91.3
%
total
100.0
%
)
Net
I
/
O
:
467.5
KB
/
s
(
3.8
*
10
^
6
bps
)
|
最终,我使用这些设置达到了每秒 6,622 个连接:
1
|
httperf
--
hog
--
server
192.168.122.10
--
num
-
conn
100000
--
ra
20000
--
timeout
5
|
(总共创建了 100,000 个连接,并且以每秒 20,000 个连接的固定速率创建)
它还有一些潜在的优势,并且拥有比‘ab‘更多的特性。但它不是我要用在这个项目里的重量级工具。我需要的是能够支持分布式多负载测试节点的工具。因此,我的下一个尝试是:Jmeter。
Apache Jmeter
这是一个功能齐全的 web 应用测试套件,它可以模拟真实用户的所有行为。你可以使用 Jmeter 的代理去访问你的网站,进行点击、登陆、模仿用户可以做的所有行为。Jemeter 会把这些行为记录下来作为测试用例。然后 Jmeter 会反复执行这些动作来模拟你想要的用户数量。尽管配置 Jmeter 比 ‘ab‘ 和 ’httperf‘ 复杂得多,但它是一个很有趣的工具!
根据我的测试,它每秒可以产生 14,000 个请求!这绝对是一个好的进展。
我使用了 Googlle Code project 上的一些插件,并且使用它们的“Stepping Threads”和“HTTP RAW”请求,最终每秒大约可以产生 30,000 个请求!但这已经达到极限了,所以还要寻找另一个工具。这里有一个我之前的 Jmeter 配置,希望可以帮助到其他人。虽然这个配置离完美相差甚远,但有时它可以满足你的要求。
Tsung: 重型的(heavy-duty)、分布式的、多协议测试工具
它每秒基本可以产生 40,000 个请求,这绝对是我们想要的工具。类似于 Jmeter,你可以把一些行为记录下来在测试时运行,并且可以测试大多数的协议。比如 SSL、HHTP、WebDAV、SOAP、PostgreSQL、MySQL、LDAP 和 Jabber/XMPP。与 Jmeter 不同的是,它没有让人感到迷茫的 GUI 设置,它仅有一个 XML 配置文件,和一些你选择的分布式节点的 SSH 密钥。它的简洁和效率对我的吸引力,完全不亚于它的健壮性和可扩展性。我发现它是一个很强大的工具,在正确的配置下它可以每秒产生百万级的 HTTP 请求。
除此之外,Tsung 还可以在 html 上产生图表以及输入你的测试的详细报告。测试的结果通俗易懂,并且你甚至可以把这些图片展示给你的 boss 看!
在这个系列文章的剩余部分,我还会讲解这个工具。现在你可以继续浏览下面的配置说明,或者直接跳到下一页。
在 CentOS 6.2 上安装 Tsung
首先,你要安装(Erlang 需要的) EPEL 源。因此,在进行下一步之前要把它安装好。安装完后,继续安装你用来产生负载的每个节点需要的包。如果你还没有在节点之间建立无密码 SSH 密钥(passwordless SSH key),那么请建立之。
1
|
yum
-
y
install
erlang
perl
perl
-
RRD
-
Simple
.noarch
perl
-
Log
-
Log4perl
-
RRDs
.noarch
gnuplot
perl
-
Template
-
Toolkit
firefox
|
从 Github 或者 Tsung 的官网上下载最新的 Tsung。
1
|
wget
http
:
/
/
tsung
.erlang
-
projects
.org
/
dist
/
tsung
-
1.4.2.tar.gz
|
解压并且编译。
1
2
3
|
tar
zxfv
tsung
-
1.4.2.tar.gz
cd
tsung
-
1.4.2
.
/
configure
&&
make
&&
make
install
|
把示例配置复制到 ~/.tsung 目录里。这是 Tsung 的配置文件和日志文件的存放地方。
1
|
cp
/
usr
/
share
/
doc
/
tsung
/
examples
/
http_simple
.xml
/
root
/
.tsung
/
tsung
.xml
|
你可以根据你的需求去编辑这个配置文件,或者使用我的配置文件。经过大量的尝试以及失败后,我目前的配置文件在使用 7 个分布式节点时可以每秒产生 5 百万个 HTTP 请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
&
lt
;
?
xml
version
=&
quot
;
1.0
&
quot
;
?
&
gt
;
&
lt
;
!
DOCTYPE
tsung
SYSTEM
&
quot
;
/
usr
/
share
/
tsung
/
tsung
-
1.0.dtd
&
quot
;
&
gt
;
&
lt
;
tsung
loglevel
=&
quot
;
notice
&
quot
;
version
=&
quot
;
1.0
&
quot
;
&
gt
;
&
lt
;
clients
&
gt
;
&
lt
;
client
host
=&
quot
;
localhost
&
quot
;
weight
=&
quot
;
1
&
quot
;
cpu
=&
quot
;
10
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.2
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode1
&
quot
;
weight
=&
quot
;
1
&
quot
;
cpu
=&
quot
;
9
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.2
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode2
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
8
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.3
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode3
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
9
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.21
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode4
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
9
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.11
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode5
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
9
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.12
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode6
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
9
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.13
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
client
host
=&
quot
;
loadnode7
&
quot
;
weight
=&
quot
;
1
&
quot
;
maxusers
=&
quot
;
40000
&
quot
;
cpu
=&
quot
;
9
&
quot
;
&
gt
;
&
lt
;
ip
value
=&
quot
;
192.168.122.14
&
quot
;
/
&
gt
;
&
lt
;
/
client
&
gt
;
&
lt
;
/
clients
&
gt
;
&
lt
;
servers
&
gt
;
&
lt
;
server
host
=&
quot
;
192.168.122.10
&
quot
;
port
=&
quot
;
80
&
quot
;
type
=&
quot
;
tcp
&
quot
;
/
&
gt
;
&
lt
;
/
servers
&
gt
;
&
lt
;
load
&
gt
;
&
lt
;
arrivalphase
phase
=&
quot
;
1
&
quot
;
duration
=&
quot
;
10
&
quot
;
unit
=&
quot
;
minute
&
quot
;
&
gt
;
&
lt
;
users
maxnumber
=&
quot
;
15000
&
quot
;
arrivalrate
=&
quot
;
8
&
quot
;
unit
=&
quot
;
second
&
quot
;
/
&
gt
;
&
lt
;
/
arrivalphase
&
gt
;
&
lt
;
arrivalphase
phase
=&
quot
;
2
&
quot
;
duration
=&
quot
;
10
&
quot
;
unit
=&
quot
;
minute
&
quot
;
&
gt
;
&
lt
;
users
maxnumber
=&
quot
;
15000
&
quot
;
arrivalrate
=&
quot
;
8
&
quot
;
unit
=&
quot
;
second
&
quot
;
/
&
gt
;
&
lt
;
/
arrivalphase
&
gt
;
&
lt
;
arrivalphase
phase
=&
quot
;
3
&
quot
;
duration
=&
quot
;
30
&
quot
;
unit
=&
quot
;
minute
&
quot
;
&
gt
;
&
lt
;
users
maxnumber
=&
quot
;
20000
&
quot
;
arrivalrate
=&
quot
;
3
&
quot
;
unit
=&
quot
;
second
&
quot
;
/
&
gt
;
&
lt
;
/
arrivalphase
&
gt
;
&
lt
;
/
load
&
gt
;
&
lt
;
sessions
&
gt
;
&
lt
;
session
probability
=&
quot
;
100
&
quot
;
name
=&
quot
;
ab
&
quot
;
type
=&
quot
;
ts_http
&
quot
;
&
gt
;
&
lt
;
for
from
=&
quot
;
1
&
quot
;
to
=&
quot
;
10000000
&
quot
;
var
=&
quot
;
i
&
quot
;
&
gt
;
&
lt
;
request
&
gt
;
&
lt
;
http
url
=&
quot
;
/
test
.
txt
&
quot
;
method
=&
quot
;
GET
&
quot
;
version
=&
quot
;
1.1
&
quot
;
/
&
gt
;
&
lt
;
/
request
&
gt
;
&
lt
;
/
for
&
gt
;
&
lt
;
/
session
&
gt
;
&
lt
;
/
sessions
&
gt
;
&
lt
;
/
tsung
&
gt
;
|
刚开始的时候有很多东西要理解,但你一旦理解了它们后就会变得很简单。
一旦你已经很好地理解了它们,你就可以创建一个便利的别名,去快速观察 Tsung 报告。
1
2
|
vim
~
/
.bashrc
alias
treport
=
"/usr/lib/tsung/bin/tsung_stats.pl; firefox report.html"
|
1
|
source
~
/
.bashrc
|
然后启动 Tsung
1
2
3
|
[
root
@
loadnode1
~
]
tsung
start
Starting
Tsung
"Log directory is: /root/.tsung/log/20120421-1004"
|
结束后观察报告
1
2
|
cd
/
root
/
.tsung
/
log
/
20120421
-
1004
treport
|
现在我们拥有了一个足够强大的负载测试工具,我们可以规划余下的集群构造了:
1. 使用 Tsung 去测试一个单一的 HTTP 服务器。获取一个基本的基准。
2. 对 web 服务器进行调优,定期使用 Tsung 进行测试提高性能。
3. 对这些系统的 TCP 套接字进行调优,获取最佳的网络性能。再来一次,测试,测试,不停地测试。
4. 构造 LVS 集群,它包含了这些充分调优过的 web 服务器。
5. 使用 Tsung IP 集群对 LVS 进行压力测试。
在之后的两篇文章里,我将会向你展示如何使你的 web 服务器获取最高性能,以及怎样用 LVS 集群软件把它们整合起来。