一、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的主要工作流程:

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

webbench.c源码注释:

 1 /*2 * (C) Radim Kolar 1997-20043 * This is free software, see GNU Public License version 2 for4 * details.5 *6 * Simple forking WWW Server benchmark:7 *8 * Usage:9 *   webbench --help10 *11 * Return codes:12 *    0 - sucess13 *    1 - benchmark failed (server is not on-line)14 *    2 - bad param15 *    3 - internal error, fork failed16 * 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 039 #define METHOD_HEAD 140 #define METHOD_OPTIONS 241 #define METHOD_TRACE 342 #define PROGRAM_VERSION "1.5"43 int method=METHOD_GET;//默认请求方法为GET,同时也支持HEAD、OPTIONS、TRACE44 int clients=1;//并发数目,默认只有一个进程发请求,通过-c参数设置45 int force=0;//是否需要等待读取从服务器返回值的数据,0表示需要读取46 int force_reload=0;//是否使用缓存,1表示不缓存,0表示缓存页面47 int proxyport=80;//代理服务器的端口48 char *proxyhost=NULL;//代理服务器的ip49 int benchtime=30;//测压时间,默认为30秒,通过-t参数设置50 51 /* internal */52 int mypipe[2];//使用管道进行父子进程之间的通信53 char host[MAXHOSTNAMELEN];//服务器端ip54 #define REQUEST_SIZE 204855 char request[REQUEST_SIZE];//所要发送的http请求56 57 static const struct option long_options[]=58 {59     {"force",no_argument,&force,1},60     {"reload",no_argument,&force_reload,1},61     {"time",required_argument,NULL,'t'},62     {"help",no_argument,NULL,'?'},63     {"http09",no_argument,NULL,'9'},64     {"http10",no_argument,NULL,'1'},65     {"http11",no_argument,NULL,'2'},66     {"get",no_argument,&method,METHOD_GET},67     {"head",no_argument,&method,METHOD_HEAD},68     {"options",no_argument,&method,METHOD_OPTIONS},69     {"trace",no_argument,&method,METHOD_TRACE},70     {"version",no_argument,NULL,'V'},71     {"proxy",required_argument,NULL,'p'},72     {"clients",required_argument,NULL,'c'},73     {NULL,0,NULL,0}74 };75 76 /* prototypes */    //函数原型77 static void benchcore(const char* host,const int port, const char *request);78 static int bench(void);79 static void build_request(const char *url);80 81 /*****************************************************************************************函数功能:webbench在运行时可以设定压测的持续时间,以秒为单位。例如我们希望测试30秒,也就意味着压>    测30秒后程序应该退出了。webbench中使用信号(signal)来控制程序结束。函数alarm_handler()是在到达结束时间时运行的信号处理函数。它仅仅是将一个记录是否超时的变量timerexpired标记为true。后面会看到,在程序的while循环中会不断检测此值,只有timerexpired=1,程序才会跳出while循环并返回。82 ******************************************************************************************/83 static void alarm_handler(int signal)//使用信号signal来空哦之程序的结束84 {85     timerexpired=1;86 }87 88 /******************************************************************************************89 函数功能:help信息,-9 -1 -2 分别代表http0.9、http1.0和http1.1协议。90 webbench支持GET,HEAD,OPTIONS,TRACE四种请求方式。91 ******************************************************************************************/92 static void usage(void)93 {94     fprintf(stderr,95             "webbench [option]... URL\n"96             "  -f|--force               Don't wait for reply from server.\n"97             "  -r|--reload              Send reload request - Pragma: no-cache.\n"98             "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"99             "  -p|--proxy <server:port> Use proxy server for request.\n"
100             "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
101             "  -9|--http09              Use HTTP/0.9 style requests.\n"
102             "  -1|--http10              Use HTTP/1.0 protocol.\n"
103             "  -2|--http11              Use HTTP/1.1 protocol.\n"
104             "  --get                    Use GET request method.\n"
105             "  --head                   Use HEAD request method.\n"
106             "  --options                Use OPTIONS request method.\n"
107             "  --trace                  Use TRACE request method.\n"
108             "  -?|-h|--help             This information.\n"
109             "  -V|--version             Display program version.\n"
110            );
111 }
112
113 //主函数main
114 int main(int argc, char *argv[])
115 {
116     int opt=0;
117     int options_index=0;
118     char *tmp=NULL;
119
120     if(argc==1)//不带参数时,直接输出help信息
121     {
122         usage();
123         return 2;
124     }
125
126     //使用getopt_long函数读取命令行参数,来设置全局变量的值,在此期间如果出现错误,会自动调用alarm_handler函数告知用户此工具的使用方法,然后退出
127     while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
128     {
129         switch(opt)
130         {
131             case  0 : break;
132             case 'f': force=1;break;
133             case 'r': force_reload=1;break;
134             case '9': http10=0;break;
135             case '1': http10=1;break;
136             case '2': http10=2;break;
137             case 'V': printf(PROGRAM_VERSION"\n");exit(0);
138             case 't': benchtime=atoi(optarg);break;
139             case 'p':
140             /* proxy server parsing server:port */
141             tmp=strrchr(optarg,':');
142             proxyhost=optarg;
143             if(tmp==NULL)
144             {
145                 break;
146             }
147             if(tmp==optarg)
148             {
149                 fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
150                 return 2;
151             }
152             if(tmp==optarg+strlen(optarg)-1)
153             {
154                 fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
155                 return 2;
156             }
157             *tmp='\0';
158             proxyport=atoi(tmp+1);break;
159             case ':':
160             case 'h':
161             case '?': usage();return 2;break;
162             case 'c': clients=atoi(optarg);break;
163         }
164     }
165
166     //optind被设置为getopt_long设置为命令行参数中被被读取的下一个元素的下标值
167     if(optind==argc) {
168         fprintf(stderr,"webbench: Missing URL!\n");
169         usage();
170         return 2;
171     }
172
173     //不能设置客户端数和请求时间为0
174     if(clients==0) clients=1;
175     if(benchtime==0) benchtime=30;
176
177     /* Copyright */
178     fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
179             "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
180             );
181
182     //构造http请求到request数组,//参数读完后,argv[optind]即放在命令行最后的url
183                                  //调用函数usage建立完整的HTTP request,
184                                  //HTTP request存储在全部变量char request[REQUEST_SIZE]
185     build_request(argv[optind]);
186
187     // print request info ,do it in function build_request:输出提示信息直到函数结束
188     /*printf("Benchmarking: ");*/
189
190     switch(method)
191     {
192         case METHOD_GET:
193         default:
194         printf("GET");break;
195         case METHOD_OPTIONS:
196         printf("OPTIONS");break;
197         case METHOD_HEAD:
198         printf("HEAD");break;
199         case METHOD_TRACE:
200         printf("TRACE");break;
201     }
202
203     printf(" %s",argv[optind]);
204
205     switch(http10)
206     {
207         case 0: printf(" (using HTTP/0.9)");break;
208         case 2: printf(" (using HTTP/1.1)");break;
209     }
210
211     printf("\n");
212
213     printf("Runing info: ");
214
215     if(clients==1)
216         printf("1 client");
217     else
218         printf("%d clients",clients);
219
220     printf(", running %d sec", benchtime);
221
222     if(force) printf(", early socket close");
223     if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
224     if(force_reload) printf(", forcing reload");
225
226     printf(".\n");
227
228     //进行压力测试,返回bench函数的执行结果
229     return bench();
230 }
231
232 //主要操作全局变量char request[REQUEST_SIZE],根据url填充其内容。
233 /******************************************************************************************build_request函数的目的就是要把类似于以上这一大坨信息全部存到全局变量request[REQUEST_SIZE]中,其中换行操作使用的是”\r\n”。而以上这一大坨信息的具体内容是要根据命令行输入的参数,以及url来确定的。该函数使用了大量的字符串操作函数,例如strcpy,strstr,strncasecmp,strlen,strchr,index,strncpy,strcat。
234  ****************************************************************************************/
235 void build_request(const char *url)
236 {
237     char tmp[10];
238     int i;
239
240     //初始化
241     bzero(host,MAXHOSTNAMELEN);
242     bzero(request,REQUEST_SIZE);
243     memset(host,0,MAXHOSTNAMELEN);
244     memset(request,0,REQUEST_SIZE);
245
246     //判断应该使用的http协议
247     if(force_reload && proxyhost!=NULL && http10<1) http10=1;
248     if(method==METHOD_HEAD && http10<1) http10=1;
249     if(method==METHOD_OPTIONS && http10<2) http10=2;
250     if(method==METHOD_TRACE && http10<2) http10=2;
251
252     //填写method方法
253     switch(method)
254     {
255         default:
256         case METHOD_GET: strcpy(request,"GET");break;
257         case METHOD_HEAD: strcpy(request,"HEAD");break;
258         case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
259         case METHOD_TRACE: strcpy(request,"TRACE");break;
260     }
261
262     strcat(request," ");
263
264     //url合法性判断
265     if(NULL==strstr(url,"://"))
266     {
267         fprintf(stderr, "\n%s: is not a valid URL.\n",url);
268         exit(2);
269     }
270     if(strlen(url)>1500)
271     {
272         fprintf(stderr,"URL is too long.\n");
273         exit(2);
274     }
275     if (0!=strncasecmp("http://",url,7))
276     {
277         //只支持http地址
278         fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
279         exit(2);
280     }
281
282     /* protocol/host delimiter:找到主机名开始的地方 */
283     i=strstr(url,"://")-url+3;
284
285     //必须以 / 结束
286     if(strchr(url+i,'/')==NULL) {
287         fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
288         exit(2);
289     }
290
291     if(proxyhost==NULL)
292     {
293         /* get port from hostname */
294         if(index(url+i,':')!=NULL && index(url+i,':')<index(url+i,'/'))
295         {
296             strncpy(host,url+i,strchr(url+i,':')-url-i);
297             bzero(tmp,10);//端口
298             //memset(tmp,0,10);
299             strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
300             /* printf("tmp=%s\n",tmp); */
301             proxyport=atoi(tmp);//设置端口
302             if(proxyport==0) proxyport=80;
303         }
304         else
305         {
306             strncpy(host,url+i,strcspn(url+i,"/"));
307         }
308         // printf("Host=%s\n",host);
309         strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
310     }
311     else
312     {
313         // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
314         strcat(request,url);
315     }
316
317     if(http10==1)
318         strcat(request," HTTP/1.0");
319     else if (http10==2)
320         strcat(request," HTTP/1.1");
321
322     strcat(request,"\r\n");
323
324     if(http10>0)
325         strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
326     if(proxyhost==NULL && http10>0)
327     {
328         strcat(request,"Host: ");
329         strcat(request,host);
330         strcat(request,"\r\n");
331     }
332
333     if(force_reload && proxyhost!=NULL)
334     {
335         strcat(request,"Pragma: no-cache\r\n");
336     }
337
338     if(http10>1)
339         strcat(request,"Connection: close\r\n");
340
341     /* add empty line at end */
342     if(http10>0) strcat(request,"\r\n");
343
344     printf("\nRequest:\n%s\n",request);
345 }
346
347 /* vraci system rc error kod */
348 /******************************************************************************************函数先进行了一次socket连接,确认能连通以后,才进行后续步骤。调用pipe函数初始化一个管道,用于子进行向父进程汇报测试数据。子进程根据clients数量fork出来。每个子进程都调用函数benchcore进行测试,并将结果输出到管道,供父进程读取。父进程负责收集所有子进程的测试数据,并汇总输出。
349  *****************************************************************************************/
350 static int bench(void)
351 {
352     int i,j,k;
353     pid_t pid=0;
354     FILE *f;
355
356     /* check avaibility of target server */
357     i=Socket(proxyhost==NULL?host:proxyhost,proxyport);//测试地址是否合法
358     if(i<0) {
359         fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
360         return 1;
361     }
362     close(i);
363
364     /* create pipe */
365     if(pipe(mypipe))//创建管道,用于子进程项父进程回报数据
366     {
367         perror("pipe failed.");
368         return 3;
369     }
370
371     /* not needed, since we have alarm() in childrens */
372     /* wait 4 next system clock tick */
373     /*
374     cas=time(NULL);
375     while(time(NULL)==cas)
376     sched_yield();
377     */
378
379     /* fork childs */
380     for(i=0;i<clients;i++)
381     {
382         pid=fork();
383         if(pid <= (pid_t) 0)
384         {
385             /* child process or error*/
386             sleep(1); /* make childs faster */
387             break;//子进程立刻跳出循环,要不子进程就立刻fork了
388         }
389     }
390
391     if( pid < (pid_t) 0)
392     {
393         fprintf(stderr,"problems forking worker no. %d\n",i);
394         perror("fork failed.");
395         return 3;
396     }
397
398     if(pid == (pid_t) 0)
399     {
400         /* I am a child */   //子进程发出实际请求
401         if(proxyhost==NULL)
402             benchcore(host,proxyport,request);
403         else
404             benchcore(proxyhost,proxyport,request);
405
406         /* write results to pipe */
407         f=fdopen(mypipe[1],"w");//打开管道写
408         if(f==NULL)
409         {
410             perror("open pipe for writing failed.");
411             return 3;
412         }
413         /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
414         fprintf(f,"%d %d %d\n",speed,failed,bytes);
415         fclose(f);
416
417         return 0;
418     }
419     else
420     {
421         f=fdopen(mypipe[0],"r");//父进程打开管道读
422         if(f==NULL)
423         {
424             perror("open pipe for reading failed.");
425             return 3;
426         }
427
428         setvbuf(f,NULL,_IONBF,0);
429
430         speed=0;//传输速度
431         failed=0;//失败请求数
432         bytes=0;//传输字节数
433
434         while(1)
435         {
436             pid=fscanf(f,"%d %d %d",&i,&j,&k);
437             if(pid<2)
438             {
439                 fprintf(stderr,"Some of our childrens died.\n");
440                 break;
441             }
442
443             speed+=i;
444             failed+=j;
445             bytes+=k;
446
447             /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
448             if(--clients==0) break;//子进程是否读取完
449
450         }
451
452         fclose(f);
453
454         //计算结果
455         printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
456             (int)((speed+failed)/(benchtime/60.0f)),
457             (int)(bytes/(float)benchtime),
458             speed,
459             failed);
460     }
461
462     return i;
463 }
464
465 /******************************************************************************************函数功能:benchcore是子进程进行压力测试的函数,被每个子进程调用。这里使用了SIGALRM信号来控制时间,alarm函数设置了多少时间之后产生SIGALRM信号,一旦产生此信号,将运行函数(1),使得timerexpired=1,这样可以通过判断timerexpired值来退出程序。另外,全局变量force表示我们是否在发出请求后需要等待服务器的响应结果。
466  * ***************************************************************************************/
467 void benchcore(const char *host,const int port,const char *req)
468 {
469     int rlen;
470     char buf[1500];//记录服务器响应请求所返回的数据
471     int s,i;
472     struct sigaction sa;
473
474     /* setup alarm signal handler */
475     sa.sa_handler=alarm_handler;//设置函数alarm_handler为信号处理函数
476     sa.sa_flags=0;
477     if(sigaction(SIGALRM,&sa,NULL))//超时会产生信号SIGALRM,用sa中的指定函数处理
478         exit(3);
479
480     //开始计时
481     alarm(benchtime); // after benchtime,then exit
482
483     rlen=strlen(req);
484     nexttry:while(1)
485     {
486         if(timerexpired)//一旦超时则返回
487         {
488             if(failed>0)
489             {
490                 /* fprintf(stderr,"Correcting failed by signal\n"); */
491                 failed--;
492             }
493             return;
494         }
495
496         s=Socket(host,port);//调用Socket函数建立TCP连接
497         if(s<0) { failed++;continue;}  //发出请求
498         if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}
499         if(http10==0) //针对http0.9做的特殊处理
500         if(shutdown(s,1)) { failed++;close(s);continue;}
501         if(force==0) //全局变量force表示是否要等待服务器返回的数据
502         {
503             /* read all available data from socket */
504             while(1)
505             {
506                 if(timerexpired) break;
507                 i=read(s,buf,1500);//从socket读取返回数据
508                 /* fprintf(stderr,"%d\n",i); */
509                 if(i<0)
510                 {
511                     failed++;
512                     close(s);
513                     goto nexttry;
514                 }
515                 else
516                 if(i==0) break;
517                 else
518                 bytes+=i;
519             }
520         }
521         if(close(s)) {failed++;continue;}
522         speed++;
523     }
524 }

总结:
1、压力测试工作应该放到产品上线之前,而不是上线以后;
2、测试时并发应当由小逐渐加大,比如并发100时观察一下网站负载是多少、打开页面是否流畅,并发200时又是多少、网站打开缓慢时并发是多少、网站打不开时并发又是多少;
3、更详细的进行某个页面测试,如电商网站可以着重测试购物车、推广页面等,因为这些页面占整个网站访问量比重较大。

疑问及解答:
线程占用的空间比进程要小,而且线程切换的时间开销也小,但为什么程序的实现上采用的是fork进程而不是使用多线程呢?
答:因为默认情况下:

主线程+辅助线程<253个自己的线程<=256

含主线程和一个辅助线程,最多255个,即一个用户只能生成253个线程。

而进程的最大数目则跟系统本身限制相关。

2.webbench中在多个进程进行写管道的情况下,在代码中没有采取同步措施,程序是如何保持数据正确呢?

答:管道写的数据不大于 PIPE_BUF 时,系统可以保证写的原子性。在2.6.29内核中,\include\linux\limits.h定义:

#define PIPE_BUF 4096

涉及到的知识点有:

命令行参数解析(getopt_long)、 信号处理(sigaction)、 socket 管道读写 。

源码中的一些函数用法及启示:

getoptlong():

在写程序时常常需要对命令行参数进行处理,当命令行参数个数较多时,如果按照顺序一个一个定义参数含义很容易造成混乱,而且如果程序只按顺序处理参数的话,一些“可选参数”的功能将很难实现。

在Linux中,可以使用getopt、getopt_long、getopt_long_only来对处理这个问题。

sleep(1)的功能:

让CPU能够空闲下来,不至于使CPU使用率一直高居不下;本线程放弃cpu时间片,其他线程处理之后,再开始本线程,多线程处理socket接收发送的时候经常这样处理,防止接收发送出现问题。

Web bench源码剖析相关推荐

  1. Mongoose源码剖析:外篇之web服务器

    引言 在深入Mongoose源码剖析之前,我们应该清楚web服务器是什么?它提供什么服务?怎样提供服务?使用什么协议?客户端如何唯一标识web服务器的资源?下面我们抛开Mongoose,来介绍一个we ...

  2. Mongoose源码剖析:Introduction and Installation

    引言 要剖析Mongoose的源码,首先你得知道它的一些基本情况和特性.并去使用它.本文就是介绍Mongoose是个什么东西?及如何安装和使用?这里假设你知道什么web服务器软件.web服务器使用什么 ...

  3. ThreadLocal源码剖析

    目录 一.ThreadLocal 1.1源码注释 1.2 源码剖析 散列算法-魔数0x61c88647 set操作 get操作 remove操作 1.3 功能测试 1.4 应用场景 二.变量可继承的T ...

  4. Vue 3源码剖析,看这篇就够了

    大家好,我是若川.源码的重要性相信不用再多说什么了吧,特别是用Vue 框架的,一般在面试的时候面试官多多少少都会考察源码层面的内容,比如: 如何理解虚拟Dom? Vue 3为什么这么快? Vue 3的 ...

  5. tomcat(12)org.apache.catalina.core.StandardContext源码剖析

    [0]README 0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(12)StandardContext源码剖析" 的 ...

  6. tomcat(11)org.apache.catalina.core.StandardWrapper源码剖析

    [0]README 0.0)本文部分文字描述转自 "how tomcat works",旨在学习 "tomcat(11)StandardWrapper源码剖析" ...

  7. 菜鸟nginx源码剖析

    菜鸟nginx源码剖析 配置与部署篇(一) 手把手配置nginx "I love you"  TCMalloc 对MYSQL 性能 优化的分析 菜鸟nginx源码剖析系列文章解读 ...

  8. SpringMVC子父容器源码剖析

    SpringMVC子父容器源码剖析 一.子父容器启动流程 二.环境准备 spring源码搭建 spring-framework-5.1.x 源码编译 环境搭建 [ idea:2020.1 ] 在spr ...

  9. underscore源码剖析之整体架构

    前言 最近打算好好看看underscore源码,一个是因为自己确实荒废了基础,另一个是underscore源码比较简单,比较易读. 本系列打算对underscore1.8.3中关键函数源码进行分析,希 ...

最新文章

  1. 重磅!脑机接口突破登Nature封面,脑中“笔迹”转屏幕文字破纪录,准确率超99%...
  2. 苹果手机无线充电板外部电磁场测试
  3. jquery实现截取pc图片_jquery实现图片裁剪思路及实现
  4. linux设定时间查看文件,查看linux系统,服务,配置文件被修改的时间
  5. 关于手机横屏打开相机或者相册闪退解决方案
  6. C# 10 新特性 —— 命名空间的变化
  7. php自己编译扩展,Linux编译PHP添加扩展库的方法
  8. 游戏筑基开发之结构体(数组、指针)、枚举、共用体、typdef(C语言)
  9. 文字表情 emoji 解析大全
  10. 用74ls90组成二十四进制计数器_尘埃粒子计数器的工作原理和应用分析
  11. LightOJ 1336
  12. 【EI稳定检索】第二届计算机科学、电子信息工程和智能控制技术国际会议(CEI 2022)
  13. php重构求圆柱圆锥的体积,圆锥的体积 - 彭阳县第二小学数学教研社区 - 宁夏教育云...
  14. android输入法剪贴板,QQ输入法手安卓V5.4剪贴板 任性粘贴
  15. Phyton安装MySQL驱动
  16. 如何取悦自己或者增加自己幸福感的方式
  17. 传统管理软件已死 智能+时代将如何“浴火重生”?
  18. 计算机网络--虚拟局域网
  19. 解决idea依赖的包突然失效,本地的maven仓库也有,但是代码里面就是引不进来的问题
  20. 自己动手做一台linux瘦客户机

热门文章

  1. 【C语言初阶】C语句成员1:分支语句
  2. [Django] drf filter过滤
  3. html如何添加pyecharts,pyecharts入门
  4. java线程模拟银行取款_多线程模拟银行取款
  5. java游戏逻辑 安排房间_捕鱼游戏 java
  6. deepin安装xfce4
  7. DRBD概念、原理和问题
  8. 硬件基础之STM32最小系统
  9. Java 输出Map中元素
  10. 计算机毕业设计之避坑指南(开题到答辩每步都重要)--告诉你如何顺利答辩毕业