「源码分析」 性能压测工具-webbench(性能测试分析工具)

一,webbench简介

今天和大家介绍一个网站压测性能测试工具,名字叫webbench。

  • a) 可测试相同硬件,不同服务的性能及不同硬件上同一个服务的运行状况。
  • b) 可以向我们展示服务器:每秒钟相应的请求数和每秒钟传输的数据量。
  • c) 可以模拟3万个并发连接去测试网站的负载能力。

同时,webbench的代码非常精简,源代码只有六百多行代码C语言编写。适合学习和分析。学习后,可修改成其他的性能测试工具。

嵌入式进阶教程分门别类整理好了,看的时候十分方便,由于内容较多,这里就截取一部分图吧。

「源码分析」 性能压测工具-webbench(性能测试分析工具)

需要的朋友私信【内核】即可领取

二,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知识等。是一个很好的源码学习工具。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年7月6日 上午10:12
下一篇 2022年7月6日 上午10:26

相关推荐

  • 招生管理系统项目

    招生管理系统项目 招生管理系统是一种用于协助学校管理招生过程的软件系统。该系统旨在帮助学校更好地管理招生信息、招聘程序、成绩评估和录取过程,从而提高学校的竞争力和招生效率。 下面是…

    科研百科 2024年12月11日
    0
  • 项目管理系统方案

    项目管理系统是一种用于管理和跟踪项目的工具,能够帮助组织更好地规划和控制项目进度,提高团队协作效率,减少项目风险。本文将介绍一种基于云计算的项目管理系统方案,包括其功能、设计、实现…

    科研百科 4天前
    0
  • 支持网盘文件解压缩的文档管理应用(限时免费)可播视音传输文件

    xHub 文件君 是一款综合的文档管理 iPad、iPhone 应用,拥有文件浏览,Wi-Fi 文件传输,视频播放,音乐播放,照片浏览,文本编辑和录音等功能,可以对文件搜索/编辑/…

    科研百科 2023年5月3日
    136
  • 比Excel好用!这2款表格制作应用,正在重新定义生产力

    买好了「随心飞」准备出门旅游,该如何制作诱人的攻略吸引朋友一同前往呢?用《Excel》或备忘录发旅行攻略?不仅不够美观,还不够酷。 看看 A 君是怎么用《vika 维格表》做的。 …

    科研百科 2022年11月18日
    151
  • 档案资料管 理方案

    档案资料管理方案 档案资料是组织和管理历史记录的重要工具,也是保存历史文化遗产的重要手段。随着现代社会的发展,档案资料的重要性也越来越凸显。因此,制定一份合理的档案资料管理方案对于…

    科研百科 2024年10月26日
    0
  • MOC管理

    MOC管理(管理成员资格)是指组织中管理成员的认证、授权和管理体系。MOC管理对于组织的成功至关重要,因为它涉及到组织中不同层级和不同领域的成员。在本文中,我们将探讨MOC管理的基…

    科研百科 2024年10月31日
    0
  • 甘肃项目沟通管理系统

    甘肃项目沟通管理系统 随着项目的不断推进,项目沟通管理的重要性也越来越凸显。甘肃项目沟通管理系统是一种能够帮助项目管理人员更好地进行项目沟通的工具,可以提高项目的效率,减少沟通成本…

    科研百科 1天前
    0
  • 2月3-9日,池州政务服务大厅暂停窗口办理业务(池州政务服务大厅上班时间)

    新安晚报安徽网大皖客户端讯2月1日,池州市数据资源局发布《关于疫情防控期间政务服务大厅延期开放的通告》:根据新型冠状病毒感染的肺炎疫情防控工作要求,经池州市疫情防控应急指挥部同意,…

    科研百科 2024年3月13日
    126
  • 建文联营挂靠项目管理系统

    1、施工企业做大的必由之路——联营挂靠 联营施工是指没有资质的实际施工人或资质低的实际施工人借用有资质或资质高的施工企业的名义承揽工程并向其交纳管理费的行为。其重要特征是自负盈亏。…

    科研百科 2022年10月19日
    182
  • 夹子音羞辱,最恶毒的网络暴力(网络用语夹子音是什么意思)

    偌大一个互联网,除了1 1=2之外,可以光速达成共识的观点不多,下面可以算一条。 “只要你也讨厌夹子音,我们就是好朋友。” 根据网络释义,夹子音指人故意夹着嗓子说话,让声线变得非常…

    科研百科 2023年5月9日
    890