一,webbench简介
今天和大家介绍一个网站压测性能测试工具,名字叫webbench。
- a) 可测试相同硬件,不同服务的性能及不同硬件上同一个服务的运行状况。
- b) 可以向我们展示服务器:每秒钟相应的请求数和每秒钟传输的数据量。
- c) 可以模拟3万个并发连接去测试网站的负载能力。
同时,webbench的代码非常精简,源代码只有六百多行代码,C语言编写。适合学习和分析。学习后,可修改成其他的性能测试工具。
嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。
需要的朋友私信【内核】即可领取
二,webbench使用介绍
安装好webbench后,可以查看说明: ./webbench -h
[root@VM_0_9_centos webbench-1.5]# ./webbenchwebbench [option]... URL -f|--force Don't wait for reply from server. -r|--reload Send reload request - Pragma: no-cache. -t|--time <sec> Run benchmark for <sec> seconds. Default 30. -p|--proxy <server:port> Use proxy server for REQUEST. -c|--clients <n> Run <n> HTTP clients at once. Default one. -9|--http09 Use HTTP/0.9 style requests. -1|--http10 Use HTTP/1.0 protocol. -2|--http11 Use HTTP/1.1 protocol. --get Use GET request method. --head Use HEAD request method. --options Use OPTIONS request method. --trace Use TRACE request method. -?|-h|--help This information. -V|--version Display program version.
参数介绍:
- -f :不等待服务器数据返回
- -r :发送重新加载请求
- -t <sec> : 运行总时长,默认30秒
- -p <sever:port> : 设计代码服务器
- -c <clients> : 设置运行多少个客户端进程
- -9/-1/-2: HTTP协议版本
- –get : get请求
- –head: head 请求
- –options: options 请求
- –trace: trace请求
- -?|-h|–help: 帮助信息
- -V|–version: 版本信息
例子:
1. 定时十秒,开启一百个客户端进程请求www.baidu.com
./webbench -t 10 -c 100 http://www.baidu.com/
三,webbench 流程介绍
- 1. 解析命令行参数,并初始化变量。
- 2. 构造HTTP请求
- 3. 选测试一个连接是否正常
- 4. 创建管道:主管理用来读,子管理用来写数据
- 5. 根据客户端数创建子进程
- 6. 每个子进程单独测试,并在定时器到了后,往管道写入自已测试数据
- 7. 主程进收到子进程数据后进行统计
- 8. 最后,主进程收到所有子进程数据后,结束并打印统计数据
四,webbench源码分析
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>// socket创建连接函数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;}
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 */// http版本int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ /* Allow: GET, HEAD, OPTIONS, TRACE */// http请求方法#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 2048char 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]... URLn"" -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=60; /* 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 bench info */ printf("nBenchmarking: "); 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"); 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();} // 2.构造HTTP请求void build_request(const char *url){ char tmp[10]; int i; bzero(host,MAXHOSTNAMELEN); bzero(request,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(proxyhost==NULL) 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; /* printf("%dn",i); */ 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); strncpy(tmp,index(url i,':') 1,strchr(url i,'/')-index(url i,':')-1); /* printf("tmp=%sn",tmp); */ proxyport=atoi(tmp); if(proxyport==0) proxyport=80; } else { strncpy(host,url i,strcspn(url i,"/")); } // printf("Host=%sn",host); strcat(request strlen(request),url i strcspn(url i,"/")); } else { // printf("ProxyHost=%snProxyPort=%dn",proxyhost,proxyport); strcat(request,url); } if(http10==1) strcat(request," HTTP/1.0"); else if (http10==2) strcat(request," HTTP/1.1"); strcat(request,"rn"); if(http10>0) strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"rn"); if(proxyhost==NULL && http10>0) { strcat(request,"Host: "); strcat(request,host); strcat(request,"rn"); } if(force_reload && proxyhost!=NULL) { strcat(request,"Pragma: no-cachern"); } if(http10>1) strcat(request,"Connection: closern"); /* add empty line at end */ if(http10>0) strcat(request,"rn"); // printf("Req=%sn",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(); // 如果创建进程失败或为子进程 // 休眠1秒 // 目的是让主进程继续创建进程 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. %dn",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 %dn",speed,failed); */ // 发送给主进程的数据: speed, failed, bytes fprintf(f,"%d %d %dn",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) { // 读取数据 // 格式:speed failed bytes 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=%dn",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); rlen=strlen(req); nexttry: while(1) { // 超时退出 if(timerexpired) { // 此次试探不算失败 if(failed > 0) { /* fprintf(stderr,"Correcting failed by signaln"); */ failed--; } return; } // 打开socket请求数据 s=Socket(host,port); // 打开失败 if(s<0) { failed ;continue;} // 发送请求失败 // failed // 关闭套接字 if(rlen!=write(s,req,rlen)) {failed ;close(s);continue;} // GET if(http10==0) if(shutdown(s,1)) { failed ;close(s);continue;} // 不强制读取服务器回复 // -f 不读取服务器回复 if(force == 0) { /* read all available data from socket */ // 循环读取数据 while(1) { // 超时退出 if(timerexpired) break; // 单次读到1500字节 i=read(s,buf,1500); /* fprintf(stderr,"%dn",i); */ // 读取失败 // 关闭并重试 if(i < 0) { failed ; close(s); goto nexttry; } else { // 对端关闭退出 if(i==0) break; // 读取成功,统计 else bytes =i; } } } // 关闭此次请求 if(close(s)) {failed ;continue;} speed ; }}
五,总结
webbench 是一个C语言编写的http性能压测试工具,代码非常简结,本人也追求代码的简洁之道。有人统计进,一千行代码中,大概有50- 500个BUG。所以代码精简,不但易于阅读,还能使程序出问题的概率降低。
通过webbench代码的学习,里面包含了:参数设置,进程,管道通信,信号,socket,http知识等。是一个很好的源码学习工具。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。