12.8.2 转发响应的包头
开始转发响应也是通过ngx_http_upstream_send_response方法执行的。图12-9展示了转发响应包头和初始化ngx_event_pipe_t结构体的流程。
下面说明一下图12-9中的步骤。
图 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结构体是打开缓存转发响应的关键,下面的章节中我们会一直与它“打交道”。