背景
最近项目要做性能测试, 要出要一份性能报告, 让我出一个有关 Tcp 和 Udp 的功能模块的测试, 流程大概是这样, 先走 TCP 协议协商一下会话, 协商成功后走 Udp 收发数据.
有点简单啊, 自己写个功能模块测一下, 然后把结果展示出来就 ok 了.
然而想法很美好, 现实有点残酷. idea 瞬间被 pass 掉, 理由就是自己写的测试模块木有说服力.
要用专业的测试工具来搞, 才有说服力. 当然作为一名开发, 用测试工具是不可能的, 就算饿死也不会用!!!
最后还是学了一下 LoadRunner, 在历经一番坎坷摸索之后, 出了个测试脚本. 不得不说, LR 真的挺好用的.(PS: 真香现场)
不说废话了, 在网上找了一下使用 LR 测试 TCP 和 UDP 的脚本, 资料有点难找, 大多都是简单概况一下.(让我一个 LR 初手, 搞不定啊), 最后在自己一番摸索之下, 把过程整理一下, 要有啥不好的地方, 还请多指教一下.
准备工作
LoadRunner 环境, 这个大家自行百度, 教程太多我就不详说了
数据包, 客户端进行 TCP 协商的数据包, 使用 udp 发送的数据包 (用 wireshark 抓取)
LR 的测试脚本
编写测试脚本
使用 LR 测试 TCP 和 UDP 要使用 LR 中 Windows sockets 协议, 它可以模拟 tcp/ip 协议和服务器进行数据交互, 这样便可以模拟终端来进行性能测试了. 首先打开 LR Virtual User Generator 选择创建脚本.
图: 创建脚本
选择创建 Windows sockets 协议的脚本.
图: 选择 socket 协议
接着会出来一个录制选项框, 在应用类型里有两种选择, 一种是 win32 的应用, 一种是浏网络应用, 选择后可以开始录制脚本, 会启用对应的应用, 根据操作录制脚本.
图: 初始化参数
我这边使用网络应用, 输入要测试服务的 URL, 选择对应的 action, 然后点击 ok 就可以开始录制.
这里可以选择的活动对应有三种
vuser_init: 创建虚拟用户初始化时做的事情, 比如要测试业务某个具体业务操作环节时, 可以先把系统用户登录的写在 init 中.
action: 用户操作的事件, 即需要测试业务操作点
vuser_end: 录制的一般是虚拟用户退出的时候做到操作, 如关闭 socket 等
不过录制脚本这个功能生成的测试脚本有的时候不符合我们的预期, 还是要自己修改, 所以我这里随便录制了一下, 然后重写脚本.
图: 结束录制
点击结束录制, 会生成脚本, 如下图, 然后我们就可以自己修改脚本了
最后就是最关键的地方了, 根据具体业务流程来编写测试脚本, 我这里总体业务流程大致如下
编写 vuser_init
我这里由于要协商会话, 所以在初始化时需要发送两次请求和接受两次请求, 脚本如下
- vuser_init()
- {
- char*recvbuf;
- int recvlen=0;
- int rc=0;
- lrs_startup(257);
- // 设定开始事务
- lr_start_transaction("Trans_Session");
- lr_start_transaction("Conn_TCP");
- // 创建 socket
- rc=lrs_create_socket("socket0","TCP","LocalHost=0","RemoteHost=127.0.0.1:8888",LrsLastArg);
- // 判断套接字创建是否成功
- if(rc!=0){
- lr_end_transaction("Conn_TCP",LR_FAIL);
- lr_end_transaction("Trans_Session",LR_FAIL);
- return 0;
- }
- lr_end_transaction("Conn_TCP",LR_PASS); // 判断 socket 是否链接成功的事务, 0 表示创建成功
- lrs_send("socket0","senCreateReqBuf",LrsLastArg); // 发送安全会话建立请求, senCreateReq 为在 data.ws 中定义的发送变量
- lrs_receive("socket0","senCreateRspBuf",LrsLastArg); // 接收消息, 存放在 senCreateReq 中, senCreateReq 是在 data.ws 中定义的接收数组, 注意数组长度一定要大于等于实际接收长度
- lrs_get_last_received_buffer("socket0",&recvbuf,&recvlen);// 把 Socket 最后接收的字节数组, 长度放在 recvlen 中, 内容放在 recvbuf 中
- if(recvlen<100) {
- lr_end_transaction("Trans_Session",LR_FAIL);
- }
- lrs_send("socket0","authReqBuf",LrsLastArg); // 发送 authRsp,authRsp 为在 data.ws 中定义的发送变量
- lrs_receive("socket0","authRspBuf",LrsLastArg); // 接收消息, 存放在 authRep 中, authRep 是在 data.ws 中定义的接收数组, 注意数组长度一定要大于等于实际接收长度
- lrs_get_last_received_buffer("socket0",&recvbuf,&recvlen);// 把 Socket 最后接收的字节数组, 长度放在 recvlen 中, 内容放在 recvbuf 中
- if(recvlen>60)
- lr_end_transaction("Trans_Session",LR_PASS);
- else
- lr_end_transaction("Trans_Session",LR_FAIL);
- return 0;
- }
1. 首选创建相关测试的事务, 在性能测试中这个可以作为测试用例通过的依据 lr_start_transaction 与 lr_end_transaction 为使用最多的事物创造组合函数, lr_start_transaction 为事物开始函数, lr_end_transaction 为事物结束函数, 并负责记录事物的运行时间.
语法格式如下:
- int lr_start_transaction (const char * transaction_name);
- int lr_end_transaction (const char * transaction_name,int status);
transacton 为事物名称, status 为事物的结束状态, 共有 LR_PASS(通过),LR_FAIL(失败),LR_AUTO(自动), LR_STOP(暂停), 其中 LR_PASS 默认的是 LR_PASS, 可以在事物结束前通过 lr_set_transaction_status 进行修改. 如果在 lr_end_transaction 中没有指定结束事物状态是 LR_AUTO, 而是明确制定为 LR_PASS,LR_FAIL, LR_STOP 其中的其中, 则事物将以最后制定状态来结束. 需要注意, 事物开始没有 lr_end_transaction 没有结束的时候, 不能用相同的事 物名称, 除非这个事物已经通过 lr_end_transaction 结束.
2. 接着创建 socket, 初始化套接字, 使用的函数如下
int lrs_create_socket("socket0","TCP","LocalHost=0","RemoteHost=127.0.0.1:8888",LrsLastArg);
参数分别是: socket 名称, 协议类型 (TCP 或 UDP), 链接类型 (远程链接: RemoteHost, 本地: LocalHost, 或者本地监听),LrsLastArg 参数结束标记, 创建成功返回 0
3. 创建完套接字后, 判断时候成功, 若是失败将对应的事务状态修改为 LR_FAIL
4. 通过 socket 发送数据, lrs_send("socket0","senCreateReqBuf",LrsLastArg); 这里 senCreateReqBuf 为待发送的数据, 数据存在 data.ws 文件中
send 填的是发送数据 buf 的名称, 在后面跟上待发送数据的长度 发送的数据在 LR 中我这边是以 16 进制发送, 按照 LR 的规则需要早 16 进制中加上 \ x. 看起来构造这个数据还是很麻烦的. 不过这里有一个比较方便的方式,
可以从 wireshark 中直接抓取对应格式的数据, 选择需要的数据, 右键复制, 选择转义字符串, 就可以了, 然后将复制的数据放在 data.ws 中就可以了
5. 接收响应数据, 判断响应数据是否正确, 我这里根据长度来判断
编写 Action
在 Action 文件中编写需要测试的操作步骤, 我这里就是发送 udp 请求, 接受响应, 判断响应是否正确, 代码如下
- Action()
- {
- char*recvbuf;
- int recvlen=0;
- int rc=0;
- lr_start_transaction("Trans_UDP");
- lr_start_transaction("Conn_UDP");
- rc=lrs_create_socket("socket1","UDP","RemoteHost=127.0.0.1:8887",LrsLastArg);
- if (rc != 0) {
- lr_end_transaction("Conn_UDP",LR_FAIL);
- lr_end_transaction("Trans_UDP",LR_FAIL);
- return 0;
- }
- lr_output_message("Received:%d",rc);
- lr_end_transaction("Conn_UDP",LR_PASS);
- lrs_send("socket1","ReqBuf",LrsLastArg); // 发送 ReqBuf,ReqBuf 为在 data.ws 中定义的发送变量
- lrs_receive("socket1","RspBuf",LrsLastArg); // 接收消息, 存放在 RspBuf 中, RspBuf 是在 data.ws 中定义的接收数组, 注意数组长度一定要大于等于实际接收长度
- lrs_get_last_received_buffer("socket1",&recvbuf,&recvlen);// 把 Socket 最后接收的字节数组, 长度放在 recvlen 中, 内容放在 recvbuf 中
- if(recvlen>=128) {
- lr_end_transaction("Trans_UDP",LR_PASS);
- } else {
- lr_log_message("Error UDP Received length:%d",recvlen);
- lr_end_transaction("Trans_UDP",LR_FAIL);
- }
- //-------------- 断开 socket--------------
- lrs_disable_socket("socket1",DISABLE_SEND_RECV);
- //-------------- 关闭 socket--------------
- lrs_close_socket("socket1");
- return 0;
- }
原理和上文提的基本一致, 这里就不再多说了
编写 vuser_init
编写结束时的操作, 这里就是关闭 init 中的 socket
- vuser_end()
- {
- //-------------- 断开 socket--------------
- lrs_disable_socket("socket0", DISABLE_SEND_RECV);
- //-------------- 关闭 socket--------------
- lrs_close_socket("socket0");
- return 0;
- }
运行测试脚本
点击 start, 执行脚本, 运行无误, 说明脚本正确
至此测试脚本编写完毕, 可以正式开始性能测试了
性能测试
始运行 LR 执行性能测试
这里我们将刚刚编写好的的测试脚本添加进去
然后配置相关的测试参数, 如并发数, 测试时间等, 开始测试
观察测试结果
最后在运行结束后, 我们可以根据分析报告诊断性能测试结果, 还可以配合 jvisualvm 工具诊断热点方法, 提升程序性能
总结
我们可以 LR 可以配合 jvisualvm 工具诊断热点方法, 提升程序性能. 如果有大神看出什么端倪的话, 欢迎批评斧正, 个人感觉还有提升空间
来源: https://www.cnblogs.com/NathanYang/p/11311050.html