vislee / leevis.com

Blog
87 stars 13 forks source link

ngx_http_mirror_module 代码分析 #113

Open vislee opened 7 years ago

vislee commented 7 years ago

概述

ngx以前的版本代码11个阶段有个tryfile阶段,从1.13.3以后改为了NGX_HTTP_PRECONTENT_PHASE阶段,且新加了个mirror模块。

github master分支已经有该代码,nginx.org文档还没有说明。 1.13.4发布了该模块,同时文档也有了mirror模块的说明

mirror模块是以子请求的形式把原始请求镜像一份或多份。通过mirror来控制镜像的location,mirror_request_body 控制镜像请求是否镜像body。

唯一的问题是原始uri不会镜像,可以通过$request_uri变量取得原始请求的uri。

指令

Syntax:  mirror uri;
Default:  —
Context:  http, server, location
uri 是server下配置的location的uri,以子请求的形式把当前请求内容镜像到uri下的location。
Syntax:  mirror_request_body on|off;
Default:  on
Context:  http, server, location
是否镜像body。

代码

mirro模块存储指令的结构体为:

typedef struct {
    // ngx_str_t 数组
    ngx_array_t  *mirror;
    ngx_flag_t    request_body;
} ngx_http_mirror_loc_conf_t;

在阶段NGX_HTTP_PRECONTENT_PHASE添加了回调函数ngx_http_mirror_handler。

解析完请求行请求头后会调用到ngx_http_process_request 函数中,该函数修改了可读可写事件的回调后调用ngx_http_handler函数执行http的10个阶段。执行完10个阶段后调用ngx_http_run_posted_requests再执行子请求。

该模块首先从注册到precontent阶段的回调函数ngx_http_mirror_handler开始执行,该回调函数调用了ngx_http_subrequest向父请求posted_requests(sr->main->posted_requests)中添加了子请求。

执行完10个阶段后调用ngx_http_run_posted_requests函数执行子请求。子请求其实就是把当前请求的内容复制一份添加到当前请求的posted_requests上,重新执行http的10个阶段。

代码:

void
ngx_http_process_request(ngx_http_request_t *r)
{
    ......
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;

    ngx_http_handler(r);

    ngx_http_run_posted_requests(c);
}

void
ngx_http_handler(ngx_http_request_t *r)
{
    ......
    // http可写事件回调
    r->write_event_handler = ngx_http_core_run_phases;
    // 执行http的10个阶段
    ngx_http_core_run_phases(r);
}

static ngx_int_t
ngx_http_mirror_handler(ngx_http_request_t *r)
{
    ngx_int_t                    rc;
    ngx_http_mirror_ctx_t       *ctx;
    ngx_http_mirror_loc_conf_t  *mlcf;

    if (r != r->main) {
        return NGX_DECLINED;
    }

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);

    // 没有配置子请求uri
    if (mlcf->mirror == NULL) {
        return NGX_DECLINED;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "mirror handler");

    if (mlcf->request_body) {
        ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);

        if (ctx) {
            return ctx->status;
        }

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mirror_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }

        ctx->status = NGX_DONE;

        ngx_http_set_ctx(r, ctx, ngx_http_mirror_module);

        rc = ngx_http_read_client_request_body(r, ngx_http_mirror_body_handler);
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }

        ngx_http_finalize_request(r, NGX_DONE);
        return NGX_DONE;
    }

    return ngx_http_mirror_handler_internal(r);
}

static ngx_int_t
ngx_http_mirror_handler_internal(ngx_http_request_t *r)
{
    ngx_str_t                   *name;
    ngx_uint_t                   i;
    ngx_http_request_t          *sr;
    ngx_http_mirror_loc_conf_t  *mlcf;

    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);

    name = mlcf->mirror->elts;

    for (i = 0; i < mlcf->mirror->nelts; i++) {
        // 创建子请求,并添加到父请求的posted_requests
        if (ngx_http_subrequest(r, &name[i], &r->args, &sr, NULL,
                                NGX_HTTP_SUBREQUEST_BACKGROUND)
            != NGX_OK)
        {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        sr->header_only = 1;
        sr->method = r->method;
        sr->method_name = r->method_name;
    }

    return NGX_DECLINED;
}

// 执行子请求
void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
    ngx_http_request_t         *r;
    ngx_http_posted_request_t  *pr;

    for ( ;; ) {

        if (c->destroyed) {
            return;
        }

        r = c->data;
        pr = r->main->posted_requests;

        if (pr == NULL) {
            return;
        }

        r->main->posted_requests = pr->next;

        r = pr->request;

        ngx_http_set_log_request(c->log, r);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http posted request: \"%V?%V\"", &r->uri, &r->args);

        // 调用ngx_http_subrequest函数添加子请求设置的回调ngx_http_handler
        r->write_event_handler(r);
    }
}
rfyiamcool commented 7 years ago

+1