一、Web bench是什么?
首先提一个概念—-压力测试。
在运维工作中,压力测试是一项很重要的工作。比如在一个网站上线之前,能承受多大访问量、在大访问量情况下性能怎样,这些数据指标好坏将会直接影响用户体验。但是,在压力测试中存在一个共性,那就是压力测试的结果与实际负载结果不会完全相同,就算压力测试工作做的再好,也不能保证100%和线上性能指标相同。面对这些问题,我们只能尽量去想方设法去模拟。所以,压力测试非常有必要,有了这些数据,我们就能对自己做维护的平台做到心中有数。
Web bench — 简洁而优美的压力测试工具。
为什么这么说呢?
Web Bench是一个网站压力测试的工具。其最后更新时间是2004年,已经十年多了。其源代码总共才500多行,全部使用C语言编写,最多可以模拟3万个并发连接,真可谓是简洁代码的代表之作。
二、实现原理
同它的实现代码一样,Webbench的代码实现原理也是相当简单,就是一个父进程fork出很多个子进程,子进程分别去执行http测试,最后把执行结果汇总写入管道,父进程读取管道数据然后进行最终测试结果的计算。
整个工具的实现流程:
三、源码剖析
1、源码下载地址:WebBench源码下载地址
下载方法:在linux指定目录的命令行中输入:
git clone https://github.com/EZLippi/WebBench.git
等下载完成之后源码文件夹即在指定目录。
2、源代码的组成::socket.c webbench.c
socket.c是创建socket连接的。主要的功能代码在webbench.c中。
Socket函数的大致内容如下:
int Socket(const char *host, int clientPort)
{
//以host为服务器端ip,clientPort为服务器端口号建立socket连接
//连接类型为TCP,使用IPv4网域
//一旦出错,返回-1
//正常连接,则返回socket描述符
}
socket.c源代码及注释:
[cpp]
/* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
*
* This module has been modified by Radim Kolar for OS/2 emx
*/
/********************************************************\
module: socket.c
program: popclient
SCCS ID: @(#)socket.c 1.5 4/1/94
programmer: Virginia Tech Computing Center
compiler: DEC RISC C compiler (Ultrix 4.1)
environment: DEC Ultrix 4.3
description: UNIX sockets code.
********************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/****************************************
* 功能:通过ip地址和端口号建立网路连接
* @host:网络ip地址
* @clientport:端口号
* return:建立的socket连接,如果返回-1,则表示建立连接失败
*****************************************/
int Socket(const char *host, int clientport)
{
int sock;
unsigned long inaddr;
struct sockaddr_in ad;
struct hostent *hp;
memset(&ad, 0, sizeof(ad));//初始化地址
ad.sin_family = AF_INET;
inaddr = inet_addr(host);//将点分十进制的主机序列转换成无符号的长整形的网络序列
if (inaddr != INADDR_NONE)
memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
else//如果host是域名
{
hp = gethostbyname(host);//用域名获取ip,下面介绍
if (hp == null)
return -1;
memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
}
ad.sin_port = htons(clientport);//将端口号从主机序列转为网络序列
sock = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
if (sock < 0)
return sock;
if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)//建立网络连接
return -1;
return sock;
}
函数gethostbyname功能:通过域名获取ip地址
#include <netdb.h>
3include<sys/socket.h>
struct hostent *gethostbyname(const char *name);
这个函数的参数是传入值是域名或者主机名,例如”www.google.cn”等等。传出值,是一个hostent的结构。如果函数调用失败,将返回NULL。
返回hostent结构体类型指针:
struct hostent
{
char *h_name; //主机的规范名
char **h_aliases; //主机的别名
int h_addrtype; //主机ip地址的类型,主机ip地址的类型,ipv4(AF_INET)或ipv6(AF_INET6)
int h_length; //主机ip地址的长度
char **h_addr_list; //主机的ip地址,以网络字节序存储,如果需要打印,需调用inet_ntop()函数,切记不能用printf函数直接打印。
#define h_addr h_addr_list[0]
};
inet_ntop函数:
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) ;
此函数将类型为af的网络地址结构src,转换成主机序的字符串形式,存放在长度为cnt的字符串中。返回指向dst的一个指针。如果函数调用错误,返回值是NULL。
webbebch.c中的主要函数:
- static void usage(void):提示Webbench的用法及命令
- static void alarm_handler(int signal) :信号处理函数,时钟结束时进行调用
- void build_request(const char *url):创建http连接请求
- static int bench(void):创建管道和子进程,调用测试http函数,实现父子进程通信并计算结果
- void benchcore(const char *host,const int port,const char *req):对http请求进行测试(子进程的具体工作)
webbench.c的主要工作流程:
-
- 解析命令行参数,根据命令行指定参数设定变量,可以认为是初始化配置。
- 2.根据指定的配置构造 HTTP 请求报文格式。
- 3.开始执行 bench 函数,先进行一次 socket 连接建立与断开,测试是否可以正常访问。
- 4.建立管道,派生根据指定进程数派生子进程。
- 5.每个子进程调用 benchcore 函数,先通过 sigaction 安装信号,用 alarm 设置闹钟函数,到时间后会产生SIGALRM信号,调用信号处理函数使子进程停止。接着不断建立 socket 进行通信,与服务器交互数据,直到收到信号结束访问测试。子进程将访问测试结果写进管道。
- 6.父进程读取管道数据,汇总子进程信息,收到所有子进程消息后,输出汇总信息,结束。
流程图:
webbench.c源码注释:
1 /*
2 * (C) Radim Kolar 1997-2004
3 * This is free software, see GNU Public License version 2 for
4 * details.
5 *
6 * Simple forking WWW Server benchmark:
7 *
8 * Usage:
9 * webbench --help
10 *
11 * Return codes:
12 * 0 - sucess
13 * 1 - benchmark failed (server is not on-line)
14 * 2 - bad param
15 * 3 - internal error, fork failed
16 *
17 */
18
19 #include "socket.c"
20 #include <unistd.h>
21 #include <sys/param.h>
22 #include <rpc/types.h>
23 #include <getopt.h>
24 #include <strings.h>
25 #include <time.h>
26 #include <signal.h>
27
28 /* values */
29 volatile int timerexpired=0;//判断测压时长是否已经达到设定时间
30 int speed=0;//记录进程成功服务器响应的数量
31 int failed=0;//记录失败的数量(speed代表成功,failed代表失败)
32 int bytes=0;//记录进程成功读取的字节数
33
34 /* globals */
35 int http10=1; /* http版本:0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
36
37 /* Allow: GET, HEAD, OPTIONS, TRACE */
38 #define METHOD_GET 0
39 #define METHOD_HEAD 1
40 #define METHOD_OPTIONS 2
41 #define METHOD_TRACE 3
42 #define PROGRAM_VERSION "1.5"
43 int method=METHOD_GET;//默认请求方法为GET,同时也支持HEAD、OPTIONS、TRACE
44 int clients=1;//并发数目,默认只有一个进程发请求,通过-c参数设置
45 int force=0;//是否需要等待读取从服务器返回值的数据,0表示需要读取
46 int force_reload=0;//是否使用缓存,1表示不缓存,0表示缓存页面
47 int proxyport=80;//代理服务器的端口
48 char *proxyhost=NULL;//代理服务器的ip