12.4 发送请求到上游服务器

向上游服务器发送请求是一个阶段,因为请求的大小是未知的,所以发送请求的方法需要被epoll调度许多次后才可能发送完请求的全部内容。在图12-3中的第6步将ngx_http_upstream_t里的write_event_handler成员设为ngx_http_upstream_send_request_handler方法,也就是说,由该方法负责反复地发送请求,可是,在图12-3的第9步又直接调用了ngx_http_upstream_send_request方法发送请求,那这两种方法之间有什么关系吗?先来看看前者的实现,它相对简单,这里直接列举了它的主要源代码,如下所示。


static void ngx_http_upstream_send_request_handler(ngx_http_request_t*r,

ngx_http_upstream_t*u)

{

ngx_connection_t*c;

//获取与上游服务器间表示连接的ngx_connection_t结构体

c=u->peer.connection;

//写事件的timedout标志位为1时表示向上游服务器发送的请求已经超时

if(c->write->timedout){

/将超时错误传递给ngx_http_upstream_next方法,该方法将会根据允许的错误重连策略决定:重新发起连接执行upstream请求,或者结束upstream请求,详见12.9.2节/

ngx_http_upstream_next(r,u,NGX_HTTP_UPSTREAM_FT_TIMEOUT);

return;

}

/header_sent标志位为1时表明上游服务器的响应需要直接转发给客户端,而且此时Nginx已经把响应包头转发给客户端了/

if(u->header_sent){

/*事实上,header_sent为1时一定是已经解析完全部的上游响应包头,并且开始向下游发送HTTP的包头了。到此,是不应该继续向上游发送请求的,所以把write_event_handler设为任何工作都没有做的ngx_http_upstream_dummy_handler方法

u->write_event_handler=ngx_http_upstream_dummy_handler;

//将写事件添加到epoll中

(void)ngx_handle_write_event(c->write,0);

//因为不存在继续发送请求到上游的可能,所以直接返回

return;

}

//调用ngx_http_upstream_send_request方法向上游服务器发送请求

ngx_http_upstream_send_request(r,u);

}


可见,ngx_http_upstream_send_request_handler方法更多的时候是在检测请求的状态,而实际负责发送请求的方法是ngx_http_upstream_send_request,图12-4列出了ngx_http_upstream_send_request方法的主要执行步骤。

12.4 发送请求到上游服务器 - 图1

图 12-4 ngx_http_upstream_send_request方法的流程图

下面说明以上8个步骤的意义。

1)调用ngx_output_chain方法向上游服务器发送ngx_http_upstream_t结构体中的request_bufs链表,这个方法对于发送缓冲区构成的ngx_chain_t链表非常有用,它会把未发送完成的链表缓冲区保存下来,这样就不用每次调用时都携带上request_bufs链表。怎么理解呢?当第一次调用ngx_output_chain方法时,需要传递request_bufs链表构成的请求,如下所示。


rc=ngx_output_chain(&u->output,u->request_bufs);


这里的u就是请求对应的ngx_http_request_t结构体中的upstream成员(ngx_http_upstream_t类型),如果ngx_output_chain一次无法发送完所有的request_bufs请求内容,ngx_output_chain_ctx_t类型的u->output会把未发送完的请求保存在自己的成员中,同时返回NGX_AGAIN。当可写事件再次触发,发送请求时就不需要再传递参数了,例如:


rc=ngx_output_chain(&u->output,NULL);


为了标识这一点,ngx_http_upstream_t结构体中专门有一个标志位request_sent表示是否已经传递了request_bufs缓冲区。因此,在第一次以request_bufs作为参数调用ngx_output_chain方法后,request_sent会置为1。

2)检测写事件的timer_set标志位,timer_set为1时表示写事件仍然在定时器中,那么这一步首先把写事件由定时器中取出,再由ngx_output_chain的返回值决定是否再次向定时器中加入写事件,那时超时时间也会重置。

检测ngx_output_chain的返回值,返回NGX_AGAIN时表示还有请求未被发送,此时跳到第3步;如果返回NGX_OK,则表示已经发送完全部请求,跳到第5步执行。

3)调用ngx_add_timer方法将写事件添加到定时器中,防止发送请求超时。超时时间就是ngx_http_upstream_conf_t配置结构体的send_timeout成员。

4)调用ngx_handle_write_event方法将写事件添加到epoll中,ngx_http_upstream_send_request方法结束。

5)如果已经向上游服务器发送完全部请求,这时将准备开始处理响应,首先把读事件添加到定时器中检查接收响应是否超时,超时时间就是ngx_http_upstream_conf_t配置结构体的read_timeout成员。

检测读事件的ready标志位,如果ready为1,则表示已经有响应可以读出,这时跳到第6步执行;如果ready为0,则跳到第7步执行。

6)调用ngx_http_upstream_process_header方法接收上游服务器的响应,在12.5节中会详细讨论该方法。

7)如果暂无响应可读,由于此时请求已经全部发送到上游服务器了,所以要防止可写事件再次触发而又调用ngx_http_upstream_send_request方法。这时,把write_event_handler设为ngx_http_upstream_dummy_handler方法,前文说过,该方法不会做任何事情。这样即使与上游间的TCP连接上再次有可写事件时也不会有任何动作发生,它就像第11章我们介绍的ngx_http_empty_handler方法。

8)调用ngx_handle_write_event方法将写事件加入到epoll中。

在发送请求到上游服务器的这个阶段中,每当TCP连接上再次可以发送字符流时,虽然事件框架就会回调ngx_http_upstream_send_request_handler方法处理可写事件,但最终还是通过调用ngx_http_upstream_send_request方法把请求发送出去的。