Tsung对具体协议、通道的支持,一般以插件形式提供接口,接口不是很复杂,插件也很容易编写,支持协议多,也就不足为怪了。
下面首先梳理一下当前Tsung 1.6.0所有内置插件,然后为一个名称为Qmsg的私有二进制协议编写插件, 运行Qmsg服务器端程序,执行压力测试,最后查看测试报告。
Tsung 1.6.0支持的协议很多,简单梳理一下:

tsung_config_protocolname
模块解析
ts_protocolname
模块支持数据操作
已经支持协议简单说明:
粗一点来看Tsung插件的工作流程(点击可以看大图):
放大一些(引用 hncscwc 博客图片,相当赞!):
Tsung针对通用协议有支持,若是私有或不那么通用的协议,就不会有专门的插件支持了,那么可选的有两条路子:
既然谈到了插件,我们也编写一个插件也体验一下编写插件的过程。
假设一个虚拟场景,打造一个新的协议Qmsg,二进制格式组成:

这种随意假象出来的格式,不妨称作为qmsg(Q可爱形式的message)协议,仅作为Demo演示而存在。简单场景:
PocketLen:**##UserId + UserComment##**
PocketLen:**##UserId + RandomCode##**
为了卡哇伊一些,多了一些点缀的“**####**”符号。
这里基于Tsung 1.6.0版本构建一个Qmsg插件,假定你懂一些Erlang代码,以及熟悉Tsung一些基本概念。
要创建Tsung的一个Qmsg插件项目,虽没有固定规范,但按照已有格式组织好代码层级也是有必要的。
├── include
│ └── ts_qmsg.hrl
├── src
│ ├── tsung
│ │ └── ts_qmsg.erl
│ └── tsung_controller
│ └── ts_config_qmsg.erl
└── tsung-1.0.dtd
Tsung的压测以xml文件驱动,因此需要界定一个Qmsg插件形式的完整会话的XML呈现,比如:
<session probability="100" name="qmsg-demo" type="ts_qmsg">
<request>
<qmsg uid="1001">Hello Tsung Plugin</qmsg>
</request>
<request>
<qmsg uid="1002">This is a Tsung Plugin</qmsg>
</request>
</session>
ts_qmsg
,会话类型所依赖协议模拟客户端实现<qmsg uid="Number">Text</qmsg>
定义了qmsg会话可配置形式,内嵌在request
元素内uid
为属性此时,你若直接在xml文件中编辑,会遇到校验错误。
Tsung的xml文件依赖tsung-1.0.dtd
文件进行校验配置是否有误,需要做对DTD文件做修改,以支持所添加新的协议。
在tsung-1.0.dtd
项目中,最小支持:
ts_qmsg
qmsg
:
<!ELEMENT request ( match*, dyn_variable*, ( http | jabber | raw | pgsql | ldap | mysql |fs | shell | job | websocket | amqp | mqtt | qmsg) )>
<!ELEMENT qmsg (#PCDATA) >
<!ATTLIST qmsg
uid CDATA "0"
ack (local | no_ack | parse) #REQUIRED
>
完整内容,可参考
tsung_plugin_demo/tsung-1.0.dtd
文件。
include/ts_qmsg.hrl
头文件include/ts_qmsg.hrl
定义数据保存的结构(也称之为记录/record):
-record(qmsg_request, {
uid,
data
}).
-record(qmsg_dyndata, {
none
}
).
ts_config_qmsg.erl
文件,用于解析和协议Qmsg关联的配置:
- 只需要实现parse_config/2
唯一方法
- 解析xml文件中所配置Qmsg协议请求相关配置
- 被ts_config:parse/1
在遇到Qmsg协议配置时调用
备注:
ts_qmsg.erl
ts_qmsg.erl
模块主要提供Qmsg协议的编解码的完整动作, 以及当前协议界定下的用户会话属性设定。
首先需要实现接口ts_plugin
规范定义的所有需要函数,定义了参数值和返回值。
-behavior(ts_plugin).
...
-export([add_dynparams/4,
get_message/2,
session_defaults/0,
subst/2,
parse/2,
parse_bidi/2,
dump/2,
parse_config/2,
decode_buffer/2,
new_session/0]).
相对来说,核心为协议的编解码功能:
get_message/2
,构造请求数据,编码成二进制,上层ts_client
模块通过Socket连接发送给目标服务器parse/2
,(当对响应作出校验时)从原始Socket上返回的数据进行解码,取出协议定义业务内容这部分代码可以参考 tsung_plugin_demo/src/tsung/ts_client.erl
文件。
虽然理论上可以单独编,生成的beam文件直接拷贝到已经安装的tsung对应目录下面,但实际上插件编写过程中要依赖多个tsung的hrl文件,这造成了依赖路径问题。采用直接和tsung打包一起部署,实际操作上有些麻烦,
为了节省体力,使用一个shell脚本 - build_plugin.sh
,方便快速编译、部署:
# !/bin/bash
cp tsung-1.0.dtd $1/
cp include/ts_qmsg.hrl $1/include/
cp src/tsung_controller/ts_config_qmsg.erl $1/src/tsung_controller/
cp src/tsung/ts_qmsg.erl $1/src/tsung/
cd $1/
make uninstall
./configure --prefix=/usr/local
make install
这里指定安装Tsung的指定目录为
/usr/local
,可以根据需要修改
需要提前准备好tsung-1.6.0目录:
wget http://tsung.erlang-projects.org/dist/tsung-1.6.0.tar.gz
tar xf tsung-1.6.0.tar.gz
在编译Qmsg插件脚本时, 指定一下tsung-1.6.0解压后的路径即可:
sh build_plugin.sh /your_path/tsung-1.6.0
后面嘛,就等着自动编译和安装呗。
既然有压测端,就需要一个Qmsg协议处理的后端程序qmsg_server.erl
,用于接收客户端请求,获得用户ID值之后,生成一个随机数字,组装成二进制协议,然后发给客户端,这就是全部功能。
这个程序,简单一个文件,在 tsung_plugin_demo
目录下面,编译运行, 默认监听5678端口:
erlc qmsg_server.erl && erl -s qmsg_server start
另外,还提供了一个手动调用接口,方便在Erlang Shell端调试:
%% 下面为
qmsg_server:sendmsg(1001, "这里是用户发言").
启动之后,监听地址 *: 5678
源码见:tsung_plugin_demo/qmsg_server.erl
因为是演示示范,一台Linxu主机上就可以进行了:
qmsg-subst-example
会话使用了用户ID个和用户发言内容自动生成机制<tsung loglevel="debug" dumptraffic="false" version="1.0">
<clients>
<client host="localhost" use_controller_vm="true"/>
</clients>
<servers>
<server host="127.0.0.1" port="5678" type="tcp"/>
</servers>
<load>
<arrivalphase phase="1" duration="1" unit="minute">
<users maxnumber="10" interarrival="1" unit="second"/>
</arrivalphase>
</load>
<sessions>
<session probability="10" name="qmsg-example" type="ts_qmsg">
<request>
<qmsg uid="1001" ack="parse">Hello Tsung Plugin Qmsg!</qmsg>
</request>
</session>
<session probability="90" name="qmsg-subst-example" type="ts_qmsg">
<setdynvars sourcetype="random_number" start="3" end="32">
<var name="random_uid"/>
</setdynvars>
<setdynvars sourcetype="random_string" length="13">
<var name="random_txt"/>
</setdynvars>
<request subst="true">
<qmsg uid="%%_random_uid%%" ack="parse">Haha : %%_random_txt%%</qmsg>
</request>
<thinktime value="6"/>
<request subst="true">
<qmsg uid="%%_random_uid%%" ack="parse">This is a Tsung Plugin</qmsg>
</request>
</session>
</sessions>
</tsung>
这部分内容,请参考 tsung_plugin_demo/tsung_qmsg.xml
文件。
当Qmsg的压力测试配置文件写好之后,可以开始执行压力测试了:
tsung -f tsung_qmsg.xml start
其输出:
tarting Tsung
Log directory is: /root/.tsung/log/20160621-1334
[os_mon] memory supervisor port (memsup): Erlang has closed
[os_mon] cpu supervisor port (cpu_sup): Erlang has closed
其中, 其日志为:/root/.tsung/log/20160621-1334
。
进入其生成压测日志目录,然后生成报表,查看压测结果哈:
cd /root/.tsung/log/20160621-1334
/usr/local/lib/tsung/bin/tsung_stats.pl
echo "open your browser (URL: http://IP:8000/report.html) and vist the report now :))"
/usr/bin/python -m SimpleHTTPServer
嗯,打开你的浏览器,输出所在服务器的IP地址,就可以看到压测结果了。
以上代码已经放入github仓库:https://github.com/weibomobile/tsung_plugin_demo。
实际业务的私有协议内容要比上面Demo出来的Qmsg复杂的多,但其私有协议插件编写,如上面所述几个步骤,按照规范编写,单机测试,然后延伸到分布式集群,完整流程都是一致的。
嗯,搞定了插件,就可以对系统愉快地进行压测了 :))