webbench--网站压力测试工具

WebBench是一款Linux上的web性能压力测试工具,可模拟最多3万个并发连接测试服务器压力。它通过fork子进程进行HTTP请求,然后统计结果。主要流程包括命令行参数解析、HTTP请求构造、压力测试执行。优点在于部署简单,支持静态和动态页面测试,但缺点是不适合中大型网站测试,且长时间测试可能导致高内存和CPU占用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

WebBench的下载安装

  1. 下载
    命令:wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.gz
  2. 解压缩
  3. 进入webbench目录文件
    命令:cd webbench
  4. 安装
    命令1:make 命令2:make install
  5. 运行
    webbench -c 10 -t 20 http://www.baidu.com/

webbench:其为linux上一款web性能压力测试工具,它最多可以模拟3万个并发连接数来测试服务器压力,其原理为fork多个子进程,每个子进程都循环做web访问测试,子进程将访问的结果通过管道告诉父进程,父进程做最终结果统计。

主要函数:
 alarm_handler 信号处理函数,时钟结束时进行调用。
 usage 输出 webbench 命令用法
 main 提供程序入口...
 build_request 构造 HTTP 请求
 bench 派生子进程,父子进程管道通信最后输出计算结果。
 benchcore 每个子进程的实际发起请求函数。    
流程

1.解析命令行参数,根据命令行指定参数设定变量,可以认为是初始化配置。
2.根据指定的配置构造 HTTP 请求报文格式。
3.开始执行 bench 函数,先进行一次 socket 连接建立与断开,测试是否可以正常访问。
4.建立管道,派生根据指定进程数派生子进程。
5.每个子进程调用 benchcore 函数,先通过 sigaction 安装信号,用 alarm 设置闹钟函数,接着不断建立 socket 进行通信,与服务器交互数据,直到收到信号结束访问测试。子进程将访问测试结果写进管道。
6.父进程读取管道数据,汇总子进程信息,收到所有子进程消息后,输出汇总信息,结束。

详细过程

webbench.c:
全局变量选项参数:都有默认值
一.命令行参数解析

  • 如果用户没有传参时直接输出用法help信息
  • 如果带参数则解析命令行参数,并根据传入参数进行相关设置。参数构造是用getopt_long()函数实现,getopt_long()函数函数根据argc来寻找(argv,”912vfrt:p:c:?h”)这两个字符串匹配的选项。getopt_long()函数将选项解析完成后,读到url不会再读取,此时argv[optind]指向url(optind是命令行参数中未读取的下一个元素)

二.根据1得到的一些参数,进行http请求构造,调用build_request()函数:

  1. 判断应该使用的HTTP协议
  2. 填写http请求第一行请求方法method
  3. url合法性判断:如果没有://则不合法;url过长非法。
  4. 如果无代理服务器:将其网络地址放到host数组中,设置端口。如果没有端口号,直接拷贝域名到host数组中。然后将资源路径填入请求行中。
  5. 如果有代理服务器:直接填入url到请求行中,填入http版本号到请求行中,填入请求报头(NAME:VALUE),填入空行
  6. http请求构造完成

三.进行压力测试,调用bench()函数:(父进程做得工作)

1.进行一次连接合法性测试。先测试一次,服务器是否可以正常连接成功;测试成功,一次连接完成关闭。
2.建立管道通信
3.按照客户端数目派生子进程。传入多少个客户端则建立多少个子进程进行连接。
4.每个子进程调用benchcore函数进行连接请求

benchcore函数是子进程处理发起请求 
 - 创建自定义信号捕捉函数,建立benchtime时间闹钟 
 - 调用socket,得连接套接字 
 - 向网络中写请求
 - 对http0.9的处理,如果关闭不成功则关闭连接写一半 
 - 读取服务器响应消息 
 - 关闭连接 
(以上过程中统计各自进程的失败次数faild,服务器回应字节数bytes,连接成功次数speed)

5.benchcore函数调用完成,每个进程向管道中写入失败次数,服务器回应字节数,连接成功次数
6.父进程打开管道读取每个进程写入的信息,进行统计计算,输出压力测试结果

Socket.c:
1.建立套接字:调用socket();
2.建立连接:调用connect();
3.返回套接字:sock.

优点为:

1.部署简单,适用于小型网站压力测试,(最多可模拟3万并发);
2.它具有静态页面测试能力也支持动态页面(ASP,PHP,JAVA,CGI)进行测试能力;
3.支持对含有SSL的安全网站如电子商务网站进行动态或静态性能测试;

缺点为:

1.不适合中大型网站测试;
2.其并发采用多进程实现并非线程,长时间其会大量占用内存与CPU,所以一般长时间的压力测试不推荐使用webbench.

源代码:
webbench.c

#include "socket.c"

#include <unistd.h>

#include <sys/param.h>

#include <rpc/types.h>

#include <getopt.h>

#include <strings.h>

#include <time.h>

#include <signal.h>



/* values */

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 */

/* 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;

int clients=1;

int force=0;

int force_reload=0;

int proxyport=80;

char *proxyhost=NULL;

int benchtime=30;



/* internal */

int mypipe[2];

char host[MAXHOSTNAMELEN];

#define REQUEST_SIZE 2048

char request[REQUEST_SIZE];



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,'?'},

    {"http09",no_argument,NULL,'9'},

    {"http10",no_argument,NULL,'1'},

    {"http11",no_argument,NULL,'2'},

    {"get",no_argument,&method,METHOD_GET},

    {"head",no_argument,&method,METHOD_HEAD},

    {"options",no_argument,&method,METHOD_OPTIONS},

    {"trace",no_argument,&method,METHOD_TRACE},

    {"version",no_argument,NULL,'V'},

    {"proxy",required_argument,NULL,'p'},

    {"clients",required_argument,NULL,'c'},

    {NULL,0,NULL,0}

};



/* prototypes */

static void benchcore(const char* host,const int port, const char *request);

static int bench(void);

static void build_request(const char *url);



static void alarm_handler(int signal)

{

    timerexpired=1;

}   



static void usage(void)

{

    fprintf(stderr,

            "webbench [option]... URL\n"

            "  -f|--force               Don't wait for reply from server.\n"

            "  -r|--reload              Send reload request - Pragma: no-cache.\n"

            "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"

            "  -p|--proxy <server:port> Use proxy server for request.\n"

            "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"

            "  -9|--http09              Use HTTP/0.9 style requests.\n"

            "  -1|--http10              Use HTTP/1.0 protocol.\n"

            "  -2|--http11              Use HTTP/1.1 protocol.\n"

            "  --get                    Use GET request method.\n"

            "  --head                   Use HEAD request method.\n"

            "  --options                Use OPTIONS request method.\n"

            "  --trace                  Use TRACE request method.\n"

            "  -?|-h|--help             This information.\n"

            "  -V|--version             Display program version.\n"

           );

}



int main(int argc, char *argv[])

{

    int opt=0;

    int options_index=0;

    char *tmp=NULL;



    if(argc==1)

    {

        usage();

        return 2;

    } 



    while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )

    {

        switch(opt)

        {

            case  0 : break;

            case 'f': force=1;break;

            case 'r': force_reload=1;break; 

            case '9': http10=0;break;

            case '1': http10=1;break;

            case '2': http10=2;break;

            case 'V': printf(PROGRAM_VERSION"\n");exit(0);

            case 't': benchtime=atoi(optarg);break;      

            case 'p': 

            /* proxy server parsing server:port */

            tmp=strrchr(optarg,':');

            proxyhost=optarg;

            if(tmp==NULL)

            {

                break;

            }

            if(tmp==optarg)

            {

                fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);

                return 2;

            }

            if(tmp==optarg+strlen(optarg)-1)

            {

                fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);

                return 2;

            }

            *tmp='\0';

            proxyport=atoi(tmp+1);break;

            case ':':

            case 'h':

            case '?': usage();return 2;break;

            case 'c': clients=atoi(optarg);break;

        }

    }



    if(optind==argc) {

        fprintf(stderr,"webbench: Missing URL!\n");

        usage();

        return 2;

    }



    if(clients==0) clients=1;

    if(benchtime==0) benchtime=30;



    /* Copyright */

    fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"

            "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"

            );



    build_request(argv[optind]);



    // print request info ,do it in function build_request

    //printf("Benchmarking: ");



    switch(method)

    {

        case METHOD_GET:

        default:

        printf("GET");break;

        case METHOD_OPTIONS:

        printf("OPTIONS");break;

        case METHOD_HEAD:

        printf("HEAD");break;

        case METHOD_TRACE:

        printf("TRACE");break;

    }



    printf(" %s",argv[optind]);



    switch(http10)

    {

        case 0: printf(" (using HTTP/0.9)");break;

        case 2: printf(" (using HTTP/1.1)");break;

    }



    printf("\n");





    printf("Runing info: ");



    if(clients==1) 

        printf("1 client");

    else

        printf("%d clients",clients);



    printf(", running %d sec", benchtime);



    if(force) printf(", early socket close");

    if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);

    if(force_reload) printf(", forcing reload");



    printf(".\n");



    return bench();

}



void build_request(const char *url)

{

    char tmp[10];

    int i;



    //bzero(host,MAXHOSTNAMELEN);

    //bzero(request,REQUEST_SIZE);

    memset(host,0,MAXHOSTNAMELEN);

    memset(request,0,REQUEST_SIZE);



    if(force_reload && proxyhost!=NULL && http10<1) http10=1;

    if(method==METHOD_HEAD && http10<1) http10=1;

    if(method==METHOD_OPTIONS && http10<2) http10=2;

    if(method==METHOD_TRACE && http10<2) http10=2;



    switch(method)

    {

        default:

        case METHOD_GET: strcpy(request,"GET");break;

        case METHOD_HEAD: strcpy(request,"HEAD");break;

        case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;

        case METHOD_TRACE: strcpy(request,"TRACE");break;

    }



    strcat(request," ");



    if(NULL==strstr(url,"://"))

    {

        fprintf(stderr, "\n%s: is not a valid URL.\n",url);

        exit(2);

    }

    if(strlen(url)>1500)

    {

        fprintf(stderr,"URL is too long.\n");

        exit(2);

    }

    if (0!=strncasecmp("http://",url,7)) 

    { 

        fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");

        exit(2);

    }



    /* protocol/host delimiter */

    i=strstr(url,"://")-url+3;



    if(strchr(url+i,'/')==NULL) {

        fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");

        exit(2);

    }



    if(proxyhost==NULL)

    {

        /* get port from hostname */

        if(index(url+i,':')!=NULL && index(url+i,':')<index(url+i,'/'))

        {

            strncpy(host,url+i,strchr(url+i,':')-url-i);

            //bzero(tmp,10);

            memset(tmp,0,10);

            strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);

            /* printf("tmp=%s\n",tmp); */

            proxyport=atoi(tmp);

            if(proxyport==0) proxyport=80;

        } 

        else

        {

            strncpy(host,url+i,strcspn(url+i,"/"));

        }

        // printf("Host=%s\n",host);

        strcat(request+strlen(request),url+i+strcspn(url+i,"/"));

    } 

    else

    {

        // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);

        strcat(request,url);

    }



    if(http10==1)

        strcat(request," HTTP/1.0");

    else if (http10==2)

        strcat(request," HTTP/1.1");



    strcat(request,"\r\n");



    if(http10>0)

        strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");

    if(proxyhost==NULL && http10>0)

    {

        strcat(request,"Host: ");

        strcat(request,host);

        strcat(request,"\r\n");

    }



    if(force_reload && proxyhost!=NULL)

    {

        strcat(request,"Pragma: no-cache\r\n");

    }



    if(http10>1)

        strcat(request,"Connection: close\r\n");



    /* add empty line at end */

    if(http10>0) strcat(request,"\r\n"); 



    printf("\nRequest:\n%s\n",request);

}



/* vraci system rc error kod */

static int bench(void)

{

    int i,j,k;  

    pid_t pid=0;

    FILE *f;



    /* check avaibility of target server */

    i=Socket(proxyhost==NULL?host:proxyhost,proxyport);

    if(i<0) { 

        fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");

        return 1;

    }

    close(i);



    /* create pipe */

    if(pipe(mypipe))

    {

        perror("pipe failed.");

        return 3;

    }



    /* not needed, since we have alarm() in childrens */

    /* wait 4 next system clock tick */

    /*

    cas=time(NULL);

    while(time(NULL)==cas)

    sched_yield();

    */



    /* fork childs */

    for(i=0;i<clients;i++)

    {

        pid=fork();

        if(pid <= (pid_t) 0)

        {

            /* child process or error*/

            sleep(1); /* make childs faster */

            break;

        }

    }



    if( pid < (pid_t) 0)

    {

        fprintf(stderr,"problems forking worker no. %d\n",i);

        perror("fork failed.");

        return 3;

    }



    if(pid == (pid_t) 0)

    {

        /* I am a child */

        if(proxyhost==NULL)

            benchcore(host,proxyport,request);

        else

            benchcore(proxyhost,proxyport,request);



        /* write results to pipe */

        f=fdopen(mypipe[1],"w");

        if(f==NULL)

        {

            perror("open pipe for writing failed.");

            return 3;

        }

        /* fprintf(stderr,"Child - %d %d\n",speed,failed); */

        fprintf(f,"%d %d %d\n",speed,failed,bytes);

        fclose(f);



        return 0;

    } 

    else

    {

        f=fdopen(mypipe[0],"r");

        if(f==NULL) 

        {

            perror("open pipe for reading failed.");

            return 3;

        }



        setvbuf(f,NULL,_IONBF,0);



        speed=0;

        failed=0;

        bytes=0;



        while(1)

        {

            pid=fscanf(f,"%d %d %d",&i,&j,&k);

            if(pid<2)

            {

                fprintf(stderr,"Some of our childrens died.\n");

                break;

            }



            speed+=i;

            failed+=j;

            bytes+=k;



            /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */

            if(--clients==0) break;

        }



        fclose(f);



        printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",

            (int)((speed+failed)/(benchtime/60.0f)),

            (int)(bytes/(float)benchtime),

            speed,

            failed);

    }



    return i;

}



void benchcore(const char *host,const int port,const char *req)

{

    int rlen;

    char buf[1500];

    int s,i;

    struct sigaction sa;



    /* setup alarm signal handler */

    sa.sa_handler=alarm_handler;

    sa.sa_flags=0;

    if(sigaction(SIGALRM,&sa,NULL))

        exit(3);



    alarm(benchtime); // after benchtime,then exit



    rlen=strlen(req);

    nexttry:while(1)

    {

        if(timerexpired)

        {

            if(failed>0)

            {

                /* fprintf(stderr,"Correcting failed by signal\n"); */

                failed--;

            }

            return;

        }



        s=Socket(host,port);                          

        if(s<0) { failed++;continue;} 

        if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}

        if(http10==0) 

        if(shutdown(s,1)) { failed++;close(s);continue;}

        if(force==0) 

        {

            /* read all available data from socket */

            while(1)

            {

                if(timerexpired) break; 

                i=read(s,buf,1500);

                /* fprintf(stderr,"%d\n",i); */

                if(i<0) 

                { 

                    failed++;

                    close(s);

                    goto nexttry;

                }

                else

                if(i==0) break;

                else

                bytes+=i;

            }

        }

        if(close(s)) {failed++;continue;}

        speed++;

    }

}

socket.c

#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>



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

    {

        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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值