分享

nginx中sub_request的处理

 syden1981 2011-08-21

nginx中sub_request的处理

首先来看subrequest的处理。

什么是subrequest,顾名思义,那就是子请求,也就是在当前的一个请求中nginx再生成一个请求。比如在nginx的HttpAddition这个filter,就有用到subrequest。

这里要注意,一个subrequest是当父reuest执行完毕后才会被执行,并且它会将所有的需要进行的handler phase重新执行一遍(这个我们后面的代码会看到).

这里还涉及到一个很关键的filter,那就是postpone filter,这个filter就用来缓存住父request,这里的缓存就是将需要发送的数据保存到一个链表中。这个是因为会先执行subrequest,然后才会执行request,因此如果有subrequest的话,这个filter就会跳过后面的发送filter,直接返回ok。

sub request是通过post pone filter以及finalize_request,还有下面的三个域来配合实现的,接下来我们会一个个的分析。

因此这里有这样三个概念,一个是postpone_request,一个是post_request,一个是post_subrequest.其实这三个也就是request的三个域了:

Java代码 复制代码 收藏代码
  1. //这个表示主的request,也就是当前的request链中最上面的那个request,通过这个域我们就能判断当前的request是不是subrequest。   
  2. ngx_http_request_t               *main;   
  3. //这个表示当前的request的父request。   
  4.     ngx_http_request_t               *parent;   
  5. //最关键就是下面三个域。   
  6.     ngx_http_postponed_request_t     *postponed;   
  7.     ngx_http_post_subrequest_t       *post_subrequest;   
  8.     ngx_http_posted_request_t        *posted_requests;  


ok,我们一个个来看,先来看postponed,这个域用来缓存父request的数据(也就是将要发送数据的request),而缓存这个动作是在postpone filter中来做的,我们后面回来分析这个filter。下面就是它的结构:

Java代码 复制代码 收藏代码
  1. struct ngx_http_postponed_request_s {   
  2.     ngx_http_request_t               *request;   
  3.     ngx_chain_t                      *out;   
  4.     ngx_http_postponed_request_t     *next;   
  5. };  


可以看到它就是一个很简单的链表,三个域的意思分别为:
request 保存了subrequest
out保存了所需要发送的chain。
next保存了下一个postpone_request.

然后是post_subrequest,这个域保存了子请求的post request,它也就是保存了需要被发送的request. 来看它的结构:

Java代码 复制代码 收藏代码
  1. typedef struct {   
  2.     ngx_http_post_subrequest_pt       handler;   
  3.     void                             *data;   
  4. } ngx_http_post_subrequest_t;  


可以看到它的结构更加简单,一个handler,保存了到时需要执行的回掉函数,一个data,保存了传递的数据。

最后是posted_requests,这个保存了所有的需要处理的request链表,也就是说它即包含子请求也包含父请求。来看它的结构:

Java代码 复制代码 收藏代码
  1. struct ngx_http_posted_request_s {   
  2.     ngx_http_request_t               *request;   
  3.     ngx_http_posted_request_t        *next;   
  4. };  


request保存了需要处理的request,next保存了下一个需要处理的request。

然后我们来详细分析sub request的处理流程以及代码,这里代码的分析顺序是按照sub request的流程来的。

首先来看sub request的设置函数ngx_http_subrequest。

这个函数的主要功能就是新建一个request,然后设置对应的属性,其中大部分属性都是和父request相同的,还有一些特殊的sub request独有的属性我们会在下面的代码中分析到(主要是我上面介绍的4个域)。

这个函数的参数比较多,有6个参数,来看它的原型:
Java代码 复制代码 收藏代码
  1. ngx_int_t   
  2. ngx_http_subrequest(ngx_http_request_t *r,   
  3.     ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,   
  4.     ngx_http_post_subrequest_t *ps, ngx_uint_t flags)  

r表示需要生成子请求的request,uri表示子请求的uri,args表示子请求的参数,psr表示最终生成的子请求,ps表示子请求的post_subrequest,flags主要控制sub request的内容是否要放到内存中。

代码比较长,我们分开来看,下面这段是相关的初始化:

Java代码 复制代码 收藏代码
  1.     
  2. ngx_connection_t              *c;   
  3.     ngx_http_request_t            *sr;   
  4.     ngx_http_core_srv_conf_t      *cscf;   
  5.     ngx_http_postponed_request_t  *pr, *p;   
  6.   
  7. //subrequest表示了当前还可以处理的最大个数的sub request,这个值的默认值是50.表示nginx中能够处理的最多的嵌套子请求的个数是50.   
  8.     r->main->subrequests--;   
  9.   
  10. //如果为0,则表示已达到最大的限制,因此返回error。   
  11.     if (r->main->subrequests == 0) {   
  12.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,   
  13.                       "subrequests cycle while processing \"%V\"", uri);   
  14.         r->main->subrequests = 1;   
  15.         return NGX_ERROR;   
  16.     }   
  17. //新建一个sub request。   
  18.     sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));   
  19.     if (sr == NULL) {   
  20.         return NGX_ERROR;   
  21.     }   
  22.   
  23.     sr->signature = NGX_HTTP_MODULE;   
  24.   
  25. //设置connection。   
  26.     c = r->connection;   
  27.     sr->connection = c;   
  28.   
  29. //下面的初始化大部分都是和父请求一样的。   
  30.     sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);   
  31.     if (sr->ctx == NULL) {   
  32.         return NGX_ERROR;   
  33.     }   
  34.   
  35.     if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,   
  36.                       sizeof(ngx_table_elt_t))   
  37.         != NGX_OK)   
  38.     {   
  39.         return NGX_ERROR;   
  40.     }   
  41. ......................................................   
  42.     sr->request_body = r->request_body;   
  43. //可以看到子请求只会是Get方法。   
  44.     sr->method = NGX_HTTP_GET;   
  45.     sr->http_version = r->http_version;   
  46.   
  47.     sr->request_line = r->request_line;  


接下来这段也是初始化,只不过主要是初始化一些sub request特有的属性。这里最关键的就是两个事件处理函数的赋值,read_event_handler和write_event_handler 。其中读事件的handler被赋值为一个空的函数,也就是在sub request中,不会处理读事件。而写事件的handler被赋值为ngx_http_handler,这个函数我们知道,它就是整个nginx的handler处理的入口,因此也就是说sub request最终会把所有的phase再重新走一遍。

这里还要注意,那就是父请求可能会有多个儿子请求。
Java代码 复制代码 收藏代码
  1. //子请求的uri   
  2.     sr->uri = *uri;   
  3.   
  4.     if (args) {   
  5. //参数设置   
  6.         sr->args = *args;   
  7.     }   
  8.   
  9.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,   
  10.                    "http subrequest \"%V?%V\"", uri, &sr->args);   
  11. //子请求的内容是否需要放到内存中。   
  12.     sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;   
  13. //这个貌似是ssi用到的。   
  14.     sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;   
  15.   
  16. ..............................................................................   
  17.   
  18.     ngx_http_set_exten(sr);   
  19. //设置main,也就是最上层的那个request。   
  20.     sr->main = r->main;   
  21. //设置父request   
  22.     sr->parent = r;   
  23. //可以看到ost_subrequest 被设置为我们传递进来的值。   
  24.     sr->post_subrequest = ps;   
  25.   
  26. //读写事件的处理函数的赋值   
  27.     sr->read_event_handler = ngx_http_request_empty_handler;   
  28.     sr->write_event_handler = ngx_http_handler;   
  29.   
  30. //设置连接的request为子请求。这里的意思是如果父请求设置第二个子请求的话,这里就不需要设置连接的request了。   
  31.     if (c->data == r && r->postponed == NULL) {   
  32.         c->data = sr;   
  33.     }   
  34.   
  35.     sr->variables = r->variables;   
  36.   
  37.     sr->log_handler = r->log_handler;   
  38.   
  39. //开始赋值postponed request.   
  40.     pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));   
  41.     if (pr == NULL) {   
  42.         return NGX_ERROR;   
  43.     }   
  44. //它的request设置为子请求,也就是每个子请求都会用一个postponed request包装起来。   
  45.     pr->request = sr;   
  46.     pr->out = NULL;   
  47.     pr->next = NULL;   
  48. //如果是第一次给父请求设置孩子,那么将pr放到postponed链表的结尾。   
  49.     if (r->postponed) {   
  50.         for (p = r->postponed; p->next; p = p->next) { /* void */ }   
  51. //找到尾部,然后插入。   
  52.         p->next = pr;   
  53.   
  54.     } else {   
  55. //否则直接设置   
  56.         r->postponed = pr;   
  57.     }   
  58.   
  59. //设置内部标记   
  60.     sr->internal = 1;   
  61.   
  62.     sr->discard_body = r->discard_body;   
  63.     sr->expect_tested = 1;   
  64.     sr->main_filter_need_in_memory = r->main_filter_need_in_memory;   
  65.   
  66.     sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;   
  67. //subrequests加1.   
  68.     r->main->subrequests++;   
  69.   
  70. //保存生成的sub request,以供外部使用。   
  71.     *psr = sr;   
  72. //设置post request。   
  73.     return ngx_http_post_request(sr);   
  74. }  


上面的代码有个有疑问的地方,那就是subrequests,我查了下代码只有这个函数里面有对它进行操作,可是这里前面--,后面++,那不是基本没有可能这个值是0。

前面的代码我们可以看到最后会调用ngx_http_post_reques来处理,这个函数是用来讲subrequest放到post request中的。

而post request的调用我会在后面分析到。

Java代码 复制代码 收藏代码
  1. ngx_int_t   
  2. ngx_http_post_request(ngx_http_request_t *r)   
  3. {   
  4.     ngx_http_posted_request_t  *pr, **p;   
  5. //新建一个post request。   
  6.     pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));   
  7.     if (pr == NULL) {   
  8.         return NGX_ERROR;   
  9.     }   
  10.   
  11. //设置request为sub request.   
  12.     pr->request = r;   
  13.     pr->next = NULL;   
  14. //找到post request的尾部。   
  15.     for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }   
  16. //然后赋值。   
  17.     *p = pr;   
  18.   
  19.     return NGX_OK;   
  20. }  


然后来看postpone 这个filter,这个filter就是用来缓存父request的chain, 并且控制sub request的发送。

代码分段来看,先来看第一部分,这部分主要是处理父请求进来的情况,也就是缓存父请求的chain。

Java代码 复制代码 收藏代码
  1. ngx_connection_t              *c;   
  2.     ngx_http_postponed_request_t  *pr;   
  3. //取得当前的链接   
  4.     c = r->connection;   
  5.   
  6. //如果r不等于c->data,前面的分析知道c->data保存的是最新的一个sub request(同级的话,是第一个),因此不等于则说明是需要保存数据的父request。   
  7.     if (r != c->data) {   
  8.   
  9.         if (in) {   
  10. //保存数据(下面会分析这段代码)   
  11.             ngx_http_postpone_filter_add(r, in);   
  12. //这里注意不发送任何数据,直接返回OK。而最终会在finalize_request中处理。   
  13.             return NGX_OK;   
  14.         }   
  15.   
  16.         return NGX_OK;   
  17.     }   
  18.   
  19. //如果r->postponed为空,则说明是最后一个sub request,也就是最新的那个,因此需要将它先发送出去。   
  20.     if (r->postponed == NULL) {   
  21.   
  22. //如果in存在,则发送出去   
  23.         if (in || c->buffered) {   
  24.             return ngx_http_next_filter(r->main, in);   
  25.         }   
  26.   
  27.         return NGX_OK;   
  28.     }  

然后来看ngx_http_postpone_filter_add这个方法,这个方法主要是拷贝当前需要发送的chain到postponed的out域中。

这里要注意一个的就是由于filter有可能会进入多次,因此如果相同的request的in chain会拷贝到相同的posrponed request中。

还有这里要注意就是这里添加的postponed request的request域是NULL,也就是说明这个postponed request就是自己,也就是r==r->postponed->request.

Java代码 复制代码 收藏代码
  1. static ngx_int_t   
  2. ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)   
  3. {   
  4.     ngx_http_postponed_request_t  *pr, **ppr;   
  5.   
  6. //如果postponed存在,则进入相关处理   
  7.     if (r->postponed) {   
  8. //找到postponed的尾部   
  9.         for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }   
  10. //如果为空,则直接添加到当前的chain   
  11.         if (pr->request == NULL) {   
  12.             goto found;   
  13.         }   
  14.   
  15.         ppr = &pr->next;   
  16.     } else {   
  17.         ppr = &r->postponed;   
  18.     }   
  19.   
  20.     pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));   
  21.     if (pr == NULL) {   
  22.         return NGX_ERROR;   
  23.     }   
  24.   
  25.     *ppr = pr;   
  26. //可以看到request是空。   
  27.     pr->request = NULL;   
  28.     pr->out = NULL;   
  29.     pr->next = NULL;   
  30.   
  31. found:   
  32. //最终复制in到pr->out,也就是保存request 需要发送的数据。   
  33.     if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {   
  34.         return NGX_OK;   
  35.     }   
  36.   
  37.     return NGX_ERROR;   
  38. }  


然后再回到ngx_http_postpone_filter,剩下的这段代码主要就是用来发送前面保存的父请求的chain.

Java代码 复制代码 收藏代码
  1.   
  2. //到达这里说明需要发送父请求的数据了。   
  3. if (in) {   
  4. //如果有chain,则保存数据。   
  5.         ngx_http_postpone_filter_add(r, in);   
  6.     }   
  7.   
  8. //开始遍历postponed request.   
  9.     do {   
  10.         pr = r->postponed;   
  11. //如果存在request,则说明这个postponed request是sub request,因此需要将它放到post_request中。   
  12.         if (pr->request) {   
  13.   
  14.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,   
  15.                            "http postpone filter wake \"%V?%V\"",   
  16.                            &pr->request->uri, &pr->request->args);   
  17.   
  18.             r->postponed = pr->next;   
  19.   
  20.             c->data = pr->request;   
  21. //放到post request中。   
  22.             return ngx_http_post_request(pr->request);   
  23.         }   
  24.   
  25.         if (pr->out == NULL) {   
  26.             ngx_log_error(NGX_LOG_ALERT, c->log, 0,   
  27.                           "http postpone filter NULL output",   
  28.                           &r->uri, &r->args);   
  29.   
  30.         } else {   
  31. //说明pr->out不为空,此时需要将保存的父request的数据发送。   
  32.             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,   
  33.                            "http postpone filter output \"%V?%V\"",   
  34.                            &r->uri, &r->args);   
  35. //发送   
  36.             if (ngx_http_next_filter(r->main, pr->out) == NGX_ERROR) {   
  37.                 return NGX_ERROR;   
  38.             }   
  39.         }   
  40.   
  41.         r->postponed = pr->next;   
  42.   
  43.     } while (r->postponed);  


然后我们来看ngx_http_finalize_request中sub request的处理部分,其实ngx_http_finalize_request中,一大部分都是sub request的处理,

这个处理其实主要就是修改一开始介绍的request的三个域。

这里要注意,由于如果有sub request的话,postponed filter会返回NGX_OK。

还有就是所有需要处理的request都必须放入到post request中,儿postponed request中保存的是暂时缓存的不需要发送的request。
  
Java代码 复制代码 收藏代码
  1. //如果r不等于r->main的话,则说明当前的请求是是sub request,此时进入相关处理。   
  2.  if (r != r->main) {   
  3. //如果含有postponed的话,则说明这个request并不是最后一个sub request,因此设置write handler,并且返回。   
  4.         if (r->buffered || r->postponed) {   
  5.   
  6.             if (ngx_http_set_write_handler(r) != NGX_OK) {   
  7.                 ngx_http_close_request(r->main, 0);   
  8.             }   
  9.   
  10.             return;   
  11.         }   
  12. //取得request的父request   
  13.         pr = r->parent;   
  14.   
  15. //如果r等于c->data,则说明当前是最后一个sub request,此时需要修改c->data,以便于在postponed filter中发送保存的父request的数据。   
  16.         if (r == c->data) {   
  17. ...................................................................   
  18.   
  19.             r->done = 1;   
  20. //如果父request的postponed存在并且它的request为当前的r,则开始处理接下来的postponed。   
  21.             if (pr->postponed && pr->postponed->request == r) {   
  22. //取next   
  23.                 pr->postponed = pr->postponed->next;   
  24.             }   
  25. //修改c->data,这个将会在run_post_request中使用,接下来就会分析这个函数。   
  26.             c->data = pr;   
  27.   
  28.         } else {   
  29. //否则则设置write handler.   
  30.             r->write_event_handler = ngx_http_request_finalizer;   
  31.   
  32.             if (r->waited) {   
  33.                 r->done = 1;   
  34.             }   
  35.         }   
  36. //最终将pr也就是父request放入到post request中。   
  37.         if (ngx_http_post_request(pr) != NGX_OK) {   
  38.             ngx_http_close_request(r->main, 0);   
  39.             return;   
  40.         }   
  41. ..............................................   
  42.   
  43.         return;   
  44.     }  


上面有一个函数那就是ngx_http_set_write_handler,这个用来设置write handler,这里是这是write handler为ngx_http_writer,而我们要知道sub request的处理是,不停的保存父request的chain(在postponed filter中),而每次保存完毕之后就返回ngx_ok,此时由于这个request已经经历完毕所有的handler phase,因此我们就需要修改它的write handler,以便于需要发送的时候跳过handler 阶段,因此就设置ngx_http_writer,这个函数主要就是调用out_put filter,而不经过handler phase。

最后我们来看post request的调用在那里。

在nginx中,request的执行是在ngx_http_process_request中的,来看这个函数的最后两句:

Java代码 复制代码 收藏代码
  1. //处理request,每个sub request在处理之前的write handler 都是这个函数。   
  2. ngx_http_handler(r);   
  3.   
  4. //开始run post request   
  5.     ngx_http_run_posted_requests(c);  


我们来详细看ngx_http_run_posted_requests的实现。这个函数就是遍历post request,然后调用它的write handler对request进行处理。

Java代码 复制代码 收藏代码
  1. void  
  2. ngx_http_run_posted_requests(ngx_connection_t *c)   
  3. {   
  4.     ngx_http_request_t         *r;   
  5.     ngx_http_log_ctx_t         *ctx;   
  6.     ngx_http_posted_request_t  *pr;   
  7.   
  8. //开始遍历   
  9.     for ( ;; ) {   
  10.   
  11. //如果连接已经被销毁,则直接返回。   
  12.         if (c->destroyed) {   
  13.             return;   
  14.         }   
  15. //取出c->data(可以看到在finalize request中会修改到这个域的,也就是这个域会始终保存最新的sub request(也就是将要被发送的request).   
  16.         r = c->data;   
  17.         pr = r->main->posted_requests;   
  18.   
  19.         if (pr == NULL) {   
  20.             return;   
  21.         }   
  22. //赋值为下一个。   
  23.         r->main->posted_requests = pr->next;   
  24.   
  25.         r = pr->request;   
  26.   
  27.         ctx = c->log->data;   
  28.         ctx->current_request = r;   
  29. //调用write handler.   
  30.   
  31.         r->write_event_handler(r);   
  32.     }   
  33. }  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多