12.1.2 ngx_http_upstream_t数据结构的意义

使用upstream机制时必须构造ngx_http_upstream_t结构体,下面详述其中每个成员的意义。


typedef struct ngx_http_upstream_s ngx_http_upstream_t;

struct ngx_http_upstream_s{

//处理读事件的回调方法,每一个阶段都有不同的read_event_handler

ngx_http_upstream_handler_pt read_event_handler;

//处理写事件的回调方法,每一个阶段都有不同的write_event_handler

ngx_http_upstream_handler_pt write_event_handler;

/表示主动向上游服务器发起的连接。关于ngx_peer_connection_t结构体,可参见9.3.2节/

ngx_peer_connection_t peer;

//当向下游客户端转发响应时(ngx_http_request_t结构体中的subrequest_in_memory标志位为0),如果打开了缓存且认为上游网速更快(conf配置中的buffering标志位为1),这时会使用pipe成员来转发响应。在使用这种方式转发响应时,必须由HTTP模块在使用upstream机制前构造pipe结构体,否则会出现严重的coredump错误。详见12.8.1节*/

ngx_event_pipe_t*pipe;

//定义了向下游发送响应的方式

ngx_output_chain_ctx_t output;

ngx_chain_writer_ctx_t writer;

//使用upstream机制时的各种配置,详见12.1.3节

ngx_http_upstream_conf_t*conf;

/HTTP模块在实现process_header方法时,如果希望upstream直接转发响应,就需要把解析出的响应头部适配为HTTP的响应头部,同时需要把包头中的信息设置到headers_in结构体中,这样,在图12-5的第8步中,会把headers_in中设置的头部添加到要发送到下游客户端的响应头部headers_out中/

ngx_http_upstream_headers_in_t headers_in;

//用于解析主机域名,本章不作介绍

ngx_http_upstream_resolved_t*resolved;

/接收上游服务器响应包头的缓冲区,在不需要把响应直接转发给客户端,或者buffering标志位为0的情况下转发包体时,接收包体的缓冲区仍然使用buffer。注意,如果没有自定义input_filter方法处理包体,将会使用buffer存储全部的包体,这时buffer必须足够大!它的大小由ngx_http_upstream_conf_t配置结构体中的buffer_size成员决定/

ngx_buf_t buffer;

//表示来自上游服务器的响应包体的长度

size_t length;

/out_bufs在两种场景下有不同的意义:①当不需要转发包体,且使用默认的input_filter方法(也就是ngx_http_upstream_non_buffered_filter方法)处理包体时,out_bufs将会指向响应包体,事实上,out_bufs链表中会产生多个ngx_buf_t缓冲区,每个缓冲区都指向buffer缓存中的一部分,而这里的一部分就是每次调用recv方法接收到的一段TCP流。②当需要转发响应包体到下游时(buffering标志位为0,即以下游网速优先,参见12.7节),这个链表指向上一次向下游转发响应到现在这段时间内接收自上游的缓存响应/

ngx_chain_t*out_bufs;

/当需要转发响应包体到下游时(buffering标志位为0,即以下游网速优先,参见12.7节),它表示上一次向下游转发响应时没有发送完的内容/

ngx_chain_t*busy_bufs;

/这个链表将用于回收out_bufs中已经发送给下游的ngx_buf_t结构体,这同样应用在buffering标志位为0即以下游网速优先的场景/

ngx_chain_t*free_bufs;

/处理包体前的初始化方法,其中data参数用于传递用户数据结构,它实际上就是下面的input_filter_ctx指针/

ngx_int_t(input_filter_init)(voiddata);

/处理包体的方法,其中data参数用于传递用户数据结构,它实际上就是下面的input_filter_ctx指针,而bytes表示本次接收到的包体长度。返回NGX_ERROR时表示处理包体错误,请求需要结束,否则都将继续upstream流程/

ngx_int_t(input_filter)(voiddata,ssize_t bytes);

/用于传递HTTP模块自定义的数据结构,在input_filter_init和input_filter方法被回调时会作为参数传递过去/

void*input_filter_ctx;

//HTTP模块实现的create_request方法用于构造发往上游服务器的请求

ngx_int_t(create_request)(ngx_http_request_tr);

/与上游服务器的通信失败后,如果按照重试规则还需要再次向上游服务器发起连接,则会调用reinit_request方法/

ngx_int_t(reinit_request)(ngx_http_request_tr);

/解析上游服务器返回响应的包头,返回NGX_AGAIN表示包头还没有接收完整,返回NGX_HTTP_UPSTREAM_INVALID_HEADER表示包头不合法,返回NGX_ERROR表示出现错误,返回NGX_OK表示解析到完整的包头/

ngx_int_t(process_header)(ngx_http_request_tr);

//当前版本下abort_request回调方法没有任意意义,在upstream的所有流程中都不会调用*/

void(abort_request)(ngx_http_request_tr);

//请求结束时会调用,参见12.9.1节

void(finalize_request)(ngx_http_request_tr,

ngx_int_t rc);

/在上游返回的响应出现Location或者Refresh头部表示重定向时,会通过ngx_http_upstream_process_headers方法(参见图12-5中的第8步)调用到可由HTTP模块实现的rewrite_redirect方法/

ngx_int_t(rewrite_redirect)(ngx_http_request_tr,

ngx_table_elt_t*h,size_t prefix);

//暂无意义

ngx_msec_t timeout;

//用于表示上游响应的错误码、包体长度等信息

ngx_http_upstream_state_t*state;

//不使用文件缓存时没有意义

ngx_str_t method;

//schema和uri成员仅在记录日志时会用到,除此以外没有意义

ngx_str_t schema;

ngx_str_t uri;

/目前它仅用于表示是否需要清理资源,相当于一个标志位,实际不会调用到它所指向的方法/

ngx_http_cleanup_pt*cleanup;

//是否指定文件缓存路径的标志位,本章不讨论文件缓存,略过

unsigned store:1;

//是否启用文件缓存,本章仅讨论cacheable标志位为0的场景

unsigned cacheable:1;

//暂无意义

unsigned accel:1;

//是否基于SSL协议访问上游服务器

unsigned ssl:1;

/向下游转发上游的响应包体时,是否开启更大的内存及临时磁盘文件用于缓存来不及发送到下游的响应包体/

unsigned buffering:1;

/request_bufs以链表的方式把ngx_buf_t缓冲区链接起来,它表示所有需要发送到上游服务器的请求内容。所以,HTTP模块实现的create_request回调方法就在于构造request_bufs链表/

ngx_chain_t*request_bufs;

/request_sent表示是否已经向上游服务器发送了请求,当request_sent为1时,表示upstream机制已经向上游服务器发送了全部或者部分的请求。事实上,这个标志位更多的是为了使用ngx_output_chain方法发送请求,因为该方法发送请求时会自动把未发送完的request_bufs链表记录下来,为了防止反复发送重复请求,必须有request_sent标志位记录是否调用过ngx_output_chain方法/

unsigned request_sent:1;

/将上游服务器的响应划分为包头和包尾,如果把响应直接转发给客户端,header_sent标志位表示包头是否发送,header_sent为1时表示已经把包头转发给客户端了。如果不转发响应到客户端,则header_sent没有意义/

unsigned header_sent:1;

};


到目前为止,ngx_http_upstream_t结构体中有些成员仍然没有使用到,还有更多的成员其实仅是HTTP框架自己使用,HTTP模块在使用upstream时需要设置的成员并不是太多,但在实现process_header、input_filter等回调方法时,还是需要对各个成员有一个初步的了解,这样才能高效地使用upstream机制。