webbench剖析__web

來源:互聯網
上載者:User

webbench:其為linux上一款web效能壓力測試工具,它最多可以類比3萬個並發串連數來測試伺服器壓力,其原理為fork多個子進程,每個子進程都迴圈做web訪問測試,子進程將訪問的結果通過管道告訴父進程,父進程做最終結果統計。

其主要原理如下圖:

其代碼實現中主要運用4個函數:getopt_long()系統命令列解析函數,build_request()函數,bench()函數,benchcore()函數。

流程如下:
webbench.c:
全域變數選項參數:都有預設值
1.命令列參數解析,參數構造(getopt_long()函數實現)(若使用者沒有傳
參設定則為其預設值);

2.根據1得到的一些參數,進行http請求構造,調用build_request()函數:
(1)構造第一行:
要求方法:method–>request[]

url:判斷url合法性,合法:
無Proxy 伺服器:
將其網路地址–>host[],連接埠號碼–>proxyport;
url資源路徑–>request[]
有Proxy 伺服器:
url直接填入–>request[];

http協議版本:http0.9 http1.0 http1.1

(2)請求前序:Name:Value–>request[]
(3)填入空行–>request[]
構造完成

3.進行壓力測試,調用bench()函數:
(1)進行一次串連合法性測試;
(2)建立管道通訊;
(3)按照用戶端數目派生子進程;
(4)每個子進程調用benchcore()函數進行串連請求:

benchcore函數:
建立自訂訊號捕捉函數,建立benchtime時間鬧鐘:
(1)調用Socket(),得到串連通訊端;
(2)向網路中寫請求;
(3)http0.9協議處理(關閉串連寫一半);
(4)讀取伺服器響應訊息;
(5)關閉串連;
在以上過程中統計各自進程的faild(失敗次數)、bytes(伺服器回應位元組 數)、speed(串連成功次數);

(5)benchcore函數調用完成,每個進程向管道中寫入faild、bytes、speed;
(6)父進程開啟管道讀取每個進程寫入的資訊,進行統計計算,輸出壓力測試結果。

Socket.c:
1.建立通訊端:調用socket();
2.建立串連:調用connect();
返回通訊端: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> //統計的壓力測試最終結果表示volatile int timerexpired = 0;  int speed = 0;  int failed = 0;  int bytes = 0;//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;    //預設要求方法為GET方式   int clients = 1;            //預設只類比一個用戶端  int force = 0;              //預設需要等待伺服器響應int force_reload = 0;       //失敗時重新請求   int proxyport = 80;         //預設訪問伺服器連接埠為80 char *proxyhost = NULL;     //預設無Proxy 伺服器  int benchtime = 30;         //預設類比請求時間為30s  // globals 版本號碼 int http10 = 1;   //0:- http/0.9, 1:- http/1.0, 2:- http/1.1 int mypipe[2];             //管道用於父子進程通訊char host[MAXHOSTNAMELEN]; //儲存伺服器網路地址  #define REQUEST_SIZE 2048  char request[REQUEST_SIZE]; //存放http請求報文資訊數組//函式宣告 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 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*指標(為NULL),對應短選項或static const struct option long_options[]=                  //不為NULL,將第四個參數值給第三個參數} {      {"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 main(int argc, char *argv[])  {      int opt = 0;      int options_index = 0;      char *tmp = NULL;      //一、檢驗命令列參數    //1.不帶選項時直接輸出用法help資訊    if(argc == 1)      {          usage();          return 2;      }      //2.帶選項時則解析命令列參數並根據傳入選項進行相關設定    // getopt_long 為命令列解析的庫函數,根據argc來尋找(argv,"912Vfrt:p:c:?h")這兩個字串匹配的選項,    //如果是短選項,則直接返回這個選項給opt,    //如果是長選項,則到option long_options[]結構體數組中尋找匹配其長選項,返回其對應的短選項給opt,    //若其第三個參數不為NULL,將第四個參數值給第三個參數,並且返回0給opt    //此函數內建全域變數:    //optarg:指向選項後的參數:-t 100,指向100;    //optind: 當前訪問到的argv索引值    //opterr: 其值非0時,代表有無效選項,缺少參數,輸出錯誤資訊    //optopt: 發現無效選項時,函數返回“? / :”,將其值設為無效選項字元    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;        //force=1代表不等待伺服器響應             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 'c':                       clients = atoi(optarg);     //設定建立的用戶端數                      break;              case 'p':                        //使用Proxy 伺服器,設定其代理網路號和連接埠號碼:格式:-p server:port                       tmp = strrchr(optarg,':');   //尋找“:”在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';  //將:號置為“\0”                      proxyport = atoi(tmp+1);  //設定新的連接埠號碼                       break;              case ':':              case 'h':              case '?': usage();return 2;break;         }      }     //getopt_long函數將選項解析完成後,讀到url不會在讀取,此時argv[optind]指向url    //optind 被 getopt_long設定為命令列參數中未讀取的下一個元素下標值     if(optind == argc) //若相等即沒有輸入URL     {          fprintf(stderr,"webbench: Missing URL!\n");          usage();          return 2;      }      //若用戶端選項後參數設為0,則更改     if(clients == 0)         clients = 1;      if(benchtime == 0)         benchtime = 60;      //輸出webbench版本相關資訊      fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"       "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"       );      //二、構造HTTP請求到request數組    build_request(argv[optind]);   //傳入URL    //http請求構造成功後    //以下輸出提示資訊      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]);   //訪問的url     switch(http10)    //http協議版本號碼    {          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");    //輸出Proxy 伺服器的資訊        if(proxyhost != NULL)         printf(", via proxy server %s:%d",proxyhost,proxyport);     if(force_reload)         printf(", forcing reload");      printf(".\n");      //開始壓力測試,返回 bench 函數執行結果      return bench();  }  //二、構造HTTP請求到request數組void build_request(const char *url)  {      char tmp[10];      int i;      //初始化     bzero(host,MAXHOSTNAMELEN);      bzero(request,REQUEST_SIZE);      //判斷應該使用的 HTTP 協議      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;      //1.填寫http請求第一行    //填寫要求方法method    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," ");      //URL 合法性判斷      //若沒有"://"則不合法    if(NULL == strstr(url,"://"))    {          fprintf(stderr, "\n%s: is not a valid URL.\n",url);          exit(2);      }      //若url過長非法    if(strlen(url)>1500)      {          fprintf(stderr,"URL is too long.\n");          exit(2);      }      if(proxyhost == NULL)   //若無Proxy 伺服器     {               if(0 != strncasecmp("http://",url,7)) //忽略大小寫比較         {              //只支援 HTTP 地址              fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");              exit(2);          }     }           //找到主機名稱開始的地方:如:http://baidu.com:80/      i = strstr(url,"://")-url+3;   //i==7    // 必須以 / 結束      if(strchr(url+i,'/')==NULL) //在字串中尋找“/” ,找不到則非法URL    {          fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");          exit(2);      }      if(proxyhost == NULL)  //若無Proxy 伺服器     {          // 得到連接埠號碼從主機名稱        if(index(url+i,':') != NULL && index(url+i,':') < index(url+i,'/'))  //若帶有連接埠號碼,index函數與strchr相似        {              //設定網路號            strncpy(host,url+i,strchr(url+i,':')-url-i);    //如將baidu.com拷貝到host數組裡即網路地址            //初始化             bzero(tmp,10);              strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1); //將連接埠號碼拷貝到tmp數組中             //設定連接埠              proxyport = atoi(tmp);              if(proxyport==0)                 proxyport=80;          }         else   //沒有連接埠號碼,直接拷貝網域名稱到host數組中        {              strncpy(host,url+i,strcspn(url+i,"/"));  //strcspn找url+i到“/”之間的字元個數         }            //將資源路徑填入請求行裡        strcat(request+strlen(request),url+i+strcspn(url+i,"/"));     }     else    //若有Proxy 伺服器    {           strcat(request,url);  //直接填入URL到請求行中    }      //填入http版本號碼到請求行中    if(http10 == 1)          strcat(request," HTTP/1.0");      else if (http10==2)          strcat(request," HTTP/1.1");      strcat(request,"\r\n");      //2.填請求前序:NAME:VALUE    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");      //3.填入空行      if(http10>0)         strcat(request,"\r\n");      //構造完成} static int bench(void)   //父進程做的工作{      int i,j,k;      pid_t pid = 0;      FILE *f;      //建立網路連接 :先測試一次,伺服器是否可以正常串連成功       i = Socket(proxyhost == NULL ? host:proxyhost, proxyport);      if(i < 0)    {          fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");          return 1;      }      close(i);   //測試成功,一次串連完成關閉    //建立管道通訊      if(pipe(mypipe))      {          perror("pipe failed.");          return 3;      }      //派生子進程進行壓力測試 :傳入多少個用戶端則建立多少個子進程進行串連     for(i = 0;i < clients;i++)      {          pid = fork();          if(pid <= (pid_t)0)          {              sleep(1);            break;     //使子進程立刻跳出迴圈,要不就子進程繼續 fork 了        }      }     //子進程建立失敗    if( pid < (pid_t)0)       {          fprintf(stderr,"problems forking worker no. %d\n",i);          perror("fork failed.");          return 3;      }     //子進程執行    if(pid == (pid_t)0)       {          //子進程發出實際請求           if(proxyhost == NULL)              benchcore(host,proxyport,request);          else              benchcore(proxyhost,proxyport,request);          

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.