什么是Tsung?Tsung是一款开源的,多协议的,分布式的压力测试工具,支持的协议包括Http,WebDAV,SOAP,MySQL,LDAP和JABBER/XMPP等。
Tsung的特点有:高性能、分布式、支持多协议、支持SSL、利用OS IP别名技术在单个机器上使用多个IP、XML方式的配置文件、支持SNMP,Erlang和MUnix的方式对目标服务器进行监控(CPU,内存,网络收发包)、模拟真实的流量,虚拟用户的发呆时间和抵达率使用随机的概率分布、HTML或者图表的方式报告测试结果。
补充:有关Tsung的发音。
其实在官网的文档中没有提及Tsung的正确发音,但是官网的邮件列表中有很多使用者讨论它的发音,有的将T与sung分开,念“T-Sung”,有的是研究音标,因为Tsung与Tsunami具有同样的前缀,因此念【tsun】,但是大多数人还是念“T-Sung”。
Tsung的安装相对比较简单,官网下载源码包后,通用的三步骤完成安装:configure;make;make install。
注意:由于Tsung的运行依赖erlang/otp,因此在安装Tsung前,需要先安装Erlang/OTP。另外,想要图表化来展示测试结果,还需要安装gnuplot。
Tsung的使用分为三个步骤:
1) 配置文件编写
2) 开始执行压力测试
通过下面的命令开始进行压力测试
tsung -f ./test.xml -l ./ start其中-f参数指定需要使用的配置文件, -l参数指定日志文件的存储位置。
3) 对结果进行分析
测试完成后,在-l指定的目录中可以看到以开始测试时间命名的文件夹,文件夹中有tsung.log文件,这里面记录的就是测试结果。显然,这种纯文本的结果不便于我们查看。tsung提供了相应的脚本,帮助我们得到图表化的结果。
/usr/local/lib/tsung/bin/tsung_stats.pl运行这个脚本后,将得到HTML形式的测试结果报告。
Tsung的配置文件采用XML的形式,整个配置文件包含在tsung标签中,大概可以分为如下几个部分:客户端配置、服务端配置、压力阶段配置、选项参数配置及会话动作配置。
<clients> <client host=”spurs” use_controller_vm=”true” maxusers=”600”> <ip value=”172.16.81.99”/> </client> </clients>use_controller_vm:是否仅使用一个erlang虚拟机,由于可能的fd限制问题,需要模拟大量用户时,将该值设置为false,即启动多个erlang虚拟机进程。当然,我们也可以通过ulimit来解决fd的大小限制。
maxusers:表示单个erlang虚拟机进程上最大的模拟用户数。
ip:模拟客户端连接服务器时使用的ip。
注意,这里可以写多个IP,其典型的场景为:服务端有根据客户端IP进行负载均衡的功能。另外,如果我们想使用多台物理机共同完成压力测试,这里可以编写多个client元素。
<servers> <server host=”172.16.81.100” port=”5672” type=”tcp”/> </servers>host:待测服务器的IP
port:待测服务器的侦听端口
type:连接使用的协议类型
<load> <!--动态创建用户--> <arrivalphase phase=”1” duration=”10” unit=”minute”> <users maxnumber=”100” interarrival=”2” unit=”second”/> </arrivalphase> <arrivalphase phase=”2” duration=”10” unit=”minute”> <users arrivalrate=”10” unit=”second”/> </arrivalphase> <!—静态创建用户--> <users session=”tsung-example” start_time=”1” unit=”second”/> </load>模拟用户的产生分为动态创建和静态创建。静态创建是指在指定时间创建用户执行指定的会话动作。
duration:表示动态用户创建持续的时间。
unit:时间单位(hour,minute,second)。
interarrival:动态用户创建时间间隔(每隔interarrival时间创建一个用户)。
arrivalrate:动态用户创建速率(每unit指定的时间创建arrivalrate个用户)。
maxnumber:该阶段最大模拟用户数。
这里定义一些通用的选项,以及不同协议独有的选项参数。例如:
<option name=”hibernate” value=”5”/> <option name=”ports_range” min=”1025” max=”65535”/> <option type=”ts_amqp” name=”username” value=”dev”/> <option type=”ts_amqp” name=”passwoad” value=”dev”/> <option type=”ts_amqp” name=”heartbeat” value=”30”/>hibernate:当thinktime超过value指定的时间时(单位:秒),开始GC处理。合理设置该值,可以有效减少测试机的内存资源。
<sessions> <session probability=”100” name=”publisher” type=”ts_amqp” bidi=”true”> <request>…</request> <thinktime value=”5”/> <transaction name=”deviceinfo”> <request>…</request> <request>…</request> … </transaction> </session> <session probability=”0” name=”consumer” type=”ts_amqp” bidi=”true”> … </session> </sessions>会话动作配置项包含在sessions标签中,由一个或多个session组成,每个session又由多个request,thinktime以及transaction等组成。
probability:动态创建用户执行该session的概率。设置为0的session通常用于静态用户。
name:session会话的名称。
type:该session会话将使用的协议类型,例如ts_amqp,ts_http,ts_jabber等等。
bidi:是否需要双向通信。具体是指是否为一问一答模式,还是服务器也可能主动推送数据或发送请求。
request:包含具体的请求动作,根据不同的协议类型而不同。
thinktime:冥想时间,可通俗的理解为睡眠时间。
transaction:多个request及thinktime等组成,用于结果分析时计算一连串请求的响应时间。
一些常用的协议测试:
1) AMQP
对于AMQP协议,在request标签下常用的请求内容包括:
<amqp type=”connection.open” host=”/”></amqp> <amqp type=”channel.open” channel=”0”></amqp> <amqp type=”confirm.select” channel=”0”></amqp> <amqp type=”basic.publish” channel=”0” exchange=”amqp.direct” routing_key=”test.q” payload_size=100 persistent=”false” payload=”hello world”></amqp> <amqp type=”basic.consume” channel=”1” queue=”test.1” ack=”false”></amqp> <amqp type=”basic.qos” channel=”1” prefetch_size=”100” prefetch_count=”10”></amqp> <amqp type=”waitForConfirms” timeout=”5”></amqp> <amqp type=”waitForMessages” timeout=”60”></amqp> <amqp type=”channel.close” channel=”1”></amqp> <amqp type=”connection.close”></amqp>常用的选项有:
<option type=”ts_amqp” name=”username” value=”dev”/> <option type=”ts_amqp” name=”password” value=”dev”/> <option type=”ts_amqp” name=”heartbeat” value=”30”/>2) HTTP
对于HTTP协议,在requet元素中的内容相对简单,仅有一个类型供选择。
<http url=’/’ version=’1.1’ method=’POST’> <http_header name=”Connection” value=”keep-alive”/> </http>
3) XMPP
<jabber type=”connect” ack=”no_ack”/> <jabber type=”auth_sasl” ack=”local”/> <jabber type=”connect” ack=”local”/> <jabber type=”auth_sasl_bind” ack=”local”/> <jabber type=”auth_sasl_session” ack=”local”/> <jabber type=”presence:initial” ack=”no_ack”/></request> <jabber type=”muc:join” ack=”no_ack” room=”test” nick=”hncscwc”/> …XMPP涉及的内容非常多,出席、聊天、群组、联系人等等。这里暂不一一展开。
1) 统计数据表
上图所示是一些主要的统计信息,transaction统计,以及网络吞吐统计。部分内容说明如下:
connect: 模拟用户连接建立耗时。
page: 以thinktime,transaction分隔的连续的一组request的请求响应时间。通常,对于一个网站首页,不仅仅是一个http请求,同时还会有各种CSS,JS等脚本的下载请求。那么这一连串的请求响应时间才表示首页显示所需要的时间,这是page存在的一个重要理由。当然,我们也可以将这个请求放到一个transaction里。
transaction: 配置文件中对应transaction的请求响应时间。
request:每个request的响应时间。
这里是一些用户统计信息,以及不同协议独有的统计信息。
connected: 建立连接的模拟用户数。
finish_users_count: 完成会话动作配置的模拟用户数。
users:并发用户数(从开始执行会话动作开始,到会话动作执行结束前)。
2) 曲线图
响应时间的曲线图,包括不同的transaction,page以及connect,request的。
吞吐,速率曲线
并发用户曲线图:
前面提到了,对一个网站进行压力测试时,除了基本的URL外,还需要请求大量的CSS,JS等才能真正模拟用户的行为。那么成百上千的请求一个一个手写是相对比较笨的方法。Tsung提供了一个录制的工具,通过这个工具,我们只需在浏览器上正常的进行访问,所有这些请求会自动写入配置文件的会话动作配置项中。具体步骤为:
1) 启动录制程序
tsung-record -L 8090 start2) 浏览器访问
访问前先设置代理,代理的IP地址为本机地址,端口为启动录制程序-L指定的端口,然后正常进行访问。
3)停止录制并修改配置文件
停止录制后,在默认路径/root/.tsung/下会生成一个xml配置文件,这个配置文件只有会话动作。我们在这个基础上进行完善就变成了我们自己的压力测试配置文件。
在压力测试过程中,可能会针对不同的条件进行对比测试。例如,对AMQP测试其confirm机制开启与否的响应速度,消息是否持久化的响应速度。当我们压测完成后,可通过plotter将测试结果显示在同一张图上,以供对比分析。
tsplot “First test” firsettest/tsung.log “Second test” secondtest/tsung.log –d outputdir注意:该命令需在图形界面下操作。
1) 使用多台物理机完成压力测试
a. 使用多台物理机进行压力测试时,所有物理机之间都必须安装Erlang/OTP,且版本必须一致。
b. 只需要在其中一台物理机上执行tsung命令,Tsung会自动在其他物理机上启动相应的erlang虚拟机进程。
c. 几台物理机之间必须是SSH免密码登录的方式
2) BUG
a. start new beam failed
前面提到设置use_controller_vm为false时,超过单个虚拟机进程最大模拟用户数时,会启动多个虚拟机进程共同完成压力测试。实际使用1.5.1版本测试过程中发现,启动新的虚拟机进程失败。
ts_config_server.erl 做如下修改后,可解决问题。
%% 源代码 handle_cast({newbeam, Host, Args}, State=#state{last_beam_id = NodeId, config=Config, logdir = LogDir}) -> Args = set_remote_args(LogDir,Config#config.ports_range), Seed = Config#config.seed, Node = remote_launcher(Host, NodeId, Args), ts_launcher_static:stop(Node), ts_launcher:launch({Node, Args, Seed}), {noreply, State#state{last_beam_id = NodeId+1}}; %% 修改后的代码 handle_cast({newbeam, Host, Arrival}, State=#state{last_beam_id = NodeId, config=Config, logdir = LogDir}) -> Args = set_remote_args(LogDir,Config#config.ports_range), Seed = Config#config.seed, Node = remote_launcher(Host, NodeId, Args), rpc:multicall([Node], tsung, start, [], ?RPC_TIMEOUT), ts_launcher_static:stop(Node), ts_launcher:launch({Node, Arrival, Seed}), {noreply, State#state{last_beam_id = NodeId+1}};
本文简单讲解了Tsung的使用,一些高级的主题,如高级配置(变量使用,动态替换,循环等)、插件编写等没有涉及到。另外,Tsung只是一个工具,真正要将性能测试做好,还需要完备的监控工具及相关理论知识,这样,才能从测试结果中很好的分析出可能存在的问题,进而有针对性的进行调优。