Weighttp 文件目录
查看README,这里对Weighttp的编译安装进行了说明。
weighttp - a lightweight and simple webserver benchmarking tool
-----------------------------------------
Please see http://weighttp.lighttpd.net/ for current info.
BUILD
=====
Make sure you have libev* and python (for waf) installed, then:
$ ./waf configure
$ ./waf build
See ./waf --help for available configure options and other commands available.
INSTALL
=======
$ ./waf install
or
$ sudo ./waf install
USAGE
=====
$ weighttp -h
UNINSTALL
=========
$ ./waf uninstall
or
$ sudo ./waf uninstall
You can also chain commands:
$ ./waf configure clean build install
----
* libev can be found in your distro's repository or at http://software.schmorp.de/pkg/libev.html
通过上面,weighttp的安装需要依赖libev (事件驱动框架)and python (for waf)。查看waf命令
[root@server1 weighttp-master]# ./waf --help
waf [command] [options]
Main commands (example: ./waf build -j4)
build : builds the project
clean : removes the build files
configure: configures the project
dist : makes a tarball for redistributing the sources
distcheck: checks if the sources compile (tarball from 'dist')
distclean: removes the build directory
install : installs the build files
uninstall: removes the installed files
Options:
--version show program's version number and exit
-h, --help show this help message and exit
-j JOBS, --jobs=JOBS amount of parallel jobs (2)
-k, --keep keep running happily on independent task groups
-v, --verbose verbosity level -v -vv or -vvv [default: 0]
--nocache ignore the WAFCACHE (if set)
--zones=ZONES debugging zones (task_gen, deps, tasks, etc)
-p, --progress -p: progress bar; -pp: ide output
--targets=COMPILE_TARGETS
build given task generators, e.g. "target1,target2"
configuration options:
-b BLDDIR, --blddir=BLDDIR
build dir for the project (configuration)
-s SRCDIR, --srcdir=SRCDIR
src dir for the project (configuration)
--prefix=PREFIX installation prefix (configuration) [default: '/usr/local/']
installation options:
--destdir=DESTDIR installation root [default: '']
-f, --force force file installation
C Compiler Options:
--check-c-compiler=CHECK_C_COMPILER
On this platform (linux) the following C-Compiler will be checked by default: "gcc icc suncc"
有兴趣的可以查看waf的使用,就不做介绍了,我也不会
Weighttp源码总共6个文件
weight.h中定义了结构体和一些宏。结构体Config定义了请求的配置文件,请求地址,数量,线程数,并发用户数等。
#define CLIENT_BUFFER_SIZE 32 * 1024
#define W_MALLOC(t, n) ((t*) calloc((n), sizeof(t)))
#define W_REALLOC(p, t, n) ((t*) realloc(p, (n) * sizeof(t)))
#define W_ERROR(f, ...) fprintf(stderr, "error: " f "\n", __VA_ARGS__)
#define UNUSED(x) ( (void)(x) )
struct Config;
typedef struct Config Config;
struct Stats;
typedef struct Stats Stats;
struct Worker;
typedef struct Worker Worker;
struct Client;
typedef struct Client Client;
#include "client.h"
#include "worker.h"
struct Config {
uint64_t req_count;
uint8_t thread_count;
uint16_t concur_count;
uint8_t keep_alive;
char *request;
uint32_t request_size;
struct addrinfo *saddr;
};
work.h中定义了结构体Stats和Worker,Stats用来描述请求的状态,请求的时间,完成数量,成功数量,失败数量,响应状态等。Worker描述了一个整体信息,描述运行的相关参数,包含Config,Request,ev_loop,Client,Stats等。
struct Stats {
ev_tstamp req_ts_min; /* minimum time taken for a request */
ev_tstamp req_ts_max; /* maximum time taken for a request */
ev_tstamp req_ts_total; /* total time taken for all requests (this is not ts_end - ts_start!) */
uint64_t req_todo; /* total number of requests to do */
uint64_t req_started; /* total number of requests started */
uint64_t req_done; /* total number of requests done */
uint64_t req_success; /* total number of successful requests */
uint64_t req_failed; /* total number of failed requests */
uint64_t req_error; /* total number of error'd requests */
uint64_t bytes_total; /* total number of bytes received (html+body) */
uint64_t bytes_body; /* total number of bytes received (body) */
uint64_t req_1xx;
uint64_t req_2xx;
uint64_t req_3xx;
uint64_t req_4xx;
uint64_t req_5xx;
};
struct Worker {
uint8_t id;
Config *config;
struct ev_loop *loop;
char *request;
Client **clients;
uint16_t num_clients;
Stats stats;
uint64_t progress_interval;
};
Client.h定了Client,用来执行每次请求,定义了执行状态,解析状态,等等。
struct Client {
enum {
CLIENT_START,
CLIENT_CONNECTING,
CLIENT_WRITING,
CLIENT_READING,
CLIENT_ERROR,
CLIENT_END
} state;
enum {
PARSER_START,
PARSER_HEADER,
PARSER_BODY
} parser_state;
Worker *worker;
ev_io sock_watcher;
uint32_t buffer_offset;
uint32_t parser_offset;
uint32_t request_offset;
ev_tstamp ts_start;
ev_tstamp ts_end;
uint8_t keepalive;
uint8_t success;
uint8_t status_success;
uint8_t chunked;
int64_t chunk_size;
int64_t chunk_received;
int64_t content_length;
uint64_t bytes_received; /* including http header */
uint16_t header_size;
char buffer[CLIENT_BUFFER_SIZE];
};
头文件还对函数进行了声明。
Weighttp执行过程:
Weighttp每次执行时,先对命令行进行解析,根据命令行输入的参数构造完整的request,定义请求的相关参数
根据输入参数,分配线程和Worker,根据work信息执行线程,work则调用client
线程最终调用client来执行请求的发送和接受响应,并对响应进行解析,给出相关结果(请求成功、失败、传输数据量等)
程序根据执行的结果,同时计算请求的处理速度等,最后输出一个完整信息
weighttp.c
static void show_help(void) {} 用来输出weighttp的帮助信息
static struct addrinfo *resolve_host(char *hostname, uint16_t port, uint8_t use_ipv6) 构造地址信息
static char *forge_request(char *url, char keep_alive, char **host, uint16_t *port, char **headers, uint8_t headers_num)构造请求
uint64_t str_to_uint64(char *str)用来计算Content-Length,将响应结果中的Content-Length的值转换成数字
int main(int argc, char *argv[])主函数,主函数先对输入参数进行判断(getopt),给相关参数赋值;然后构造完整的请求(forge_request);分配线程和worker(W_MALLOC)地址,然后再for循环中执行线程(pthread_create);接下来等待线程结束(pthread_join)读取执行结果;通过ev_tstamp来计算执行时间,最后释放内存。
worker.c
Worker *worker_new(uint8_t id, Config *config, uint16_t num_clients, uint64_t num_requests)创建worker
void worker_free(Worker *worker) 释放worker
void *worker_thread(void* arg)执行线程,启动client,处理请求
client.c
static void client_set_events(Client *client, int events)设置事件驱动监听
Client *client_new(Worker *worker)创建client
void client_free(Client *client)释放client
static void client_reset(Client *client)重置client参数
static uint8_t client_connect(Client *client)发启动连接,连接到目的服务器地址
static void client_io_cb(struct ev_loop *loop, ev_io *w, int revents)事件驱动
void client_state_machine(Client *client)执行请求处理。包括五个状态:
开始CLIENT_START
r = socket(config->saddr->ai_family, config->saddr->ai_socktype, config->saddr->ai_protocol);
连接CLIENT_CONNECTING
if (-1 == connect(client->sock_watcher.fd, client->worker->config->saddr->ai_addr, client->worker->config->saddr->ai_addrlen))
发送请求CLIENT_WRITING
r = write(client->sock_watcher.fd, &config->request[client->request_offset], config->request_size - client->request_offset);
接受响应
CLIENT_READING
r = read(client->sock_watcher.fd, &client->buffer[client->buffer_offset], sizeof(client->buffer) - client->buffer_offset - 1);
错误CLIENT_ERROR
结束CLIENT_END。
static uint8_t client_parse(Client *client, int size) 解析响应结果,包括解析Head和解析Body。
PARSER_START:读取status_code,设置status状态
PARSER_HEADER:解析Content-Length,Connection,Transfer-Encoding。
PARSER_BODY:根据chunk值做不同处理。
weighttp返回结果设置,根据client->success值确定stats.req_success和stats.req_failed。
f (client->success) {
client->worker->stats.req_success++;
client->worker->stats.bytes_body += client->bytes_received - client->header_size;
} else {
client->worker->stats.req_failed++;
}
client->success值修改地方:
/* disconnect */
if (client->parser_state == PARSER_BODY && !client->keepalive && client->status_success
&& !client->chunked && client->content_length == -1) {
client->success = 1;
client->state = CLIENT_END;
} else {
client->state = CLIENT_ERROR;
}
case CLIENT_ERROR:
//printf("client error\n");
client->worker->stats.req_error++;
client->keepalive = 0;
client->success = 0;
client->state = CLIENT_END;
if (client->chunk_size == 0) {
/* chunk of size 0 marks end of content body */
client->state = CLIENT_END;
client->success = client->status_success ? 1 : 0;
return 1;
}
if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) {
/* full response received */
client->state = CLIENT_END;
client->success = client->status_success ? 1 : 0;
}