12.8.5 ngx_event_pipe_write_to_downstream方法

ngx_event_pipe_write_to_downstream方法负责把in链表和out链表中管理的缓冲区发送给下游客户端,因为out链表中的缓冲区内容在响应中的位置要比in链表更靠前,所以out需要优先发送给下游。图12-13给出了ngx_event_pipe_write_to_downstream方法的流程图,这个流程图的核心就是在与下游的连接事件上出于可写状态时,尽可能地循环发送out和in链表缓冲区中的内容,其中在第7、第8步中还会涉及shadow域中指向的缓冲区释放的问题。

12.8.5 ngx_event_pipe_write_to_downstream方法 - 图1

图 12-13 ngx_event_pipe_write_to_downstream方法的流程图

下面详细分析一下图12-13中的13个步骤。

1)首先检查上游连接是否结束,判断依据与图12-12中的第1步非常相似,检查upstream_eof、upstream_error还有upstream_done标志位,任意一个标志位为1都表示上游连接不会再收到响应了,这时跳到第2步执行;否则继续检查与下游连接的写事件ready标志位,如果ready为1,表示可以向下游发送响应,这时跳到第5步执行,否则ngx_event_pipe_write_to_downstream方法结束。

2)调用output_filter方法把out链表中的缓冲区发送到下游客户端。

3)调用output_filter方法把in链表中的缓冲区发送到下游客户端。

4)将downstream_done标志位置为1(目前没有任何意义),ngx_event_pipe_write_to_downstream方法结束。

5)计算busy缓冲区中待发送的响应长度,检查它是否超过busy_size配置,如果其大于或等于busy_size,则跳到第10步执行;否则继续向下准备发送out或者in缓冲区中的内容。也就是说,当ngx_http_request_t结构体中out缓冲区中的待发送内容已经超过了busy_size,就跳到第10步,不再发送out和in缓冲区中的内容,优先把ngx_http_request_t结构体的out中的内容发送出去。

6)首先检查out链表是否为空,如果out中有内容,那么立刻跳到第7步准备发送out缓冲区中的响应,如果out为空,那么再检查in链表。如果in链表中有内容,立刻跳到第8步准备发送in缓冲区中的响应,如果in链表也为空,则说明这次调用中没有需要发送的响应,跳到第10步执行。

7)取出out链表首部的第一个ngx_buf_t缓冲区,检查待发送的长度加上这个缓冲区后是否已经超过busy_size配置,如果超过,则立刻跳到第10步执行;如果没有超过,则out自动指向链表中的下一个ngx_chain_t元素,跳到第9步准备将之前取出的第一个缓冲区发送出去。注意,这里还会调用ngx_event_pipe_free_shadow_raw_buf方法来处理这个待发送的缓冲区的shadow域,实际上,这一步就是为了释放free_raw_bufs链表中的缓冲区。

8)取出in链表首部的第一个缓冲区准备发送,所有步骤与第7步相同。

9)将刚才调用ngx_event_pipe_free_shadow_raw_buf方法处理过的缓冲区再添加到out链表首部(待发送的内容都添加到out缓冲区链表中了),同时跳到第6步继续执行这个循环。

10)检查out链表以及之前各个步骤中是否有需要发送的内容(其实是通过一个局部变量flush作为标志位来表示是否有需要发送的内容),当out为空且确实没有待发送的内容时,返回NGX_OK,ngx_event_pipe_write_to_downstream方法结束,否则跳到第11步向下游发送响应。

11)调用output_filter方法向下游发送out缓冲区。

12)调用ngx_chain_update_chains方法更新free、busy、out缓冲区。在图12-8的第4步中曾经介绍过该方法,不再赘述。

13)遍历free链表中的缓冲区,释放缓冲区中的shadow域,这样,这些暂不使用的缓冲区才可以继续用来接收新的来自上游服务器的响应。然后,跳到第1步继续发送响应来执行这个大循环。

至此,buffering配置为1时转发上游响应到下游的整个流程就全部介绍完了,它的流程复杂,但效率很高,作为反向代理使用非常有优势。