nginx phase handler处理及log phase处理

参考:nginx处理请求的十一阶段分析_晓镁的博客-CSDN博客

nginx的phase共有十一个处理阶段,使用 ngx_http_phase_t 结构存储每个阶段可用的处理函数(handler),它实际上是动态数组 ngx_array_t,元素类型为 ngx_http_handler_pt,存储在ngx_http_core_main_conf_t中:

typedef struct {

    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */

    ngx_http_phase_engine_t    phase_engine;

    ngx_hash_t                 headers_in_hash;

    ngx_hash_t                 variables_hash;


    ngx_array_t                variables;         /* ngx_http_variable_t */

    ngx_array_t                prefix_variables;  /* ngx_http_variable_t */

    ngx_uint_t                 ncaptures;


    ngx_uint_t                 server_names_hash_max_size;

    ngx_uint_t                 server_names_hash_bucket_size;

    ngx_uint_t                 variables_hash_max_size;

    ngx_uint_t                 variables_hash_bucket_size;


    ngx_hash_keys_arrays_t    *variables_keys;

    ngx_array_t               *ports;

    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

} ngx_http_core_main_conf_t;

typedef struct {

    ngx_array_t                handlers;

} ngx_http_phase_t;

这个其实相当一个二维链表:

引擎处理

使用二维数组 phases 可以调用所有 handler 模块,但它的组织形式不够灵活,效率也不高。实际上,nginx 不会直接使用 handler,而是为每个阶段实现一个特定的 checker 函数,在 checker 里调用 handler,并根据返回值实现阶段的灵活跳转。

// 存储handler/checker,里面用next实现阶段的快速跳转

struct ngx_http_phase_handler_s {

    // 阶段的checker函数

    ngx_http_phase_handler_pt  checker;

    // 每个模块自己的处理函数

    ngx_http_handler_pt        handler;

    // 指向下一个阶段第一个模块在数组里的位置

    ngx_uint_t                 next;

};

// 所有的http请求都要使用这个引擎处理

typedef struct {

    // 存储所有handler/checker的数组,里面用next实现阶段的快速跳转

    ngx_http_phase_handler_t  *handlers;

    // server重写的跳转位置

    ngx_uint_t                 server_rewrite_index;

    // location重写的跳转位置

    ngx_uint_t                 location_rewrite_index;

} ngx_http_phase_engine_t;

引擎的初始化:

在解析到 http{} 块时,ngx_http_block() 函数会调用所有模块的 postconfiguration 函数把自己的 handler 处理函数添加到 phases 数组里。

随后 nginx 执行函数 ngx_http_init_phase_handlers(),该函数会遍历 phases 数组,计算 handler 模块的数量,统计所以已经注册的 handler 数量并分配内存,再安阶段分类,把每个 handler 与对应阶段的 checker 组合起来,填入引擎数组。

举个例子,假设 POST_READ  阶段有3个模块分别注册了回调函数,SERVER_REWRITE 有2个模块注册了回调函数,那么 ph[0-2] 在 POST_READ 阶段,ph[3-4] 是在 SERVER_REWRITE 阶段。

前一个阶段的所有的 ngx_http_phase_handler_s 中 next 都指向下一个阶段的开始。比如下面3个的值都为 3,序号 3 刚好是下一个阶段 SERVER_REWRITE 序号起始。

ph[0]->next = 3

ph[1]->next = 3

ph[2]->next = 3

举个运行时的例子。

运行中时ph数组的内容
数组下标      checker                                                 handler                                   next

引擎运行机制

ngx_http_core_run_phases:

ngx_http_request_t 里的成员 phase_handler 标记了当前处理过程中所在阶段的 handler 序号,从 0 开始,通常是 NGX_HTTP_SERVER_REWRITE_PHASE 的第一个模块 ngx_http_rewrite_module。

在接收完请求头后,nginx 开始调用函数 ngx_http_core_run_phases 执行引擎。

// 启动引擎数组处理请求

// 从phase_handler的位置开始调用模块处理

void

ngx_http_core_run_phases(ngx_http_request_t *r)

{

    ngx_int_t                   rc;

    ngx_http_phase_handler_t   *ph;

    ngx_http_core_main_conf_t  *cmcf;



    // 得到core main配置

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);



    // 获取引擎里的handler数组

    ph = cmcf->phase_engine.handlers;



    // 从phase_handler的位置开始调用模块处理

    // 外部请求的引擎数组起始序号是0,从头执行引擎数组,即先从Post read开始

    // 内部请求,即子请求.跳过post read,直接从server rewrite开始执行,即查找server

    while (ph[r->phase_handler].checker) {

        // 调用引擎数组里的checker

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        // checker会检查handler的返回值

        // 如果handler返回again/done那么就返回ok

        // 退出引擎数组的处理

        // 由于r->write_event_handler = ngx_http_core_run_phases

        // 当再有写事件时会继续从之前的模块执行

        // 如果checker返回again,那么继续在引擎数组里执行

        // 模块由r->phase_handler序号指定,可能会有阶段的跳跃

        if (rc == NGX_OK) {

            return;

        }

    }

}

不同阶段的 checker 流程大同小异,我们看下 NGX_HTTP_CONTENT_PHASE 的 ngx_http_core_content_phase()。

ngx_int_t

ngx_http_core_content_phase(ngx_http_request_t *r,

    ngx_http_phase_handler_t *ph)

{

    size_t     root;

    ngx_int_t  rc;

    ngx_str_t  path;



    // 检查请求是否有handler,也就是location里定义了handler

    if (r->content_handler) {

        // 设置写事件为ngx_http_request_empty_handler

        // 即暂时不再进入ngx_http_core_run_phases

        // 这是因为内容产生阶段已经是“最后”一个阶段了,不需要再走其他阶段

        // 之后发送数据时会改为ngx_http_set_write_handler

        // 但我们也可以修改,让写事件触发我们自己的回调

        r->write_event_handler = ngx_http_request_empty_handler;



        // 调用location专用的内容处理handler

        // 返回值传递给ngx_http_finalize_request

        // 相当于处理完后结束请求

        // 这种用法简化了客户代码,相当于模板方法模式

        // rc = handler(r); ngx_http_finalize_request(rc);

        // 结束请求

        // 但如果count>1,则不会真正结束

        // handler可能返回done、again,例如调用read body

        ngx_http_finalize_request(r, r->content_handler(r));



        // 需要在之后的处理函数里继续处理,不能调用write_event_handler

        return NGX_OK;

    }



    // 没有专门的handler

    // 调用每个模块自己的处理函数

    rc = ph->handler(r);

    if (rc != NGX_DECLINED) {

        ngx_http_finalize_request(r, rc);

        return NGX_OK;

    }

    // 模块handler返回decline,需要继续执行本阶段的下一个 handler。

    ph++;



    // 下一个模块有 checker

    if (ph->checker) {

        // 索引加1

        r->phase_handler++;

        // again继续引擎数组的循环

        return NGX_AGAIN;

    }

    // 已经到了引擎数组的最末尾

    // 没有一个content模块可以处理

    // 结束引擎数组的循环

    return NGX_OK;

}

日志处理阶段

日志处理不在 ngx_http_core_run_phases 里调用,而是在请求完毕时调用。

// 请求已经结束,调用log模块记录日志

// 在ngx_http_finalize_request和ngx_http_free_request里调用

static void

ngx_http_log_request(ngx_http_request_t *r)

{

    ngx_uint_t                  i, n;

    ngx_http_handler_pt        *log_handler;

    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    // log handler不在引擎数组里

    log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;

    n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;



    // 不检查handler的返回值,直接调用,不使用checker

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

        log_handler[i](r);

    }

}

日志阶段是在给出应答后才进行调用

配置解析阶段:

static ngx_int_t

ngx_http_log_init(ngx_conf_t *cf)

{

ngx_str_t   *value;

ngx_array_t a;

ngx_http_handler_pt   *h;

ngx_http_log_fmt_t   *fmt;

ngx_http_log_main_conf_t   *lmcf;

ngx_http_core_main_conf_t  *cmcf;



lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);



if (lmcf->combined_used) {

if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {

return NGX_ERROR;

}



value = ngx_array_push(&a);

if (value == NULL) {

return NGX_ERROR;

}



*value = ngx_http_combined_fmt;

fmt = lmcf->formats.elts;



if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)

!= NGX_CONF_OK)

{

return NGX_ERROR;

}

}



cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);



h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);

if (h == NULL) {

return NGX_ERROR;

}



*h = ngx_http_log_handler;



return NGX_OK;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值