12.8.2 转发响应的包头

开始转发响应也是通过ngx_http_upstream_send_response方法执行的。图12-9展示了转发响应包头和初始化ngx_event_pipe_t结构体的流程。

下面说明一下图12-9中的步骤。

12.8.2 转发响应的包头 - 图1

图 12-9 buffering标志位为0时转发响应包头的流程图

1)首先调用ngx_http_send_header方法向下游客户端发送ngx_http_request_t结构体的headers_out中设置过的HTTP响应包头。

2)如果客户端请求中存在HTTP包体,而且包体已经保存到临时文件中了,这时将会调用ngx_pool_run_cleanup_file方法清理临时文件,以释放不必要的资源。这里的第1步和第2步与图12-8中的完全一样,不再详述。

3)ngx_http_upstream_t结构体中的pipe成员并不是在这一步中创建,它仅在这一步中初始化部分成员,因此,一旦pipe到这一步还没有创建,就会出现内存访问越界,引发严重错误。ngx_event_pipe_t结构体的初始化大概包括以下部分:


//注意,这里是直接引用必须分配过内存的pipe指针

ngx_event_pipe_t*p=u->pipe;

/设置向下游客户端发送响应的方法为ngx_http_output_filter,该方法在第11章中介绍过/

p->output_filter=(ngx_event_pipe_output_filter_pt)ngx_http_output_filter;

/output_ctx指向当前请求的ngx_http_request_t结构体,这是因为接下来转发包体的方法都只接受ngx_event_pipe_t参数,且只能由output_ctx成员获取到表示请求的ngx_http_request_t结构体/

p->output_ctx=r;

//设置转发响应时启用的每个缓冲区的tag标志位

p->tag=u->output.tag;

//bufs指定了内存缓冲区的限制

p->bufs=u->conf->bufs;

//设置busy缓冲区中待发送的响应长度触发值

p->busy_size=u->conf->busy_buffers_size;



//upstream在这里被初始化为Nginx与上游服务器之间的连接

p->upstream=u->peer.connection;

//downstream在这里被初始化为Nginx与下游客户端之间的连接

p->downstream=c;

//初始化用于分配内存缓冲区的内存池

p->pool=r->pool;

//初始化记录日志的log成员

p->log=c->log;

//设置临时存放上游响应的单个缓存文件的最大长度

p->max_temp_file_size=u->conf->max_temp_file_size;

//设置一次写入文件时写入的最大长度

p->temp_file_write_size=u->conf->temp_file_write_size;

//以当前location下的配置来设置读取上游响应的超时时间

p->read_timeout=u->conf->read_timeout;

//以当前location下的配置来设置发送到下游的超时时间

p->send_timeout=clcf->send_timeout;

//设置向客户端发送响应时TCP中的send_lowat“水位”

p->send_lowat=clcf->send_lowat;


4)初始化preread_bufs预读缓冲区链表(所谓预读,就是在读取包头时也预先读取到了部分包体),注意,该链表中的缓冲区都是不会分配内存来存放上游响应内容的,而仅使用ngx_buf_t结构体指向实际的存放响应包体的内存。如何初始化preread_bufs呢?如下所示。


p->preread_bufs->buf=&u->buffer;

p->preread_bufs->next=NULL;

p->preread_size=u->buffer.last-u->buffer.pos;


实际上就是把preread_bufs中的缓冲区指向存放头部的buffer缓冲区,在图12-11中的第1步会介绍它的用法。

5)设置处理上游读事件回调方法为ngx_http_upstream_process_upstream。

6)设置处理下游写事件的回调方法为ngx_http_upstream_process_downstream。

7)调用ngx_http_upstream_process_upstream方法处理上游发来的响应包体。

ngx_event_pipe_t结构体是打开缓存转发响应的关键,下面的章节中我们会一直与它“打交道”。