精华内容
下载资源
问答
  • webbench加上帖子 这是一个基于webbench-1.5的项目。 POST方法已添加,用户可以指定多个自定义HTTP标头。 安装:make && make安装。 用法: 1.内容类型:application / x-www-form-urlencoded webbench-发布内容...
  • Webbench1.5源码,可以使用,并且附带详细注释,并且经过修改,可以适用于长连接。。。。。。。。。
  • Webbench:Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设置的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用...
  • webbench [选项]... URL -f|--force 不要等待服务器的回复。 -r|--reload 发送重新加载请求 - Pragma: no-cache。 -t|--time 运行基准测试几秒钟。 默认 30。 -p|--proxy server:port 使用代理服务器
  • Webbench是一个非常简单的压力测试工具,Webbench最多可以模拟3万个并发连接去测试网站的负载能力。 (1)Webbench安装 代码如下: wget //www.jb51.net/soft/linux/webbench-1.5.tar.gz tar zxvf webbench-1.5.tar....
  • 一、Webbench简单介绍  在一个网站上线前, 通常我们应该做一些相关的压力测试, 以便了解当前Web服务器在高并发高负载情况下的响应状况和速度,方便对Web服务器进行优化和重构。目前有很多免费的web压力测试工具...
  • webbench-1.5.tar.gz.zip

    2020-09-14 11:28:53
    在运维工作中,压力测试是一项很重要的工作。比如在一个网站上线之前,能承受多大访问量、在大访问量情况下性能怎样,...Webbench是知名的网站压力测试工具,它是由Lionbridge公司(http://www.lionbridge.com)开发。
  • 压测工具webbench-1.5.zip

    2021-09-01 10:59:52
    Webbench是知名的网站压力测试工具,它是由Lionbridge公司开发。Web Bench 是用于对 WWW 或代理服务器进行基准测试的非常简单的工具。使用 fork() 模拟多个客户端,可以使用 HTTP/0.9-HTTP/1.1 请求。这个基准测试...
  • webbench-1.5.tar.gz

    2020-09-30 14:25:25
    Webench是一款轻量级的网站测压工具,最多可以对网站模拟3w左右的并发请求,...webbench 做压力测试时,最多模拟3万个客户端,该软件自身也会消耗CPU和内存资源,为了测试准确,请将 webbench 安装在单独的服务器上。
  • Webbench1.5

    2018-09-12 16:08:26
    Webbench 是一个在 linux 下使用的非常简单的网站压测工具。它使用 fork ()模拟多个客户端同时访问我们设定的 URL,测试网站在压力下工作的性能,最多可以模拟 3 万个并发连接去测试网站的负载能力。Webbench 使用...
  • webbench源码

    2017-12-31 16:33:14
    webbench是一款轻量级的性能压测工具,通过剖析源码可以使你更能对其有更深 的理解
  • WebBench

    2017-12-21 20:54:54
    WebBench平台 下载 安装 使用 WebBench1.平台 Mac OS X 2.下载 *下载之前请保证系统已经安装brew | wget工具 # -- 1.安装相关依赖 $: brew install ctags# -- 2.下载webbench包 $: wget ...

    WebBench

    1.平台

    Mac OS X

    2.下载

    *下载之前请保证系统已经安装brew | wget工具

    # -- 1.安装相关依赖
    $: brew install ctags
    
    # -- 2.下载webbench包
    $: wget http://blog.zyan.cc/soft/linux/webbench/webbench-1.5.tar.gz
    
    # -- 解压
    $: tar -zxf webbench-1.5.tar.gz
    
    3.安装
    # -- 进入webbench目录
    $: cd webbench-1.5
    
    # -- 建立webbench的安装路径(webbench默认安装在/usr/local/man/man1)
    $: sudo mkdir -pv /usr/local/man/man1
    
    # -- 安装
    $: make
    $: sudo make install
    
    # -- 测试
    $: webbench --version
    4.使用
    # -- ["-c" 并发数 "-t" 运行测试时间 "URL" 测试地址]
    # -- 使用
    $: webbench -c 10000 -t 120 URL
    # -- 例如(测试本地地址)
    $: webbench -c 10000 -t 120 http://localhost:1234/
    展开全文
  • webbench 5.0

    2013-11-14 10:34:58
    webbench5.0 web压力测试工具,windows下的,包含客户端和控制端
  • webbench压力测试

    2018-07-27 15:24:59
    利用webbench进行压力测试的笔记 对后台读写能力进行有效的评估
  • WebBench测试工具.rar

    2019-10-02 12:46:57
    在官方给的webBench上修改了代码,可以支持长短连接 支持http09 10 11分别使用参数 -k -9 -1 -2
  • webbench:其为linux上一款web性能压力测试工具,它最多可以模拟3万个并发连接数来测试服务器压力,其原理为fork多个子进程,每个子进程都循环做web访问测试,子进程将访问的结果通过管道告诉父进程,父进程做最终...
  • WebBench(c++版本)

    2017-12-20 14:21:49
    这是我自己根据原作者WebBench改的,我之前上传过原作者的WebBench。我的版本使用c++作为开发语言,多线程压测网站,使用锁保护数据
  • webbench

    2019-12-09 13:43:10
    1、官网 http://home.tiscali.cz/~cz210552/webbench.html 2、下载 wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar....tar -xzvf webbench-1.5.tar.gz cd webbench-1.5 mkdir /usr/local/man ma...

    1、官网
    http://home.tiscali.cz/~cz210552/webbench.html
    2、下载
    wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.gz
    3、安装

    tar -xzvf webbench-1.5.tar.gz
    cd webbench-1.5
    mkdir /usr/local/man
    make install clean

    4、参数
    只支持get请求,可设定head参数
    常用参数:
    -c 并发用户数
    -t 压测时长

    webbench [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.

    5、结果分析

    webbench -t 60 -c 100 http://www.baidu.com/

    Speed 为每分钟多个个请求
    Requests 成功多少个请求,失败多少个请求

    Webbench - Simple Web Benchmark 1.5
    Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.

    Benchmarking: GET http://www.baidu.com/
    100 clients, running 60 sec.

    Speed=2643 pages/min, 5045450 bytes/sec.
    Requests: 2641 susceed, 2 failed.
     

    展开全文
  • 数据库性能基准测试 DB-Webbench ,Webbench 是一款数据库性能基准测试程序,模拟博客真实应用对数据库所做操作进行性能测试。不单单...
  • webbench:Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设置的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用...
  • webbench.rar

    2015-08-06 20:50:26
    Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用C语言编写, ...
  • 网络平台 网络http高并发测试工具,可使用该工具进行并发的压力测试。 说明:这是webbench1.5的源码。细微的修改,增加了多个不同类型的错误变量,以使其结果更加准确
  • Webbench

    2020-07-27 00:23:04
    简介 Webench是一款轻量级的网站测压工具,最多可以对网站模拟3w左右的并发请求,可以控制时间、是否使用缓存、是否等待服务器回复等等,且对中小型网站有明显的效果,基本上...webbench的标准测试可以向我们展示服务

    简介

    Webench是一款轻量级的网站测压工具,最多可以对网站模拟3w左右的并发请求,可以控制时间、是否使用缓存、是否等待服务器回复等等,且对中小型网站有明显的效果,基本上可以测出中小型网站的承受能力,对于大型的网站,如百度、淘宝这些巨型网站没有意义,因为其承受能力非常大。同时测试结果也受自身网速、以及自身主机的性能与内存的限制,性能好、内存大的主机可以模拟的并发就明显要多。
    Webbench能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webbench的标准测试可以向我们展示服务器的两项内容:每秒钟相应请求数和每秒钟传输数据量。webbench不但具有静态页面的测试能力,还能对动态页面(ASP,PHP,JAVA,CGI)进行测试的能力。还有就是他支持对含有SSL的安全网站例如电子商务网站进行静态或动态的性能测试。

    安装

    github:地址
    下载压缩文件 webbench-1.5.tar.gz
    解压,编译,在当前目录生成webbench可执行文件

    用法

    通过帮助信息了解

    webbench [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.
    

    示例

    通过访问http://www.baidu.com/来测试

    1个客户端,持续60s

    $./webbench -c 1 -t 60 http://www.baidu.com/
    Webbench - Simple Web Benchmark 1.5
    Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
    
    Benchmarking: GET http://www.baidu.com/
    1 client, running 60 sec.
    
    Speed=183 pages/min, 873009 bytes/sec.
    Requests: 183 susceed, 0 failed.
    

    100个客户端,持续30s

    $./webbench -c 100 -t 30 http://www.baidu.com/
    Webbench - Simple Web Benchmark 1.5
    Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
    
    Benchmarking: GET http://www.baidu.com/
    100 clients, running 30 sec.
    
    Speed=162 pages/min, 1106176 bytes/sec.
    Requests: 78 susceed, 3 failed.
    

    涉及知识点

    命令行解析
    进程创建
    IPC(管道)
    socket

    主要函数

    main:函数入口,命令行解析
    usage:帮助信息
    build_request:通信报文创建
    bench:进程创建,父进程接收子进程的运行结果
    benchcore:通信测试
    Socket:创建tcp连接
    alarm_handler:定时处理

    主要流程

    流程图

    注释源码

    socket.c

    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;
    
    	/* ip或域名赋值 */
        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;
        
        /* 连接指定ip或域名 */
        if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
            return -1;
        
        /* 返回套接字 */
        return sock;
    }
    

    webbench.c

    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;
    		}
    	}
    
    	/* 判断是否存在URL */
    	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();
    }
    
    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("%d\n",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=%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("Req=%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);
    
    	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++;
    	}
    }
    
    

    结语

    1、该开源方案的代码风格不方便阅读,所以拿到代码,首先调整格式
    2、大量使用全局变量,不方便阅读,看上去也很乱,可以使用结构体把相关变量规整
    3、进程消耗的资源比较大,所以可以尝试使用线程的方式来优化
    4、作者提供了一个非常好的测试网页的思路,有很多方面是值得学习的

    展开全文
  • webbench-1.5Project

    2017-03-28 10:23:15
    将linux下的开源项目webbench-1.5置于vs2017
  • Webbench实现与详解

    2019-11-26 16:00:51
    Webbench是一款轻量级的网站测试工具,最多可以对网站模拟3w左右的并发请求,可以控制时间、是否使用缓存、是否等待服务器回复等等,且对中小型网站有明显的效果,基本上可以测出中小型网站的承受能力,对于大型的...

    Webbench是一款轻量级的网站测试工具,最多可以对网站模拟3w左右的并发请求,可以控制时间、是否使用缓存、是否等待服务器回复等等,且对中小型网站有明显的效果,基本上可以测出中小型网站的承受能力,对于大型的网站,如百度、淘宝这些巨型网站没有意义,因为其承受能力非常大。同时测试结果也受自身网速、以及自身主机的性能与内存的限制,性能好、内存大的主机可以模拟的并发就明显要多。

    Webbench用C语言编写,运行于linux平台,下载源码后直接编译即可使用,非常迅速快捷,对于中小型网站的制作者,在上线前用webbench进行系列并发测试不失为一个好的测试方法。

    使用方法也是非常的简单,例如对百度进行测试。

    Webbench实现的核心原理是:父进程fork若干个子进程,每个子进程在用户要求时间或默认的时间内对目标web循环发出实际访问请求,父子进程通过管道进行通信,子进程通过管道写端向父进程传递在若干次请求访问完毕后记录到的总信息,父进程通过管道读端读取子进程发来的相关信息,子进程再时间到后结束,父进程在所有子进程退出后统计并给用户显示最后的测试结果,然后退出。

    源代码主要有三个源文件:Socket.c\Socket.h\webbench.c

    其中Socket.c与Socket.h封装了对于目标网站的TCP套接字的构造,其中Socket函数用于获取连接目标网站TCP套接字,这一部分后面就不做说明了,重点讲解webbench.c,其中有些对于各种错误的处理就不在此加以说明了,详见末尾的源码。

    详细实现过程


    命令行参数解析

    该模块由build_request函数实现,该函数流程如下

    这个模块需要进行大量的错误分析,分析用户有可能出现的错误填写,并及时终止,如果在这一步不做好这件事,后面会出现几乎找不出的错误。然后就是在填写报文时,一定要严格的注意格式,不要随意少空格、回车换行,保证报文填写的正确。

    压力测试

    这一模块由bench函数来完成

    这一过程中关键是恰当的利用管道在父子进程间通信,其中函数benchcore函数时每个子进程要求时间内发送请求报文的函数,该函数会记录请求的成功次数、失败次数、以及服务器回复的字数。

    过程分析得再清楚,不如代码清除来得好:

    #include "Socket.h"
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/param.h>
    #include <rpc/types.h>
    #include <getopt.h>
    #include <strings.h>
    #include <time.h>
    #include <signal.h>
    #include <string.h>
    #include <error.h>
    
    static void usage(void) {
        fprintf(stderr, 
            "webbench [选项参数]... URL\n"
            "  -f|--force        不等待服务器响应\n"
            "  -r|--reload       重新请求加载(无缓存)\n"
            "  -t|--time <sec>   运行时间,单位:秒,默认为30秒\n"
            "  -p|--proxy <server:port>  使用代理服务器发送请求\n"
            "  -c|--clients <n>          创建多少个客户端,默认为1个\n"
            "  -9|--http09               使用http0.9协议来构造请求\n"
            "  -1|--http10               使用http1.0协议来构造请求\n"
            "  -2|--http11               使用http1.1协议来构造请求\n"
            "  --get                     使用GET请求方法\n"
            "  --head                    使用HEAD请求方法\n"
            "  --options                 使用OPTIONS请求方法\n"
            "  --trace                   使用TRACE请求方法\n"
            "  -?|-h|--help              显示帮助信息\n"
            "  -V|--version              显示版本信息\n");
    };
    
    //http请求方法
    #define METHOD_GET 0
    #define METHOD_HEAD 1
    #define METHOD_OPTIONS 2
    #define METHOD_TRACE 3
    
    //相关参数选项的默认值
    int method = METHOD_GET;
    int clients = 1;
    int force = 0; //默认需要等待服务器响应
    int force_reload = 0;  //默认不重新发送请求
    int proxyport = 80;   //默认访问80端口,http国际惯例
    char *proxyhost = NULL;  //默认无代理服务器,因此初值为空
    int benchtime = 30; //默认模拟请求时间
    
    //所用协议版本
    int http = 1; //0:http0.9 1:http1.0 2:http1.1
    int clients = 1;
    int force = 0; //默认需要等待服务器响应
    int force_reload = 0; //默认不重新发送请求
    int proxyport = 80;  //默认访问80端口,http国际惯例
    char *proxyhost = NULL; //默认无代理服务器,因此初值为空
    int benchtime = 30; //默认模拟请求时间
    
    //所用协议版本
    int http = 1; //0:http0.9 1:http1.0 2:http1.1
    
    //用于父子进程通信的管道
    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}
    };
    
    int speed = 0;
    int failed = 0;
    long long bytes = 0;
    int timeout = 0;
    void build_request(const char *url);
    static int bench();
    static void alarm_handler(int signal);
    void benchcore(const char *host, const int port, const char *req);
    
    int main(int argc, char *argv[]) {
        int opt = 0;
        int options_index = 0;
        char *tmp = NULL;
    
        //首先进行命令行参数的处理
        //1.没有输入选项
        if (argc == 1) {
            usage();
            return 1;
        }
    
        //2.有输入选项则一个一个解析
        while ((opt = getopt_long(argc, argv, "frt:p:c:?V912", long_options, &options_index)) != NULL) {
            switch (opt) {
                case 'f':
                    force = 1;
                    break;
                case 'r':
                    force_reload = 1;
                    break;
                case '9':
                    http = 0;
                    break;
                case '1':
                    http = 1;
                    break;
                case '2':
                    http = 2;
                    break;
                case 'V':
                    printf("WebBench 1.5 convered by fh\n");
                    exit(0);
                case 't':
                    benchtime = atoi(optarg); //optarg指向选项后的参数
                    break;
                case 'c':
                    clients = atoi(optarg);  //与上同
                    break;
                case 'p':  //使用代理服务器,则设置其代理网络号和端口号,格式: -p server:port
                    tmp = strrchr(optarg, ':'); //查找':'在optarg中最后出现的位置
                    proxyhost = optarg;   
                    if (tmp == NULL) { //说明没有端口号
                        break;
                    }
                    if (tmp == optarg) { //端口号在optarg最开头,说明缺失主机名
                        fprintf(stderr, "选项参数错误,代理服务器 %s:缺失主机名", optarg);
                        return 2;
                    }
                    if (tmp == optarg + strlen(optarg) - 1) {  //':'在optarg末位,说明缺少端口号
                        fprintf(stderr, "选项参数错误,代理服务器 %s 缺少端口号", optarg);
                        return 2;
                        
                        *tmp = '\0';  //将optarg从':'开始截断
                        proxyport = atoi(tmp+1); //把代理服务器端口号设置好
                        break;
                case '?':
                    usage();
                    exit(0);
                    break;
                default:
                    usage();
                    return 2;
                    break;
            }
        }
    
        //选项参数解析完毕后,刚好是读到URL,此时argv[optind]指向URL
        if (optind == argc) { //这样说明没有输入URL,不明白的话自己写一条命令行看看
            fprintf(stderr, "缺少URL参数\n");
            usage();
            return 2;
        }
        if (benchtime == 0) 
            benchtime = 30;
    
        fprintf(stderr, "webbench: 一款轻巧的网站测压工具");
        build_request(argv[optind]); //参数当然是URL
        
        //请求报文构造好了
        //开始测压
        printf("\n测试中: \n");
    
        switch (method) {
            case METHOD_OPTIONS:
                printf("OPTIONS");
                break;
            case METHOD_HEAD:
                printf("HEAD");
                break;
            case METHOD_TRACE:
                printf("TRACE");
                break;
            case METHOD_GET:
            default:
                printf("GET");
                break;
        }
    
        printf(" %s", argv[optind]);
        switch (http) {
            case 0:
                printf("(使用 HTTP/0.9)");
                break;
            case 1:
                printf("(使用 HTTP/1.0)");
                break;
            case 2:
                printf("(使用 HTTP/1.1)");
                break;
        }
    
        printf("\n");
        printf("%d 个客户端", clients);
        printf(",%d s", benchtime);
        if (force) 
            printf(",选择提前关闭连接");
    
        if (proxyhost != NULL) 
            printf(",经由代理服务器 %s:%d   ", proxyhost, proxyport);
    
        if (force_reload)
            printf(",选择无缓存");
    
        printf("\n");
    
        //真正开始压力测试
        return bench();
    }
    
    void build_request(const char *url) {
        char tmp[10];
        int i = 0;
        
        bzero(host, MAXHOSTNAMELEN);
        bzero(request, REQUEST_SIZE);
        
        //缓存和代理都是http1.0后才有的
        //无缓存和代理都要在http1.0以上才能使用
        //因此这里要处理一下,不然可能会出问题
        if (force_reload && proxyhost != NULL && http < 1) 
            http = 1;
        //HEAD请求时http1.0后才有的
        if (method == METHOD_HEAD && http < 1) 
            http = 1;
        //OPTIONS和TRACE都是http1.1才有
        if (method == METHOD_OPTIONS && http < 2)
            http = 2;
        if (method == METHOD_TRACE && http < 2)
            http = 2;
    
        //开始填写http请求
        //请求行
        //填写请求方法
        switch (method) {
            case METHOD_HEAD:
                strcpy(request, "HEAD");
                break;
            case METHOD_OPTIONS:
                strcpy(request, "OPTIONS");
                break;
            case METHOD_TRACE:
                strcpy(request, "TRACE");
                break;
            default:
            case METHOD_GET:
                strcpy(request, "GET");
        }
        
        strcat(request, " ");
        //判断URL的合法性
        //1.URL中没有"://"
        if (strstr(url, "://") == NULL) {
            fprintf(stderr, "\n%s:是一个不合法的URL\n", url);
            exit(2);
        }
        //2.URL过长
        if (strlen(url) > 1500) {
            fprintf(stderr, "URL 长度过长\n");
            exit(2);
        }
        
        //3.没有代理服务器却填写错误
        if (proxyhost == NULL) { //若无代理
            if (strncasecmp("http://", url, 7) != 0) { //忽略大小写比较前7位
                fprintf(stderr, "\nurl无法解析,是否需要但没有选择使用代理服务器的选项?\n");
                usage();
            }
        }
        //定位url中主机名开始的位置
        //比如 http://www.xxx.com/
        i = strstr(url, "://") - url + 3;
        //4.在主机名开始的位置找是否有'/',若没有则非法
        if (strchr(url + i, '/') == NULL) {
            fprintf(stderr, "\nURL非法:主机名没有以'/'结尾\n");
            exit(2);
        }
    
        //判断完URL合法性后继续填写URL到请求行
        //无代理时
        if (proxyhost == NULL) {
            //有端口号时,填写端口号
            if (index(url+i, ':') != NULL && index(url, ':') < index(url, '/')) {
                //设置域名或IP      
                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);
    
                //设置端口号
                proxyport = atoi(tmp);
                //避免写":"却没有写端口号
                if (proxyport == 0)
                    proxyport = 80;
            } else { //无端口号
                strncpy(host, url+i, strcspn(url+i, "/")); //找到url+i到第一个"/"之间的字符个数
            }
        } else { //有代理服务器就简单了,直接填就行,不用自己处理
            strcat(request, url);
        }
    
        //填写http协议版本到请求行
        if (http == 1)
            strcat(request, " HTTP/1.0");
        if (http == 2)
            strcat(request, " HTTP/1.1");
        strcat(request, "\r\n");
    
        //请求报头
        if (http > 0)
            strcat(request, "User-Agent:WebBench 1.5\r\n");
        //填写域名或IP    
        if (proxyhost == NULL && http > 0) {
            strcat(request, "Host: ");
            strcat(request, host);
            strcat(request, "\r\n");
        }
    
        //若选择强制重新加载,则填写无缓存
        if (force_reload && proxyhost != NULL) {
            strcat(request, "Pragma: no-cache\r\n");
        }
    
        //我们目的是构造请求给网址,不需要传输任何内容,当然不必用长连接
        //否则太多的连接维护会造成太大的消耗,大大降低可构造的请求数和客户端数
        //http1.1后是默认的keep-alive的
        if (http > 1)
            strcat(request, "Connection: close\r\n");
        
        //填入空行后就构造完成了
        if (http > 0) 
            strcat(request, "\r\n");
    }
    
    //父进程的作用:创建子进程,读子进程测试到的数据,然后处理
    static int bench() {
        int i=0, j=0;
        long long k = 0;
        pid_t pid = 0;
        FILE *f = NULL;
        
        //尝试建立连接一次
        i = Socket(proxyhost == NULL ? host:proxyhost, proxyhost);
    
        if (i < 0) {
            fprintf(stderr, "\n连接服务器失败,中断测试\n");
            return 3;
        }
    
        close(i); //尝试连接成功了,关闭该连接
        //建立父子进程通信的管道
        if (pipe(mypipe)) {
            perror("通信管道建立失败");
            return 3;
        }
        
        //让子进程去测试,建立多少个子进程进行连接由参数clients决定
        for (i-0; i<clients; i++) {
            pid = fork();
            if (pid <= 0) {
                sleep(1);
                break;//失败或者子进程都结束循环,否则该子进程可能继续fork了,显然不可以
            }
        }
    
        //处理fork失败的情况
        if (pid < 0) {
            fprintf(stderr, "第 %d 子进程创建失败", i);
            perror("创建子进程失败");
            return 3;
        }
        //子进程执行流
        if (pid == 0) {
            //由子进程来发出请求报文
            benchcore(proxyhost == NULL ? host : proxyhost, proxyhost, request);
        
            //子进程获得管道写端的文件指针
            f = fdopen(mypipe[1], "w");
            if (f == NULL) {
                perror("管道写端打开失败");
                return 3;
            }
    
            //向管道中写入该子进程在一定时间内请求成功的次数
            //失败的次数
            //读取到的服务器回复的总字节数
            fprintf(f, "%d %d %lld\n", speed, failed, bytes);
            fclose(f); //关闭写端
            return 0;
        } else { 
            //子进程获得管道读端的文件指针
            f = fdopen(mypipe[0], "r");
    
            if (f == NULL) {
                perror("管道读端打开失败");
                return 3;
            }
    
            //fopen标准IO函数是自带缓冲区的,
            //我们的输入数据非常短,并且要求数据要及时,
            //因此没有缓冲是最合适的
            //我们不需要缓冲区
            //因此我们把缓冲类型设置为_IONBF
            setvbuf(f, NULL, _IONBF, 0);
            
            speed = 0; //连接成功的总次数,后面除以时间可以得到速度
            failed = 0; //失败的请求数
            bytes = 0; //服务器回复的总字节数
            
            //唯一的父进程不停的读
            while (1) {
                pid = fscanf(f, "%d %d %lld", &i, &j, &k);//得到成功读入的参数个数
                if (pid < 3) {
                    fprintf(stderr, "某个子进程死亡\n");
                    break;
                }
    
                speed += i;
                failed += j;
                bytes += k;
                //我们创建了clients,正常情况下要读clients次
                if (--clients == 0) 
                    break;
            }
    
            fclose(f);
    
            //统计处理结果
            printf("\n速度: %d pages/min,%d bytes/s,\n请求: %d 成功,%d 失败\n", \
                    (int)((speed+failed)/(benchtime/60.0f)), \
                    (int)(bytes/(float)benchtime),\
                    speed, failed);
        }
        return i;
    }
    
    //闹钟信号处理函数
    static void alarm_handler(int signal) {
        timeout = 1;
    }
    //子进程真正的向服务器发出请求报文并以其得到此期间的相关数据
    void benchcore(const char *host, const int port, const char *req) {
        int rlen;
        char buf[1500];
        int s,i;
        struct sigaction sa;
    
        //安装闹钟信号的处理函数
        sa.sa_handler = alarm_handler;
        sa.sa_flags = 0;
        if (sigaction(SIGALRM, &sa, NULL)) 
            exit(3);
    
        //设置闹钟函数
        alarm(benchtime);
    
        rlen = strlen(req);
    
    nexttry:
        while(1) {
            //只有在收到闹钟信号后会使time = 1
            //即该子进程的工作结束了
            if (timeout) {
                if (failed > 0) {
                    failed--;
                }
                return;
            }
    
            //建立到目标网站服务器的tcp连接,发送http请求
            s = Socket(host, port);
            if (s < 0) {
                failed++; //连接失败
                continue;
            }
            //发送请求报文
            if (rlen != write(s, req, rlen)) {
                failed++;
                close(s); //写失败了也不能忘了关闭套接字
                continue;
            }
            //http0.9的特殊处理
            //因为http0.9是在服务器回复后自动断开连接的,不Keep-alive
            //在此可以提前先彻底关闭套接字的写的一半,如果失败了那么肯定是个不正常的状态,
            //如果关闭成功则继续往后,因此可能还有需要接受服务器的恢复内容
            //但是写这一半是一定可以关闭了,作为客户端进程上不需要再写了
            //因此我们主动破坏套接字的写端,但是这不是关闭套接字,关闭还是得close
            //事实上,关闭写端后,服务器没写完的数据也不会再写了,这个就不考虑了。
            if (http == 0) {
                if (shutdown(s, 1)) {
                    failed++;
                    close(s);
                    continue;
                }
            }
    
            //-f没有设置时默认等待服务器的回复
            if (force == 0) {
                while (1) {
                    if (timeout) 
                        break;
                    i = read(s, buf, 1500); //读服务器发回的数据到buf中
    
                    if (i < 0) {
                        failed++; //读失败
    
                        close(s);//失败后一定要关闭套接字,不然失败个数多时会严重浪费资源
                        goto nexttry;//这次失败了那么继续下一次连接,与发出请求
                    } else {
                        if (i == 0) //读完了
                            break;
                        else
                            bytes += i;//统计服务器回复的字节数
                    }
                }
            }
            if (close(s)) {
                failed++;
                continue;
            }
            speed++;
        }
    }
    

    源码

    git clone https://github.com/l-f-h/webbench-1.5.git

    make && make clean

     

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,523
精华内容 2,209
关键字:

webbench