ngx_req_status计数统计模块源码分析 – 作者:stan1y

req_stat配置为计数器按照server{}维度统计状态码,使用时配置如下:

req_status_lazy on;

req_status_zone req_status “$server_addr:$server_port:$upstream_addr” 50M;

req_status req_status;

server {

listen 127.0.0.1:80;

location /check_status {

req_status_show req_status;

}

}

在req_status_zone指令的函数中,将这个字符串进行编译,放入ctx→value,ctx为这个模块的上下文:

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

ccv.complex_value = &ctx->value;

// compile之后,ctx的value存储这一格式

if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {

return NGX_CONF_ERROR;

}

// ctx->val是个string,记录这个key的名字

ctx->val = ngx_palloc(cf->pool, sizeof(ngx_str_t));

if (ctx->val == NULL) {

return NGX_CONF_ERROR;

}

*ctx->val = value[2];

}

ngx_http_reqstat把对应的req_status_zone配置相应的共享内存:

static char *

ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

{

// 这块是从ngx_conf_handler中取出来的配置,按理说我们配置里面都把ngx_http_reqstat放在http下面,应该也是main conf

ngx_http_reqstat_conf_t      *rlcf = conf;

value = cf->args->elts;

// 这个smcf是从cf->ctx中拿出来的reqstat main 配置,按理说和上面的rlcf应该相同,不明白为何不一样。

// 终于弄明白了,cmd->type那块是NGX_HTTP_MAIN_CONF,而ngx_conf_handler那块是NGX_MAIN_CONF,所以一定会走到loc_conf里面,所以cmd第四个那个位置就是这个指令修改的配置最终在哪里的意思

// 因此这块修改都是http块下面生成的loc conf

smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_reqstat_module);

// 将rlcf的monitor初始化,并且将没有的添加进smcf中。

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放入对应阶段中:

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阶段,每个请求都经过,如果没有store的话,初始化store:

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只关联于这个request,一旦这个request销毁,就不复存在,因此每个新的请求都要创建一个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)

{

// 获取共享内存,fnode数组,value数组。

shm_zone = rcf->monitor->elts;

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和上下文ctx

z = shm_zone[i];

ctx = z->data;

// 核心:判断是否是lazy

if (rcf->lazy) {

// 是的话,使用r重新compile val,这次有了upstream相关的,任何变量都能用。

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一致的话,就不用重新在tree里面搜索了,代表两次编译(虽然打开lazy开关),但是一致,

if (value[i].len == val.len

&& ngx_strncmp(value[i].data, val.data, val.len) == 0)

{

fnode = fnode_store[i];

// 如果不行的话,需要重新搜索,之前搜索那个匹配不上。

} else {

// 终于有了真正的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 {

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

}

}

来源:freebuf.com 2021-06-07 10:59:32 by: stan1y

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论