需求:
修改Apache AB 工具,使其能够从文件中读取压力/性能测试的 url、header、cookie信息,并采用这些信息进行随机组合访问的压力和性能测试。
解决方案:
1. 如何从文件中读取url、header、cookie信息,并保证该操作不影响Apache AB的性能及数据统计?
首先,我所考虑的方法是,在ab读取设置参数的时候,如果配置为需要从文件中读取url、header、cookie等信息,则立即将这些信息读入到内存中备用,这样就能在Apache AB开始压测前完成所有的数据准备工作。
/* edit by gongyuan.cz */ while ((status = apr_getopt(opt, "J:O:Q:n:c:t:b:T:p:u:v:rkVhwix:y:z:C:H:P:A:g:X:de:Sq" #ifdef USE_SSL "Z:f:" #endif ,&c, &optarg)) == APR_SUCCESS) { switch (c) { /* edit by gongyuan.cz start --- */ case 'J': if(0 != (r = open_pathfile(optarg))) exit(r); break; case 'O': if(0 != (r = open_cookiefile(optarg))) exit(r); break; case 'Q': if(0 != (r = open_headerfile(optarg))) exit(r); break; /* edit by gongyuan.cz end ----- */
其次,我设计的数据在文件中的存放格式为:
其中,5代表文件中url的个数,而其后面的5行,每行代表一个url内容。
接下来是读取文件的代码介绍:
/* read data to Path from file, edit by gongyuan.cz */ static int open_pathfile(const char *file) { int file_len = 0, pos_cur = 0, line_size = 0, index = 0; char* databuff = NULL; char* dataline = NULL; // 读取文件,并判断是否读取成功 FILE * in = fopen(file, "r"); if (!in) { perror("Cannot open path file"); exit(1); } // 获取文件的总长度大小 fseek(in, 0, SEEK_END); file_len = ftell(in); fseek(in, 0, SEEK_SET); // 设置一个数据buffer,并将文件的所有内容放入该buffer中 databuff = (char*)malloc(file_len + 1); fread(databuff, 1, file_len, in); *(databuff+file_len) = '\0'; // 以换行符“\n”为分隔符,获取每一行的数据 // 在此,先读取第一行的数据,即url的数目,之后根据该数值为存放 // url的数组申请空间 dataline = strstr(databuff + pos_cur, "\n"); if(dataline) { line_size = dataline - databuff - pos_cur; dataline = (char*)malloc(line_size + 1); strncpy(dataline, databuff + pos_cur, line_size); *(dataline + line_size)= '\0'; // 为存储url内容的数组 char ** pathName分配空间 pathNum = atoi(dataline); pathName = (char**)malloc((pathNum + 1) * sizeof(char *)); if(pathName == NULL){ perror("Malloc path name error"); exit(1); } pos_cur += line_size + 1; } // 读取后续的url内容,并存放到数组中 do { dataline = strstr(databuff + pos_cur, "\n"); // 在此加了限制,防止数组越界 if(dataline&&index < pathNum) { line_size = dataline - databuff - pos_cur; // set pathName pathName[index] = (char*)malloc(line_size + 1); strncpy(pathName[index], databuff + pos_cur, line_size); *(pathName[index] + line_size) = '\0'; index++; pos_cur += line_size + 1; } }while(dataline); // 最后结束,关闭文件,返回0 fclose(in); return 0; } /* ------------------------------------------------------- */
2. 如何构建url、header、cookie信息的随机组合访问的压力和性能测试?
为了能够根据我们的需求对AB的请求信息进行设置,我将AB内设置请求信息的代码独立编写成一个函数。
/* set request, edit by gongyuan.cz */ static void set_request() { int snprintf_res = 0; /* setup request */ if (posting <= 0) { snprintf_res = apr_snprintf(request, sizeof(_request), "%s %s HTTP/1.0\r\n" "%s" "%s" "%s" "%s" "\r\n", (posting == 0) ? "GET" : "HEAD", (isproxy) ? fullurl : path, keepalive ? "Connection: Keep-Alive\r\n" : "", cookie, auth, headerNum ? hdrs_cp : hdrs); } else { snprintf_res = apr_snprintf(request, sizeof(_request), "%s %s HTTP/1.0\r\n" "%s" "%s" "%s" "Content-length: %" APR_SIZE_T_FMT "\r\n" "Content-type: %s\r\n" "%s" "\r\n", (posting == 1) ? "POST" : "PUT", (isproxy) ? fullurl : path, keepalive ? "Connection: Keep-Alive\r\n" : "", cookie, auth, postlen, (content_type[0]) ? content_type : "text/plain", (headerNum ? hdrs_cp : hdrs)); } if (snprintf_res >= sizeof(_request)) { err("Request too long\n"); } if (verbosity >= 2) printf("INFO: %s header == \n---\n%s\n---\n", (posting == 2) ? "PUT" : "POST", request); reqlen = strlen(request); /* * Combine headers and (optional) post file into one contineous buffer */ if (posting >= 1) { char *buff = malloc(postlen + reqlen + 1); if (!buff) { fprintf(stderr, "error creating request buffer: out of memory\n"); return; } strcpy(buff, request); memcpy(buff + reqlen, postdata, postlen); request = buff; } #ifdef NOT_ASCII inbytes_left = outbytes_left = reqlen; status = apr_xlate_conv_buffer(to_ascii, request, &inbytes_left, request, &outbytes_left); if (status || inbytes_left || outbytes_left) { fprintf(stderr, "only simple translation is supported (%d/%" APR_SIZE_T_FMT "/%" APR_SIZE_T_FMT ")\n", status, inbytes_left, outbytes_left); exit(1); } #endif /* NOT_ASCII */ } /* ------------------------------------------------------- */
之后,对write_request函数进行修改,在其最前面添加如下代码:
static void write_request(struct connection * c) { /* edit by gongyuan.cz start */ /* set path */ if(pathNum){ int index = random() % pathNum; path = apr_pstrcat(cntxt, pathName[index], NULL); /* set fullurl */ if(isproxy){ fullurl = apr_pstrcat(cntxt, fullurl, pathName[index], NULL); } } /* set cookie */ if(cookieNum){ int index = random() % cookieNum; cookie = apr_pstrcat(cntxt, "Cookie: ", cookieName[index], "\r\n", NULL); } /* set header */ if(headerNum){ int index = random() % headerNum; hdrs_cp = apr_pstrcat(cntxt, hdrs, headerName[index], "\r\n", NULL); } /* set request */ if(0 != pathNum || 0 != cookieNum || 0 != headerNum){ set_request(); } /* edit by gongyuan.cz end */
最后,经过以上的修改之后,修改版的Apache AB工具,不仅具备之前的所有功能,还具备从文件中读取url、header、cookie等信息并随机组合之后进行压力/性能测试。
修改版的Apache AB与原版Apache AB的性能及数据统计对比报告,在之前博客:
中有详细的介绍。
ok,性能/压力测试工具Apache ab修改系列:Part3 介绍完毕,欢迎拍砖。转发请备注转自:100continue.iteye.com。 谢谢。