nginx subrequest(子请求) 原理及使用

nginx subrequest主要有以下几种类型

#define NGX_HTTP_SUBREQUEST_IN_MEMORY      2   //请求在内存中执行
#define NGX_HTTP_SUBREQUEST_WAITED         4   //阻塞延后处理
#define NGX_HTTP_SUBREQUEST_CLONE          8   //完全拷贝主请求的处理行为
#define NGX_HTTP_SUBREQUEST_BACKGROUND     16  //请求在"后台"执行 后面会详细介绍nginx"后台"处理的机制

描述subrequest请求链状处理结构 (延后处理的请求结构)

struct ngx_http_postponed_request_s {
    ngx_http_request_t               *request;  //请求实例
    ngx_chain_t                      *out;      //请求携带的响应数据信息
    ngx_http_postponed_request_t     *next;     //指向下一个postponed的请求结构
}

投递的子请求结构

typedef struct {
    ngx_http_post_subrequest_pt       handler;   //子请求post处理触发的执行函数
    void                             *data;     //设置待处理的数据
} ngx_http_post_subrequest_t

已经 “投递”的请求链结构

struct ngx_http_posted_request_s {
    ngx_http_request_t               *request;    //请求实例
    ngx_http_posted_request_t        *next;       //指向已投递请求链的下一个
}

创建子请求ngx_http_subrequest 实现

ngx_int_t
ngx_http_subrequest(ngx_http_request_t *r,
    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
    ngx_http_post_subrequest_t *ps, ngx_uint_t flags)
{
...
    if (r->subrequests == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "subrequests cycle while processing \"%V\"", uri);
        return NGX_ERROR;
    }

    /*
     * 1000 is reserved for other purposes.
     */
    if (r->main->count >= 65535 - 1000) {   //处理计数的最大值不能超过 55535
        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
                      "request reference counter overflow "
                      "while processing \"%V\"", uri);
        return NGX_ERROR;
    }

    sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t)); //为子请求分配内存
    if (sr == NULL) {
        return NGX_ERROR;
    }

    sr->signature = NGX_HTTP_MODULE; //设置请求的签名信息

    c = r->connection;
    sr->connection = c;

    sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
    if (sr->ctx == NULL) {
        return NGX_ERROR;
    }

    if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,
                      sizeof(ngx_table_elt_t)) //配置请求头响应头部
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }
    ...//设置子请求main server location级别的配置信息

    sr->pool = r->pool;  //子请求共享请求的内存池
    ... //必要的数据清理及内容拷贝 子请求配置的请求method为GET

    if (args) {
        sr->args = *args;  //函数参数配置了args参数 则设置参数到请求参数中
    }

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http subrequest \"%V?%V\"", uri, &sr->args);

    //根据函数参数来配置子请求的类型信息
    sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
    sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
    sr->background = (flags & NGX_HTTP_SUBREQUEST_BACKGROUND) != 0;
    ... 
    sr->main = r->main; //main请求是唯一的
    sr->parent = r;   //设置当前的请求为子请求的父节点
    sr->post_subrequest = ps;  //post"投递"处理设置
    sr->read_event_handler = ngx_http_request_empty_handler; //读事件置为空处理函数(子请求的处理是不受请求端控制的)
    sr->write_event_handler = ngx_http_handler; //设置写事件处理(即响应到请求端的处理)

    sr->variables = r->variables; //请求的变量信息设置

    sr->log_handler = r->log_handler; //共用同一个日志

    if (!sr->background) {   //对于不是NGX_HTTP_SUBREQUEST_BACKGROUND标志的请求 会将请求放在一个名为postponed的链结构中 新的子请求会放在链的尾部 
        if (c->data == r && r->postponed == NULL) {
            c->data = sr;
        }

        pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
        if (pr == NULL) {
            return NGX_ERROR;
        }

        pr->request = sr;
        pr->out = NULL;
        pr->next = NULL;

        if (r->postponed) {
            for (p = r->postponed; p->next; p = p->next) { /* void */ }
            p->next = pr;

        } else {
            r->postponed = pr;
        }
    }

    sr->internal = 1;

    sr->discard_body = r->discard_body;
    sr->expect_tested = 1;
    sr->main_filter_need_in_memory = r->main_filter_need_in_memory;

    sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;  //设置uri可重写的次数
    sr->subrequests = r->subrequests - 1; 
    /*
     减少子请求可用数量的计数默认的最大可用值为NGX_HTTP_MAX_SUBREQUESTS +1  这里NGX_HTTP_MAX_SUBREQUESTS配置的值为50 意味着除了main以外 子请求的递归深度不能超过50层
    */

    tp = ngx_timeofday();  //设置请求的开始时间
    sr->start_sec = tp->sec;
    sr->start_msec = tp->msec;

    r->main->count++; //整个请求的计数加1

    *psr = sr;

    if (flags & NGX_HTTP_SUBREQUEST_CLONE) {  //设置子请求的标志为NGX_HTTP_SUBREQUEST_CLONE 设置当前请求的所有处理行为到子请求
        sr->method = r->method;
        sr->method_name = r->method_name;
        sr->loc_conf = r->loc_conf;
        sr->valid_location = r->valid_location;
        sr->valid_unparsed_uri = r->valid_unparsed_uri;
        sr->content_handler = r->content_handler;
        sr->phase_handler = r->phase_handler;
        sr->write_event_handler = ngx_http_core_run_phases;

        ngx_http_update_location_config(sr);
    }

    return ngx_http_post_request(sr, NULL); //将子请求投放到main请求的posted_request链中
}

nginx 对请求的处理过程
nginx投放请求到posted_requests中

ngx_int_t
ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)
{
    ngx_http_posted_request_t  **p;

    if (pr == NULL) {  //没有配置posted_requests处理请求 则创建
        pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
        if (pr == NULL) {
            return NGX_ERROR;
        }
    }

    pr->request = r;
    pr->next = NULL;

    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ } //将posted_request处理结构投放到posted_requests链尾部

    *p = pr; //设置posted_requests链尾部指针

    return NGX_OK;
}

nginx处理已经投放的请求处理

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) {  //tcp连接已经被销毁 已经没有处理的必要 直接返回
            return;
        }

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

        if (pr == NULL) { //posted_reuquests为空 说明没有需要处理的posted_request
            return;
        }

        r->main->posted_requests = pr->next; 注意到posted_requests指针的位置移动 每处理一个posted_request 就会指向链的下一个

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

        r->write_event_handler(r);
    }
}

nginx 对postponed 请求的处理过程 这里指的是对子请求的处理

ngx_http_finalize_request终止请求中对子请求的处理
1.

if (r != r->main && r->post_subrequest) {  //并非main主请求 调用子请求创建时设置的post_subrequest处理函数
        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc); 
    }
    if (r != r->main) {  //先判断是否为主请求
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (r->background) { //background 标记是没有postponed需要处理的
            if (!r->logged) {
                if (clcf->log_subrequest) {
                    ngx_http_log_request(r);
                }

                r->logged = 1;

            } else {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "subrequest: \"%V?%V\" logged again",
                              &r->uri, &r->args);
            }

            r->done = 1;   //设置已经完成了请求处理
            ngx_http_finalize_connection(r); //结束与请求端的连接
            return;
        }

        if (r->buffered || r->postponed) { //buffered或者postponed标记 意味着还有事件处理没有完整

            if (ngx_http_set_write_handler(r) != NGX_OK) {  //设置写事件 对于还有未处理完的读事件和写事件准备  
                ngx_http_terminate_request(r, 0);
            }

            return;
        }

        pr = r->parent;   //请求树结构往上回溯

        if (r == c->data) {

            r->main->count--;  //减少main的处理计数

            if (!r->logged) {
                if (clcf->log_subrequest) {
                    ngx_http_log_request(r);
                }

                r->logged = 1;

            } else {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "subrequest: \"%V?%V\" logged again",
                              &r->uri, &r->args);
            }

            r->done = 1;

            if (pr->postponed && pr->postponed->request == r) {
                pr->postponed = pr->postponed->next; //postponed向后移动
            }

            c->data = pr;

        } else {

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

            r->write_event_handler = ngx_http_request_finalizer; //设置写处理函数

            if (r->waited) {
                r->done = 1;
            }
        }

        if (ngx_http_post_request(pr, NULL) != NGX_OK) { //将子请求投放到posted_request处理链中
            r->main->count++;
            ngx_http_terminate_request(r, 0);
            return;
        }

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

        return;
    }

ngx_http_postpone_filter 的处理过程 这里是对响应包体部分

static ngx_int_t
ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
   ... 
    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                   "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);

    if (r != c->data) { //对于inactive的请求 如果用于响应的chain不为空 也会被加入到postponed请求的out响应链中

        if (in) {
            if (ngx_http_postpone_filter_add(r, in) != NGX_OK) {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

#if 0
        /* TODO: SSI may pass NULL */
        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                      "http postpone filter NULL inactive request");
#endif

        return NGX_OK;
    }

    if (r->postponed == NULL) { //没有postponed链数据  交由下一个body_filter进行处理

        if (in || c->buffered) {
            return ngx_http_next_body_filter(r->main, in);
        }

        return NGX_OK;
    }

    if (in) {  //被加入到postponed请求的out响应链中
        if (ngx_http_postpone_filter_add(r, in) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    do {
        pr = r->postponed;   //遍历postponed请求链 投放到main请求的posted_request链中

        if (pr->request) {

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http postpone filter wake \"%V?%V\"",
                           &pr->request->uri, &pr->request->args);

            r->postponed = pr->next;

            c->data = pr->request;

            return ngx_http_post_request(pr->request, NULL);
        }

        if (pr->out == NULL) { //postponed响应链为空 意味着其实没有响应数据
            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                          "http postpone filter NULL output");

        } else {
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http postpone filter output \"%V?%V\"",
                           &r->uri, &r->args);
                     //postponed响应链中有有效数据 则交由下一个body_filter进行处理
            if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }

        r->postponed = pr->next; //遍历下一个

    } while (r->postponed);

    return NGX_OK;
}

将请求的响应in 链加入到postponed out响应链中

static ngx_int_t
ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_postponed_request_t  *pr, **ppr;

    if (r->postponed) {
        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ } //得到postponed的尾部

        if (pr->request == NULL) {  //postponed_request中的请求为空 直接再利用
            goto found;
        }

        ppr = &pr->next;

    } else {
        ppr = &r->postponed;
    }

    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));  //为postponed_request分配内存
    if (pr == NULL) {
        return NGX_ERROR;
    }

    *ppr = pr;

    pr->request = NULL;
    pr->out = NULL;
    pr->next = NULL;

found:

    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {  //将in链中的指针等信息拷贝到out链中
        return NGX_OK;
    }

    return NGX_ERROR;
}

这里以一张subrequest子请求处理流程图形象表示nginx 对子请求的处理过程 这里的流程图也是网上比较常用的

root为http请求的main请求节点 sub为subrequest子请求节点。main请求和subrequest请求都有一个postponed处理
链 请求链后面可以加入同级别的sibling的子请求postponed信息 里面设置了待处理的数据及处理发生时 的handler处理函数
postponed 是一种延时处理的方式。 在body_filter触发时 会将子请求的postponed的请求加入到main请求的posted_requests处理链中 处理链在写事件处理触发的时候就会被处理 这样就有了子请求的处理机会,这种处理机制与其命名postponed (延时投放) 是一致的。除了在body_filter中触发 同时在ngx_http_finalize_request发生时 subrequest的postponed也会被投放到posted_requests处理链中。

你可能感兴趣的:(nginx,web,sub_request)