天天看點

ab壓力測試工具ifdef USE_SSLendif

ab(apache bench)是web伺服器APACHE自帶的壓力測試工具。它通過模拟多個用戶端同時對web伺服器發起請求,對WEB伺服器的并發性能進行測試。對于一般的伺服器端程式,ab也提供了一種并發性測試的思路。本文将對ab的關鍵邏輯進行分析。ab的源碼可以從http://code.google.com/p/apachebench-standalone/下載下傳。

ab的使用說明如下:

Usage: ab [options] [http[s]://]hostname[:port]/path

Options are:

-n requests Number of requests to perform

-c concurrency Number of multiple requests to make

-t timelimit Seconds to max. wait for responses

-b windowsize Size of TCP send/receive buffer, in bytes

-p postfile File containing data to POST. Remember also to set -T

-u putfile File containing data to PUT. Remember also to set -T

-T content-type Content-type header for POSTing, eg.

‘application/x-www-form-urlencoded’

Default is ‘text/plain’

-v verbosity How much troubleshooting info to print

-w Print out results in HTML tables

-i Use HEAD instead of GET

-x attributes String to insert as table attributes

-y attributes String to insert as tr attributes

-z attributes String to insert as td or th attributes

-C attribute Add cookie, eg. ‘Apache=1234. (repeatable)

-H attribute Add Arbitrary header line, eg. ‘Accept-Encoding: gzip’

Inserted after all normal header lines. (repeatable)

-A attribute Add Basic WWW Authentication, the attributes

are a colon separated username and password.

-P attribute Add Basic Proxy Authentication, the attributes

are a colon separated username and password.

-X proxy:port Proxyserver and port number to use

-V Print version number and exit

-k Use HTTP KeepAlive feature

-d Do not show percentiles served table.

-S Do not show confidence estimators and warnings.

-g filename Output collected data to gnuplot format file.

-e filename Output CSV file with percentages served

-r Don’t exit on socket receive errors.

-h Display usage information (this message)

-Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers)

-f protocol Specify SSL/TLS protocol (SSL3, TLS1, or ALL)

其中,最常用的參數為-n和-c,這兩個參數分别指定了向伺服器發起的請求數量和并發程度。其它參數則主要針對HTTP請求和響應的格式以及ab的輸出方式。

ab的實作依賴于APR(Apache Portable Runtime)庫,該庫對unix平台程式設計接口進行了統一的封裝和擴充,但從名子可以很容易的看出它們之間的對應關系。本文不再贅述。ab在模拟并發通路時并沒有使用多線程,而是使用了一種異步connect+epoll的方式來實作,下文主要對這一過程進行分析。

我們從ab的main方法看起,該方法主要對參數進行解析并設定了一些代表參數的全局變量,最後分别調用了以下方法:

copyright();

test();

apr_pool_destroy(cntxt);

即ab的主要邏輯包含在test方法中。test方法首先進行了一些全局的初始化工作。其中,最重要的一個資料結構是數組struct connection con[c],代表同時存在的c個用戶端連接配接。struct connection的結構如下:

struct connection {

apr_pool_t *ctx;

apr_socket_t *aprsock;

apr_pollfd_t pollfd;

int state;

apr_size_t read;

apr_size_t bread;

apr_size_t rwrite, rwrote;

apr_size_t length;

char cbuff[CBUFFSIZE];

int cbx;

int keepalive;

int gotheader;

apr_time_t start,

connect,

endwrite,

beginread,

done;

int socknum;

int port;

ifdef USE_SSL

SSL *ssl;

endif

};

test方法首先調用start_connect開始c個初始的連接配接

for (i = 0; i < concurrency; i++) {

con[i].socknum = i;

start_connect(&con[i]);

}

start_connect首先建立一個socket端口并将其設定為非阻塞,這樣調用connect時就會立即傳回EINPROGRESS錯誤,但TCP三步握手仍在背景進行。通過這種方式,就可以同時發起多個連接配接,進而模拟多個用戶端請求伺服器的場景。start_connect的主要邏輯如下:

re = apr_connect(sock);

if(re == EINPROGRESS){

将sock加入到epoll的監控清單中,等待連接配接完成

}

else if(re == SUCCESS){

連接配接已完成,調用write_request送出請求,後文将介紹

}

else{

報錯

}

傳回test 方法,在建立完成初始的連接配接之後,test開始使用epoll來監聽連接配接完成、有新的響應資料等事件并進行處理:

do{

do{

status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults);

}while (status == EINTR);

for(I = 0;I < n;++i){
    ……
    rv = next_fd->rtnevents; //取epoll事件
    if ((rv & APR_POLLIN) || (rv & APR_POLLPRI) || (rv & APR_POLLHUP))
        read_connection(c); //讀伺服器端的響應資料
    if ((rv & APR_POLLERR) || (rv & APR_POLLNVAL)) {
        出錯,此時調用start_connect重試連接配接
    }
    if(rv & ARP_POLLOUT){ //可寫,如果連接配接完成則傳回可寫事件
        if(處于CONNECTING狀态){
            rv = apr_socket_connect(c->aprsock, destsa); //此時再連接配接應直接傳回
            if (rv != APR_SUCCESS) {
                重新調用start_connect開始連接配接
            }
            else{
                //連接配接成功
                調用write_request發送請求
            }
        }
    }
}
           

}while(未完成)

上文中提到的write_request和read_connection分别為向伺服器送出請求以及從伺服器讀取響應資料的方法。其中write_request方法會将預先建立好的http請求的内容一次性發送出去;而read_connection則嘗試讀取、解析伺服器端響應并在讀取到EOF後調用close_connection方法關閉目前連接配接。

最後, close_connection方法除了關閉socket端口、記錄結果外,如果請求數未達到要求時還會複用要關閉的struct connection,重新調用start_connect發起連接配接請求,進而推進測試任務。

在一般的作業系統中,單個程序能夠建立的線程數量是有限的。通過使用異步connect的方式代替多線程,ab可以提高用戶端能夠達到的并發程度,進而提高測試能力。雖然ab本身是針對http伺服器的測試工具,但這種設計可以被用來對任何伺服器端程式進行并發性測試。