Weighttp 源码分析

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;
				}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值