ngx_http_post_request
1 定义ngx_http_post_request 函数 定义在 ./nginx-1.24.0/src/http/ngx_http_request.cngx_int_tngx_http_post_request(ngx_http_request_t*r,ngx_http_posted_request_t*pr){ngx_http_posted_request_t**p;if(prNULL){prngx_palloc(r-pool,sizeof(ngx_http_posted_request_t));if(prNULL){returnNGX_ERROR;}}pr-requestr;pr-nextNULL;for(pr-main-posted_requests;*p;p(*p)-next){/* void */}*ppr;returnNGX_OK;}ngx_http_post_request 函数的作用是 将一个需要延迟处理的 HTTP 请求挂载到主请求的 posted_requests 链表末尾 以便在当前事件处理结束后再统一取出并继续执行2 详解1 函数签名ngx_int_tngx_http_post_request(ngx_http_request_t*r,ngx_http_posted_request_t*pr)返回值 NGX_OK —— 操作成功。 NGX_ERROR —— 操作失败参数1 ngx_http_request_t *r 当前需要被“延迟处理”的 HTTP 请求对象参数2 ngx_http_posted_request_t *pr 延迟请求链表节点 准备插入到链表中的新节点2 逻辑流程1 局部变量 2 分配新节点 3 初始化节点 4 遍历链表到末尾插入新节点 5 返回成功1 局部变量{ngx_http_posted_request_t**p;二级指针 p 它的类型是“指向 ngx_http_posted_request_t 指针的指针”2 分配新节点if(prNULL){prngx_palloc(r-pool,sizeof(ngx_http_posted_request_t));if(prNULL){returnNGX_ERROR;}}检查调用者是否提供了现成的节点 若 pr 为 NULL表示需要由函数自己分配节点 否则跳过分配直接使用已有节点3 初始化节点pr-requestr;pr-nextNULL;将待延迟处理的请求对象 r 记录到链表节点的 request 字段中 将新节点的 next 指针初始化为 NULL4 遍历链表到末尾插入新节点for(pr-main-posted_requests;*p;p(*p)-next){/* void */}*ppr;初始化p r-main-posted_requests r-main 指向主请求。无论当前 r 是主请求还是子请求 Nginx 都将延迟请求链表统一挂在主请求的 posted_requests 字段下 这样所有相关请求的延迟调度都能由主请求的事件循环统一管理。 posted_requests 是链表头指针指向链表的第一个节点 取 r-main-posted_requests 得到头指针自身的地址赋给二级指针 p。 此时 p 指向头指针变量*p 就是头指针的值即第一个节点或 NULL。 条件判断*p 检查 *p 是否为 NULL。 若 *p 非空说明当前节点存在循环继续 若 *p NULL表示已到达链表尾部或空链表循环终止。 每次循环体执行前进行判断循环体为空{ /* void */ }所有工作都在步进中完成。 步进p (*p)-next (*p) 是当前节点指针(*p)-next 是当前节点的 next 成员。 (*p)-next 取得该 next 成员自身的地址然后赋给 p。 *p 从指向头节点, 一步步移动到指向下一个节点 *p 也就是 next 循环体为空因为不需要在遍历过程中对节点本身做任何处理 唯一目的是找到尾部的 next 循环结束时 无论原链表是否为空*p 总是最后一个节点的 next 此时 *p 的值为 NULL代表链表末尾。使用二级指针避免了区分“链表为空”和“链表非空”的特殊情况代码。 如果使用一级指针遍历需要对空链表单独处理。 这里通过直接操作 next 域的地址 将插入操作统一成一句 *p pr代码更简洁、高效。将新节点链接到链表尾部 将新节点 pr 赋值给 *p即链表尾部的 next 若原链表为空 p 指向 posted_requests 字段 *p pr 使 posted_requests 字段记录 pr链表变成只有一个节点。 若原链表非空 *p 指向原尾节点的 next *p pr 使原尾节点的 next 指向 pr完成尾插。5 返回成功returnNGX_OK;}