Web bench源码剖析

本文介绍了Web bench,一个简洁的压力测试工具,用于模拟大量并发连接进行网站性能测试。文章详细阐述了Web bench的实现原理,通过父进程fork子进程的方式执行HTTP测试,并通过管道汇总结果。同时,对源码进行了剖析,包括socket.c和webbench.c的主要功能,以及使用到的关键函数如gethostbyname、inet_ntop等。最后,讨论了为何选用进程而非线程,并提及了命令行参数解析和信号处理等知识点。

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

一、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-2004
  3 * This is free software, see GNU Public License version 2 for
  4 * details.
  5 *
  6 * Simple forking WWW Server benchmark:
  7 *
  8 * Usage:
  9 *   webbench --help
 10 *
 11 * Return codes:
 12 *    0 - sucess
 13 *    1 - benchmark failed (server is not on-line)
 14 *    2 - bad param
 15 *    3 - internal error, fork failed
 16 * 
 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 0
 39 #define METHOD_HEAD 1
 40 #define METHOD_OPTIONS 2
 41 #define METHOD_TRACE 3
 42 #define PROGRAM_VERSION "1.5"
 43 int method=METHOD_GET;//默认请求方法为GET,同时也支持HEAD、OPTIONS、TRACE
 44 int clients=1;//并发数目,默认只有一个进程发请求,通过-c参数设置
 45 int force=0;//是否需要等待读取从服务器返回值的数据,0表示需要读取
 46 int force_reload=0;//是否使用缓存,1表示不缓存,0表示缓存页面
 47 int proxyport=80;//代理服务器的端口
 48 char *proxyhost=NULL;//代理服务器的ip
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值