一.简介
Webbench是有名的网站压力测试工具,它是由 Lionbridge公司开发。
Webbech能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webBech的标准测试可以向我们展示服务器的 两项 内容:每秒钟相应请求数和每秒钟传输数据量。webbench不但能具有便准静态页面的测试能力,还能对动态页面(ASP,PHP,JAVA,CGI)进 行测试的能力。还有就是他支持对含有SSL的安全网站例如电子商务网站进行静态或动态的性能测试。
Webbench最多可以模拟3万个并发连接去测试网站的负载能力,其源码仅仅500行左右。
二.源码解读
源码一共包含两个源文件:socket.c 和 webbench.c 。
1. socket.c
本文件仅包含一个Socket函数:
int Socket(const char *host, int clientPort)
{
/*
* 以host和clientPort构成一对TCP的套接字(host支持域名)
* 创建失败返回-1,成功返回一个sockt描述符
*
* 涉及的知识点:
* inet_addr:若字符串有效则将字符串转换为32位二进制
* 网络字节序的IPV4地址,否则为INADDR_NONE;
* gethostbyname:返回对应于给定主机名的包含主机名字
* 和地址信息的hostent结构指针;
* htons:将一个无符号短整型(s)的主机数值(h)转换为网络字节顺序(n);
* socket:创建套接字
* connect:连接到相应的主机
*/
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
{
hp = gethostbyname(host);
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;
}
2.webbench.c
在webbench.c中,包含了下面几个函数:
static void alarm_handler(int signal);
static void usage(void);
static void build_request(const char *url);
static int bench(void);
static void benchcore(const char *host, const int port, const char *req);
int main(int argc, char *argv[]);
- Webbench工作流程:
进入main函数,解析命令行参数;
调用build_request()函数,构造http request信息;
调用bench()函数,根据client大小fork子进程;
子进程调用benchcore()函数进行测试,挂接信号处理函数alarm_handler,将结果写入管道。
主进程从管道读取消息,并输出到stdout。
下面逐个部分进行分析:
- 全局变量
/* values */
/* 超时标识,由命令行参数-t指定,当被设置为1时,所有子进程退出
* volatile:
* - 让系统总是从内存读取数据,
* - 告诉编译器不要做任何优化,
* - 变量会在程序外被改变
*/
volatile int timerexpired=0;
int speed=0; //子进程成功得到服务器响应的总数
int failed=0; //子进程请求失败总数
int bytes=0; //读取到的字节总数
/* globals */
int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ //HTTP协议版本
/* Allow: GET, HEAD, OPTIONS, TRACE */
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_OPTIONS 2
#define METHOD_TRACE 3
#define PROGRAM_VERSION "1.5"
int method=METHOD_GET; //HTTP请求方法,默认为GET
int clients=1; //并发数,即子进程个数,默认为1,可以由命令行参数-c指定
int force=0; //是否等待从服务器获取数据,0表示等待
int force_reload=0; //是否使用cache,0为使用
int proxyport=80; //代理服务器端口,默认为80
char *proxyhost=NULL; //代理服务器IP,默认为NULL
/*
* 执行时间,默认为30秒,可由命令行参数-t指定,
* 当子进程执行时间超过这个秒数之后,
* 发送 SIGALRM 信号,将 timerexpired 设置为 1,
* 让所有子进程退出
*/
int benchtime=30;
/* internal */
int mypipe[2]; //创建管道(半双工),用于父子进程间通信
char host[MAXHOSTNAMELEN]; //服务器IP
#define REQUEST_SIZE 2048
char request[REQUEST_SIZE]; //HTTP请求信息
/* 命令行的选项配置表,参见man文档:man getopt_long */
static const struct option long_options[]=
{
{
"force",no_argument,&force,1},
{
"reload",no_argument,&force_reload,1},
{
"time",required_argument,NULL,'t'},
{
"help",no_argument,NULL,