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处理链中。