简介
ngx_http_reqstat_module - The Tengine Web Server
该模块功能为监视tengine运行状况,包括:连接数、请求数、各种响应码范围的请求数、输入输出流量、rt、upstream访问等。
该模块是tengine自带的module,如我们想在openresty中使用该模块,不仅需要add-module编译至openresty的nginx中,还需要改一些nginx相关的代码。
本文目的就是分析该模块,并把该tengine模块集成到openresty中。之后,reqstat监视的数据用来对接云监控系统,监控olwaf引擎运行状态。
只能按照server块的粒度来进行统计状态码。
示例配置如下:
req_status_zone server_status "$server_addr:$server_port:$upstream_addr" 100M;
req_status server_status;server {listen 10.10.10.10:82;location /server_status {req_status_show req_status;}
}
Syntax: req_status_zone zone_name value size
Default: none
Context: main
创建统计使用的共享内存k-v格式。zone_name是共享内存的名称,value用于定义key,通常设置为nginx内置变量。size是共享内存的大小。
例如:创建名为“server_status”的共享内存,大小100M,使用“$host,$server_addr:$server_port”为key作为统计粒度。
req_status_zone server_status "$server_addr:$server_port:$upstream_addr" 100M;
req_status_zone指令对应的函数ngx_http_reqstat_zone分析。
将这个字符串进行编译放入ctx->value,ctx为reqstat_module的上下文。
ngx_http_reqstat_zone函数
static char * ngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ... ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_reqstat_ctx_t)); ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = &ctx->value; //指向ctx->value if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } //经过compile后ctx的value存储这一格式
ctx->val = ngx_palloc(cf->pool, sizeof(ngx_str_t)); if (ctx->val == NULL) { return NGX_CONF_ERROR; } //ctx->val是string类型记录配置中key *ctx->val = value[2]; ... } nginx中ngx_http_compile_complex_value解析变量配置使用方法: typedef struct{ ngx_conf_t *cf; ngx_str_t *value; ngx_http_complex_value_t *complex_value; //... } ngx_http_compile_complex_value_t; //代码中常用ccv作为变量名 //ccv->cf和cc->value(带变量配置的字符串)为入参 //ccv->complex_value 为出参,常保存在xxx_conf_t中。 //该结构体常常与ngx_http_compile_complex_value函数一起配合使用。 //ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)。 //编译解析带变量的配置如上文中$server_addr:$server_port:$upstream_addr。 --------------上面是带变量配置解析动作,下面就是使用获取变量配置动作------------------- //入参为r和val,val通过ngx_http_compile_complex_value()获得。 //value为变量求值后的具体值。 //ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val, ngx_str_t *value); |
ngx_http_reqstat函数
ngx_http_reqstat函数处理对应的req_status_zone配置相应的共享内存。
每个ngx_module_t关联一个ngx_command_t数组。 每个ngx_command_t结构对应nginx.conf配置文件中的一个指令。 ngx_command_t结构体定义如下: struct ngx_command_s {
static char * ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //cmd->type为NGX_HTTP_MAIN_CONF //cmd->conf结构指针偏移,为这个指令修改的配置最终偏移 ... // ngx_conf_handler为NGX_MAIN_CONF从中取出配置 //配置里面把ngx_http_reqstat放在http下面 ngx_http_reqstat_conf_t *rlcf = conf; value = cf->args->elts; //smcf为从cf->ctx中获取reqstat main配置。这与上面rlcf不一样。 smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_reqstat_module); //rlcf monitor初始化 rlcf->monitor = ngx_array_create(cf->pool, cf->args->nelts - 1, sizeof(ngx_shm_zone_t *)); for (i = 1; i < cf->args->nelts; i++) { // 添加对应配置数量的shm_zone放入rlcf->monitor数组 shm_zone = ngx_shared_memory_add(cf, &value[i], 0, &ngx_http_reqstat_module); z = ngx_array_push(rlcf->monitor); *z = shm_zone; z = smcf->monitor->elts; for (j = 0; j < smcf->monitor->nelts; j++) { if (!ngx_strcmp(value[i].data, z[j]->shm.name.data)) { break; } } // smcf存一个全量 if (j == smcf->monitor->nelts) { z = ngx_array_push(smcf->monitor); if (z == NULL) { return NGX_CONF_ERROR; } *z = shm_zone; } } ... } |
配置指定完之后,调用ngx_http_reqstat_init来把两个handler放入对应阶段中。
ngx_http_reqstat_init函数
static ngx_int_t ngx_http_reqstat_init(ngx_conf_t *cf) { ... cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); *h = ngx_http_reqstat_log_handler; h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers); *h = ngx_http_reqstat_init_handler; ... } |
每个请求都要经过ngx_http_reqstat_init_handler在rewrite阶段。
创建初始化ngx_http_reqstat_store_t store。
ngx_http_reqstat_init_handler函数
static ngx_int_t ngx_http_reqstat_init_handler(ngx_http_request_t *r) { ... ngx_http_reqstat_conf_t *rmcf, *rlcf; ngx_http_reqstat_store_t *store; store = ngx_http_get_module_ctx(r, ngx_http_reqstat_module); rmcf = ngx_http_get_module_main_conf(r, ngx_http_reqstat_module); rlcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module); if (store == NULL) { if (r->variables[rmcf->index].valid) { return NGX_DECLINED; } // 创建store store = ngx_http_reqstat_create_store(r, rlcf); if (store == NULL) { return NGX_ERROR; } // store关联当前的r,一旦这个请求r ngx_http_request_t销毁,就没有了。 // 因此每个新的请求都要创建一个store。 ngx_http_set_ctx(r, store, ngx_http_reqstat_module); } ... } |
ngx_http_reqstat_create_store函数
static ngx_http_reqstat_store_t * ngx_http_reqstat_create_store(ngx_http_request_t *r, ngx_http_reqstat_conf_t *rlcf) { ... store = ngx_pcalloc(r->pool, sizeof(ngx_http_reqstat_store_t)); // 创建monitor index和value index if (ngx_array_init(&store->monitor_index, r->pool, rlcf->monitor->nelts, sizeof(ngx_http_reqstat_rbnode_t *)) == NGX_ERROR) { return NULL; } if (ngx_array_init(&store->value_index, r->pool, rlcf->monitor->nelts, sizeof(ngx_str_t)) == NGX_ERROR) { return NULL; } // 在shm_zone里查找val找到node,每个shm_zone对应store的value_index和monitor_index shm_zone = rlcf->monitor->elts; for (i = 0; i < rlcf->monitor->nelts; i++) { z = shm_zone[i]; ctx = z->data; if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "failed to reap the key \"%V\"", ctx->val); continue; } value = ngx_array_push(&store->value_index); *value = val; fnode = ngx_http_reqstat_rbtree_lookup(shm_zone[i], &val); fnode_store = ngx_array_push(&store->monitor_index); *fnode_store = fnode; ... } |
ngx_http_reqstat_log_handler函数
static ngx_int_t ngx_http_reqstat_log_handler(ngx_http_request_t *r) { ... shm_zone = rcf->monitor->elts; // 获取共享内存node和value数组。 fnode_store = store->monitor_index.elts; value = store->value_index.elts; // 遍历之前的monitor_index与shm_zone数量是对应的 // 给每个shm_zone加入这个计数 for (i = 0; i < store->monitor_index.nelts; i++) { if (fnode_store[i] == NULL) { continue; } // 获取z z = shm_zone[i]; // 获取上下文ctx ctx = z->data; if (rcf->lazy) { // 判断是否是lazy,是的话使用r重新compile val // 这里用到了上面介绍的ngx_http_compile_complex_value获取变量值 if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "failed to reap the key \"%V\"", ctx->val); continue; } // 如果val与之前编译的value一致,无需在reqstat_rbtree里搜索 if (value[i].len == val.len && ngx_strncmp(value[i].data, val.data, val.len) == 0) { fnode = fnode_store[i]; } else { // 否则需要在reqstat_rbtree里搜索,找到了fnode fnode = ngx_http_reqstat_rbtree_lookup(shm_zone[i], &val); if (fnode == NULL) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "failed to alloc node in zone \"%V\", " "enlarge it please", &z->shm.name); fnode = fnode_store[i]; } else { //如果fnode不为空则开始计数 ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_OUT, r->connection->sent); ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_IN, r->connection->received); if (store) { store->recv = r->connection->received; } } } } else { fnode = fnode_store[i]; } ... } |
ngx_http_reqstat_module集成到openresty
把tengine的ngx_http_reqstat.h和ngx_http_reqstat_module.c文件,拷贝到openresty的bundle->nginx->src->http->modules文件夹中。
然后nginx->auto->options文件增加
--with-http_reqstat_module) HTTP_REQ_STATUS=YES ;;
--with-http_reqstat_module enable ngx_http_reqstat_module
nginx->src->core->ngx_connection.h文件添加
struct ngx_connection_s {void *data;ngx_event_t *read;ngx_event_t *write;ngx_socket_t fd;ngx_recv_pt recv;ngx_send_pt send;ngx_recv_chain_pt recv_chain;ngx_send_chain_pt send_chain;ngx_listening_t *listening;off_t sent;off_t received; #增加received ngx_log_t *log;ngx_pool_t *pool;int type;struct sockaddr *sockaddr;socklen_t socklen;ngx_str_t addr_text;ngx_proxy_protocol_t *proxy_protocol;#if (NGX_SSL || NGX_COMPAT)ngx_ssl_connection_t *ssl;
#endifngx_udp_connection_t *udp;struct sockaddr *local_sockaddr;socklen_t local_socklen;ngx_buf_t *buffer;ngx_queue_t queue;ngx_atomic_uint_t number;ngx_uint_t requests;......}
nginx->src->http->ngx_http.c文件增加
ngx_http_output_header_filter_pt ngx_http_top_header_filter;
ngx_http_output_body_filter_pt ngx_http_top_body_filter;
ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;ngx_int_t (*ngx_http_top_input_body_filter) (ngx_http_request_t *r,ngx_buf_t *buf); #增加ngx_str_t ngx_http_html_default_types[] = {ngx_string("text/html"),ngx_null_string
};
nginx->src->http->ngx_http.h文件增加
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter;
extern ngx_http_input_body_filter_pt ngx_http_top_input_body_filter; #增加
nginx->src->http->ngx_http_upstream.h文件增加
typedef struct {ngx_uint_t status;ngx_msec_t response_time;ngx_msec_t connect_time;ngx_msec_t header_time;time_t header_sec; #增加ngx_uint_t header_msec; #增加ngx_msec_t queue_time;off_t response_length;off_t bytes_received;off_t bytes_sent;ngx_str_t *peer;
} ngx_http_upstream_state_t;
nginx->src->http->ngx_http_write_filter_module.c文件增加
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);ngx_int_t (*ngx_http_write_filter_stat)(ngx_http_request_t *r) = NULL; #增加nginx->src->http->ngx_http_core_module.h文件增加struct ngx_http_core_loc_conf_s {ngx_str_t name; /* location name */#if (NGX_PCRE)ngx_http_regex_t *regex;
#endifunsigned noname:1; /* "if () {}" block or limit_except */unsigned lmt_excpt:1;unsigned named:1;unsigned exact_match:1;unsigned noregex:1;unsigned auto_redirect:1;//......ngx_bufs_t client_body_buffers; //增加size_t client_body_postpone_size; //增加size_t client_body_buffer_size; /* client_body_buffer_size */size_t send_lowat; /* send_lowat */size_t postpone_output; /* postpone_output */size_t sendfile_max_chunk; /* sendfile_max_chunk */size_t read_ahead; /* read_ahead */size_t subrequest_output_buffer_size;/* subrequest_output_buffer_size *///......ngx_uint_t server_tag_type; /* server tag type 增加*/ngx_str_t server_tag; /* customized server tag 增加*/ngx_str_t server_tag_header; /* server tag header 增加*///......
}ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);ngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size);typedef ngx_int_t (*ngx_http_input_body_filter_pt)(ngx_http_request_t *r, ngx_buf_t *buf); //增加
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain);
typedef ngx_int_t (*ngx_http_request_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain);