linux tcp压测工具,wrktcp: 支持tcp协议压测的wrk工具,全配置不依赖lua

wrktcp - 无lua依赖的tcp协议压测wrk工具

你可以查看readme in the file:README_EN.md

[TOC]

本程序主要是基于wrk的基础上 取消了ssl和lua依赖,使用tcpini配置用来实现tcp协议下的压力测试。

主要功能

整体框架基于wrk做的扩展,统计、大部分命令、输出结果沿用的wrk,增加了部分参数。

增加 --html 参数,用于输出html结果文件。

增加--test参数,用于验证ini文件是否正确。

增加--trace参数,用于在命令行打印时间趋势信息。

支持tcp协议的压力测试,包括各种协议和通讯格式。

支持定义发送报文信息,应答报文信息,以及应答的成功响应的结果判断对比配置。

支持请求报文的参数变量。参数类似loadrunner,有如下四种:

COUNTER, 迭代器,如唯一流水等,可支持定义范围,定义步长。

DATETIME,时间模式,格式与strtime完全一致。

CONNECTID,连接唯一ID,一般用于做为终端号等使用。

FILE,文件模式,支持从文件中顺序读取内容进行内容赋值。

支持压测过程中,动态刷新TPS和延时。

支持记录压测过程信息,并生成html页面进行展示。

也支持http,因为http也是tcp协议的一种,只要http的报文头明确配置在文件中即可,具体可以参加sample_http.ini。

与wrk的区别

取消:lua的依赖,自定义的内容和判断不用学习lua,一个配置文件即可。

取消:不依赖任何第三方库,取消ssl和luajit的deps。因此这是一个纯净的(pure)C的工程。

增加:支持tcp协议。使用ini配置文件来配置tcp报文的格式。连接地址、报文约定、请求报文、应答报文信息都在该文件中配置。

增加:动态刷新TPS和延时,在较长时间的压测过程中也能实时的观察压测情况。

增加:直接生成html报表。

增加:执行过程中的实时TPS信息统计刷新。

增加:每个连接只连接一次的测试参数,用于调试ini文件。

改进:thread stat的分布统计,把最大支持TPS从1000W缩减到100W,TPS精度从1增加到0.1。

改进:wrk输出结果,增加了成功和失败的分类,充分考虑了业务级错误。

编译安装

macos

cd wrktcp路径

执行make

linux

cd wrktcp路径

执行make

windows

需要下载MINGW编译器,再进行编译。

快速使用

修改sample_tiny.ini配置

[common]

host = 127.0.0.1

port = 8000

[request]

req_body = this is a test

执行命令

wrktcp -t2 -c20 -d10s --latency sample_tiny.ini

和wrk的用法类似,本命令代表如下意义:

使用2个线程(-t2),并发20个连接(-c20),保持运行5秒(-d5s),使用配置sample_tiny.ini来运行.

输出结果

/Users/suitm/code/c/wrktcp>wrktcp -t2 -c20 -d10s --latency sample_tiny.ini

Running 10s loadtest @ 127.0.0.1:8000 using sample_tiny.ini

2 threads and 20 connections

Time:10s TPS:2347.30/0.00 Latency:5.90ms BPS:48.14KB Error:0

Thread Stats Avg Stdev Max +/- Stdev

Latency 64.19ms 185.36ms 1.11s 91.91%

Req/Sec 1.42k 417.52 1.92k 73.46%

Latency Distribution

50% 6.03ms

75% 8.17ms

90% 168.91ms

99% 983.51ms

23503 requests in 10.09s, 482.00KB read

Requests/sec: 2328.22 (Success:2328.22/Failure:0.00)

Transfer/sec: 47.75KB

命令的详细说明

命令行参数的详细说明

-t, --threads: 使用线程总数,一般推荐使用CPU核数的2倍-1

-c, --connections: 连接总数,与线程无关。每个线程的连接数为connections/threads

-d, --duration: 压力测试时间, 可以写 2s, 2m, 2h

--latency: 打印延迟分布情况

--timeout: 指定超时时间,默认是5000毫秒,越长占用统计内存越大。

--trace: 打印出分布图

--html: 将压测的结果数据,输出到html文件中。

--test: 每个连接只会执行一次,一般用于测试配置是否正确。

-v --version: 打印版本信息

结果的详细解释

Running 10s loadtest @ 127.0.0.1:8000 using sample_tiny.ini

2 threads and 20 connections

代表命令的基本情况以及连接的基本情况,程序执行加载完成配置后立刻打印。

Thread Stats Avg Stdev Max +/- Stdev

Latency 64.19ms 185.36ms 1.11s 91.91%

Req/Sec 1.42k 417.52 1.92k 73.46%

Latency代表延时时间,Req/Sec代表每秒的成功应答笔数(TPS),需要注意的是这部分统计是单个线程的统计数据。

说明依次是:线程状态名(Thread Stats)、平均值(Avg)、标准差(Stdev)、最大值(Max)、正负一个标准差内数据所占比例(+/- Stdev)

如果压测系统稳定,平均值和最大值是期望相等的, 标准差的期望值是0,+/-标准差的期望值是100%。

Latency Distribution

50% 12.99ms

75% 14.02ms

90% 15.74ms

99% 274.89ms

延迟分布的统计数据,50% - 99%是按相应时间进行排序的。50%代表第50%的数据延时是多少99%代表第99%的数据延时是多少。

23503 requests in 10.09s, 482.00KB read

Requests/sec: 2328.22 (Success:2328.22/Failure:0.00)

Transfer/sec: 47.75KB

第一行代表: 在10.11秒内有6862个请求,一共读取了140.72KB的数据。

Requests/sec: 678.90 总请求TPS,包含成功和失败的总数,和wrk相同。

Requests/sec-Success和Requests/sec-Failure : 是区分业务应答是否成功的,可能正常应答了,但是返回的信息是错误信息。

Transfer/sec: 每秒的传输数据大小。

配置文件的详细说明

[common]

host = 127.0.0.1

port = 8000

[request] # 报文长度的长度,默认是8

req_len_len = 8

# 报文长度是全部还是只计算报文体,默认是body可选配置为total

req_len_type = body

# 报文头配置,默认只有长度

req_head = XMLHEAD$(length)20201231

# 必须配置,报文体长度

req_body = \

\

$(DATE)\

$(BRANCH)\

$(TRACENO)\

$(TERMNO)\

313233000017\

张三\

\

HELLO,this is a test\

\

[response]

# 报文头长度,默认是8

rsp_headlen = 23

# 报文头中的报文长度位置,默认是1

rsp_len_beg = 8

# 报文头中的报文长度的长度,默认是8

rsp_len_len = 8

# 报文长度是全部还是只计算报文体,默认是body可选配置为total

rsp_len_type = body

# 响应码类型,默认是fixed

rsp_code_type = xml

# 响应码位置,默认是body

rsp_code_location = body

# 响应码tag,默认是1 6

rsp_code_location_tag =

# 成功响应码,默认是000000

rsp_code_success = 000000

[parameters]

TRACENO = COUNTER, 1, 100000, 1, %08ld

BRANCH = FILE, branch.txt

TERMNO = CONNECTID, %08ld

DATE = DATETIME, %H:%M:%S

关于为什么不使用wrk2

未从wrk2进行扩展的原因,个人观点是作者从技术说的很对,但是从实际工程角度没有实际价值。原因有两点:

第一、一般情况下,压测都是希望有一个较好的结果。

第二、如果是压测软件都会有的统计口径问题,那么即使这个统计方式有问题,但是因为是统一的统计口径和方法,因此也是可信赖的。

因此,相对于每次讨论TPS都需要明确使用何种软件并且是否排除了协调遗漏,这种复杂的前提造成的沟通困难来说,统一沟通方法就先的更为重要了。

确认须知

wrktcp是基于wrk的扩展开发。当然wrk也是使用了大量的redis、nignx, luajit的源码,使用和扩展的时候,请注意对应的开源库的相关协议。

压测的参数技巧

macos下必须调整的参数:

# 最大连接数,mac默认是128,不调整的话连接经常失败

sysctl kern.ipc.somaxconn

sudo sysctl -w kern.ipc.somaxconn=1024

# TIME_WAIT的回收时间,默认是15秒,压测的时候,需要改成1秒,否则超过17k的随机端口未释放,mac就会报错(linux是30k左右)

sysctl net.inet.tcp.msl

sudo sysctl -w net.inet.tcp.msl=1000

linux下的参数调整:

修改/etc/sysctl.conf, 是否可以开启需要根据实际情况,总结来说,如果有F5或者负载均衡器,则不能开启tcp_tw_recycle

M.16/root/wrktcp/wrktcp>sysctl -p

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_tw_recycle = 1

net.ipv4.tcp_timestamps = 1

/******************************************************************************** * * * G-TcpClient:基于完成端口的Tcp客户端通讯模块(IOCP TcpClient) * * * * Copyright © 2009-2010 GuestCode 代码客(卢益贵) * * 版权所有 侵权必究 * * * * QQ:48092788 E-Mail:48092788@qq.com 源码博客:http://blog.csdn.net/guestcode * * * * GSN:34674B4D-1F63-11D3-B64C-11C04F79498E * * * ********************************************************************************/ #pragma once extern "C" { //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 类型定义 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #ifndef _GTYPE #define _GTYPE typedef unsigned char* PGBUF; typedef void(__stdcall *PGFN_ON_CONNECTED)(unsigned int unPerHandle, unsigned char* pBuf, unsigned int unLen); typedef void(__stdcall *PGFN_ON_RECEIVED)(unsigned int unPerHandle, unsigned char* pBuf, unsigned int unLen); typedef void(__stdcall *PGFN_ON_SENDED)(unsigned int unPerHandle, unsigned int unSendID, unsigned int unLen); typedef void(__stdcall *PGFN_ON_DISCONNECTED)(unsigned int unPerHandle, unsigned int unFlag); typedef void(__stdcall *PGFN_ON_THREAD)(unsigned int unThreadContext, unsigned int unThreadHandle, unsigned int unThreadID, BOOL bIsBegin, unsigned int unFlag); /* typedef struct _CONNECTION { unsigned int unPerHandle; }CONNECTION, *PCONNECTION; typedef void(__stdcall *PGFN_ON_CONNECTED)(unsigned int unPerHandle, unsigned char* pBuf, unsigned int unLen); typedef void(__stdcall *PGFN_ON_RECEIVED)(PCONNECTION pConnection, unsigned char* pBuf, unsigned int unLen); typedef void(__stdcall *PGFN_ON_SENDED)(PCONNECTION pConnection, unsigned int unSendID, unsigned int unLen); typedef void(__stdcall *PGFN_ON_DISCONNECTED)(PCONNECTION pConnection, unsigned int unFlag); void __stdcall GTcpClt_OnThread(unsigned int unThreadContext, unsigned int unThreadHandle, unsigned int unThreadID, BOOL bIsBegin, unsigned int unFlag) { } void __stdcall GTcpClt_OnConnected(unsigned int unPerHandle, void* _NULL, unsigned int unNULL) { } void __stdcall GTcpClt_OnReceived(PCONNECTION pConnection, unsigned char* pBuf, unsigned int unLen) { } void __stdcall GTcpClt_OnSended(PCONNECTION pConnection, unsigned int unSendID, unsigned int unLen) { } void __stdcall GTcpClt_OnDisconnected(PCONNECTION pConnection, unsigned int unFlag) { } */ #define _USE_UNICODE 1 #ifndef _DLL //#define _DLL #endif #ifdef _DLL #define DllExport _declspec(dllexport) #else #define DllExport #endif #define VER_FLAG_WIDE_CHAR 0x01 #define VER_FLAG_BETA 0x02 #define VER_FLAG_ZERO_READ 0x04 #define VER_FLAG_TRIAL 0x08 #define VER_FLAG_DEBUG 0x10 #define HNDS_CONNECT 1 #define HNDS_CONNECTED 2 #define HNDS_DISCONNECT 3 #endif //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 类型定义 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 版本信息 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #if(_USE_UNICODE) DllExport wchar_t* __stdcall GTcpClt_GetVersionName(void); #else DllExport char* __stdcall GTcpClt_GetVersionName(void); #endif DllExport float __stdcall GTcpClt_GetVersionNumber(void); DllExport unsigned int __stdcall GTcpClt_GetVersionFlag(void); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 版本信息 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 功能函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DllExport DWORDLONG __stdcall GTcpClt_GetPhyMemInfo(DWORDLONG* pdwTotal); #if(_USE_UNICODE) DllExport void __stdcall GTcpClt_WriteLog(wchar_t* pstrLog, unsigned int unCode = 0); DllExport void __stdcall GTcpClt_GetHostIP(wchar_t* pstrIP, unsigned int unLen, BOOL bIsInternetIP = FALSE); #else DllExport void __stdcall GTcpClt_WriteLog(char* pstrLog, unsigned int unCode = 0); DllExport void __stdcall GTcpClt_GetHostIP(char* pstrIP, unsigned int unLen, BOOL bIsInternetIP = FALSE); #endif //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 功能函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>> PerIoData函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DllExport unsigned int __stdcall GTcpClt_GetGBufSize(void); DllExport unsigned int __stdcall GTcpClt_GetIoDataSize(void); DllExport unsigned int __stdcall GTcpClt_GetIoDataUse(void); DllExport unsigned int __stdcall GTcpClt_GetIoDataTotal(void); DllExport float __stdcall GTcpClt_GetIoDataUseRate(void); DllExport unsigned int __stdcall GTcpClt_GetIoDataUseMem(void); //<<<<<<<<<<<<<<<<<<<<<<<<<<<< PerIoData函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>> PerHndData函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DllExport unsigned int __stdcall GTcpClt_GetHndDataUse(void); DllExport unsigned int __stdcall GTcpClt_GetHndDataTotal(void); DllExport unsigned int __stdcall GTcpClt_GetHndDataSize(void); DllExport float __stdcall GTcpClt_GetHndDataUseRate(void); DllExport unsigned int __stdcall GTcpClt_GetHndDataUseMem(void); //<<<<<<<<<<<<<<<<<<<<<<<<<<<< PerHndData函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 信息函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DllExport unsigned int __stdcall GTcpClt_GetThreadNumber(void); DllExport unsigned int __stdcall GTcpClt_GetPageSize(void); DllExport unsigned int __stdcall GTcpClt_GetBlockSize(void); DllExport unsigned int __stdcall GTcpClt_GetConnectCount(void); DllExport unsigned int __stdcall GTcpClt_GetThreadRunCount(unsigned int unThreadContext); DllExport unsigned int GTcpClt_GetState(unsigned int unPerHandle); #if(_USE_UNICODE) DllExport wchar_t* __stdcall GTcpClt_GetThreadName(unsigned int unThreadContext); DllExport BOOL __stdcall GTcpSock_GetPerHandleInfo(unsigned int unPerHandle, wchar_t* pstrIP, unsigned int unIPLen, wchar_t* pstrPort, unsigned int unPortLen); DllExport BOOL __stdcall GTcpSock_GetPerHandleName(unsigned int unPerHandle, wchar_t* pstrName, unsigned int unLen); #else DllExport char* __stdcall GTcpClt_GetThreadName(unsigned int unThreadContext); DllExport BOOL __stdcall GTcpSock_GetPerHandleInfo(unsigned int unPerHandle, char* pstrIP, unsigned int unIPLen, char* pstrPort, unsigned int unPortLen); DllExport BOOL __stdcall GTcpSock_GetPerHandleName(unsigned int unPerHandle, char* pstrName, unsigned int unLen); #endif DllExport unsigned int __stdcall GTcpClt_GetProcesserNumber(void); DllExport BOOL __stdcall GTcpClt_IsActive(); DllExport unsigned int __stdcall GTcpClt_GetUseMem(void); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 信息函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 操作函数 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> DllExport void* __stdcall GTcpClt_GetPerHandleOwner(unsigned int unPerHandle); DllExport BOOL __stdcall GTcpClt_SetPerHandleOwner(unsigned int unPerHandle, void* pOwner); DllExport PGBUF __stdcall GTcpClt_AllocGBuf(void); DllExport BOOL __stdcall GTcpClt_FreeGBuf(PGBUF pGBuf); DllExport unsigned int __stdcall GTcpClt_PostSendGBuf(unsigned int unPerHandle, PGBUF pGBuf, unsigned int unLen); DllExport unsigned int __stdcall GTcpClt_PostSendBuf(unsigned int unPerHandle, unsigned char* pBuf, unsigned int unLen); DllExport void __stdcall GTcpClt_PostBroadcast(unsigned char* pBuf, unsigned int unLen); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 操作函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 主要函数 DllExport BOOL __stdcall GTcpClt_CloseConnect(unsigned int unPerHandle); #if(_USE_UNICODE) DllExport unsigned int __stdcall GTcpClt_OpenConnect( wchar_t* pstrRemoteIP, wchar_t* pstrRemotePort, wchar_t* pstrLocalIP, PGFN_ON_CONNECTED pfnOnConnected, PGFN_ON_RECEIVED pfnOnReceived, PGFN_ON_SENDED pfnOnSended, PGFN_ON_DISCONNECTED pfnOnDisconnected, void* pOwner = NULL); #else DllExport unsigned int __stdcall GTcpClt_OpenConnect( char* pstrRemoteIP, char* pstrRemotePort, char* pstrLocalIP, PGFN_ON_CONNECTED pfnOnConnected, PGFN_ON_RECEIVED pfnOnReceived, PGFN_ON_SENDED pfnOnSended, PGFN_ON_DISCONNECTED pfnOnDisconnected, void* pOwner = NULL); #endif DllExport BOOL __stdcall GTcpClt_Start(unsigned int unHeartbeatTime = 60, unsigned int unMaxNetDelayTime = 5, unsigned int unGuardThreadSleepTime = 2, PGFN_ON_THREAD pfnOnThread = NULL, unsigned int unHndDataInitNumber = 1000, unsigned int unIoDataInitNumber = 1500, unsigned int unProcesserThreadNumber = 0, unsigned int unWorkerThreadNumber = 0); DllExport void __stdcall GTcpClt_Stop(void); //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 主要函数 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< } /* ... extern "C" */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值