12.8.3 转发响应的包体
在图12-9中我们看到,处理上游读事件的方法是ngx_http_upstream_process_upstream,处理下游写事件的方法是ngx_http_upstream_process_downstream,但它们最终都是通过ngx_event_pipe方法实现缓存转发响应功能的(类似于在12.7.2节中介绍过的无缓存转发响应情形,ngx_http_upstream_process_non_buffered_upstream方法负责处理上游读事件,ngx_http_upstream_process_non_buffered_downstream方法负责处理下游写事件,但它们最终都是通过ngx_http_upstream_process_non_buffered_request方法实现转发响应功能的)。无论是否打开缓存,它们的代码都非常相似,所以本节不再罗列这两种方法的代码,直接开始介绍ngx_event_pipe方法,先来看看它的定义。
ngx_int_t ngx_event_pipe(ngx_event_pipe_t*p,ngx_int_t do_write)
其中,p参数正是负责转发响应的ngx_event_pipe_t结构体,而do_write则是标志位,其为1时表示需要向下游客户端发送响应,为0时表示仅需要由上游客户端接收响应。图12-10给出了ngx_event_pipe方法的流程图,该方法通过调用ngx_event_pipe_read_upstream方法读取上游响应,调用ngx_event_pipe_write_to_downstream方法向下游发送响应,因此,在流程图中暂时看不出内存缓冲区与临时缓存文件的用法。
图 12-10 ngx_event_pipe方法的流程图
下面介绍图12-10中的10个步骤。
1)检查do_write标志位,如果do_write为0,则直接跳到第5步开始读取上游服务器发来的响应;如果do_write为1,则继续执行第2步。
2)调用ngx_event_pipe_write_to_downstream方法(参见12.8.5节)向下游客户端发送响应包体,检测其返回值:如果返回NGX_OK,则跳到第5步处理上游的读事件;如果返回NGX_ABORT,则跳到第3步执行;如果返回NGX_BUSY,则跳到第4步执行。
3)ngx_event_pipe方法结束,返回NGX_ABORT表示请求处理失败。
4)ngx_event_pipe方法结束,返回NGX_OK表示本次暂不往下执行。
5)调用ngx_event_pipe_read_upstream方法(参见12.8.4节)读取上游服务器的响应,同时检测其返回值以及ngx_event_pipe_t结构体中的read和upstream_blocked标志位:如果返回NGX_ABORT,则跳到第3步执行,否则检查read和upstream_blocked标志位。如果这两个标志位同时为0,那么跳到第7步执行;如果这两个标志位有任一个为1,则表示需要向下游发送读到的响应,跳到第6步执行。在这里,read标志位为1时表示ngx_event_pipe_read_upstream方法执行后读取到了响应,而upstream_blocked为1时则表示执行后需要暂时停止读取上游响应,需要通过向下游发送响应来清理出空闲缓冲区,以供ngx_event_pipe_read_upstream方法再次读取上游的响应。
6)设置do_write标志位为1,继续跳到第1步向下游发送刚收到的上游响应,重复这个循环。
7)调用ngx_handle_read_event方法将上游的读事件添加到epoll中,等待下一次接收到上游响应的事件出现。
8)调用ngx_add_timer方法将上游的读事件添加到定时器中,超时时间就是ngx_event_pipe_t结构体的read_timeout成员(参见图12-9的第3步关于该成员的初始化)。
9)调用ngx_handle_write_event方法将下游的写事件添加到epoll中,等待下一次可以向下游发送响应的事件出现。
10)调用ngx_add_timer方法将下游的写事件添加到定时器中,超时时间就是ngx_event_pipe_t结构体的send_timeout成员(参见图12-9的第3步关于该成员的初始化)。
可以看到,ngx_event_pipe方法在没有涉及缓存细节的情况下设计了转发响应的流程,它是通过调用ngx_event_pipe_read_upstream方法和ngx_event_pipe_write_to_downstream方法,以及检测它们的返回值来把握缓存响应的转发,再把事件与epoll和定时器关联起来的。下面我们将详细描述如何读取响应、如何分配内存缓冲区、如何通过写入临时文件释放缓冲区、如何通过向下游发送响应来更新缓冲区。