12.5.2 处理包体的3种方式

为什么upstream机制不是仅仅负责接收上游服务器发来的包体,再交由HTTP模块决定如何处理这个包体呢?这是因为upstream有一个最重要的使命要完成!Nginx作为一个试图取代Apache的Web服务器,最基本的反向代理功能是必须存在的,而实现反向代理的Web服务器并不仅仅希望可以访问上游服务器,它更希望upstream能够实现透传、转发上游响应的功能。

upstream机制不关心如何构造发送到上游的请求内容,这事实上是由各个使用upstream的HTTP模块实现的create_request方法决定的(目前的HTTP反向代理模块是这么做的:Nginx将客户端的请求全部接收后再透传给上游服务器,这种方式很简单,又对减轻上游服务器的并发负载很有帮助),但对响应的处理就比较复杂了,下面举两个例子来说明其复杂性。

如果Nginx与上游服务器间的网速很快(例如,两者都在一个机房的内网中,或者两者间拥有专线),而Nginx与下游的客户端间网速又很慢(例如,下游客户端通过公网访问机房内的Nginx),这样就会导致Nginx接收上游服务器的响应非常快,而向下游客户端转发响应时很慢,这也就为upstream机制带来一个需求:应当尽可能地把上游服务器的响应接收到Nginx服务器上,包括将来自上游的、还没来及发送到下游的包体缓存到内存中,如果使用的内存过大,达到某个限制阈值后,为了降低内存的消耗,还需要把包体缓存到磁盘文件中。

如果Nginx与上游服务器间的网速较慢(假设是公网线路),而Nginx与下游的客户端间的网速很快(例如,客户端其实是Nginx所在机房里的另一个Web服务器),这时就不存在大量缓存上游响应的需求了,完全可以开辟一块固定大小的内存作为缓冲区,一边接收上游响应,一边向下游转发。每当向下游成功转发部分响应后就可以复用缓冲区,这样既不会消耗大量内存(增加Nginx并发量),又不会使用到磁盘I/O(减少了用户等待响应的时间)。

因此,upstream机制提供了3种处理包体的方式:不转发响应(即不实现反向代理)、转发响应时以下游网速优先、转发响应时以上游网速优先。怎样告诉upstream机制使用哪种方式处理上游的响应包体呢?当请求ngx_http_request_t结构体的subrequest_in_memory标志位为1时,将采用第1种方式,即不转发响应;当subrequest_in_memory为0时,将转发响应。而ngx_http_upstream_conf_t配置结构体中的buffering标志位,会决定转发响应时是否开启更多的内存和磁盘文件用于缓存上游响应,如果buffering为0,则以下游网速优先,使用固定大小的内存作为缓存;如果buffering为1,则以上游网速优先,使用更多的内存、硬盘文件作为缓存。

1.不转发响应

不转发包体是upstream机制最基本的功能,特别是客户端请求派生出的子请求多半不需要转发包体,upstream机制的最低目标就是允许HTTP模块以TCP访问上游服务器,这时HTTP模块仅希望解析包头、包体,没有转发上游响应的需求。upstream机制提供的解析包头的回调方法是process_header,而解析包体的回调方法则是input_filter。在12.6节将会描述这种处理包体的最基本方式是如何工作的。

2.转发响应时下游网速优先

在转发响应时,如果下游网速快于上游网速,或者它们速度相差不大,这时不需要开辟大块内存或者磁盘文件来缓存上游的响应。我们将在12.7节中讲述这种处理方式下upstream机制是如何工作的。

3.转发响应时上游网速优先

在转发响应时,如果上游网速快于下游网速(由于Nginx支持高并发特性,所以大多数时候都用于做最前端的Web服务器,这时上游网速都会快于下游网速),这时需要开辟内存或者磁盘文件缓存来自上游服务器的响应,注意,缓存可能会非常大。这种处理方式比较复杂,在12.8节中我们会详细描述其主要流程。