java开发工程师,专注于内核源码,算法,数据结构。 qq:630501400
分类: C/C++
2013-06-26 17:51:44
nginx在接收到客户端得请求之后,就开始解析http请求,也就是解析http
header,需要分配一段buf来接收这些数据,nginx并不知道这个http
header的大小,在nginx配置中client_header_buffer_size和large_client_header_buffers这两个配置项起到了作用 。
函数ngx_http_alloc_large_header_buffer是扩充buf的,这里面涉及到free和busy数组的使用,这个和pipeline模式的请求有关。对于http
1.1而言,浏览器可以开启pipeline模式请求,对于一般的keepalive请求,浏览器对于每一个http请求都是通过一条tcp连接发送出去,在这个请求发送出去到接收到完整的http响应这段时间是肯定不会在发送http请求的。因为如果在这个过程中再次发送其他的http请求,那么由于网络延迟可能这两个请求的响应不是按照先后顺序到达浏览器的,所以这样的请求模型肯定是不行的。所以一般的http1.1的请求都是在一条tcp通道中,发送完一个请求,接收到响应,在发送第二个请求。这种发送模型的请求效率比较低。pipeline模式是一个http包中包含有多个http请求,一次就发送多个http报文,然后对于服务器来说依次处理这些请求,产生响应报文,一次再发送回客户端。这种模式增加了客户端和服务器之间效率,所以再pipeline的这种模式下,如果同一个http包中的多个http报文会共享那些分配的large
buf。对于第一个请求如果使用了large
buf,二个请求就会使用之前的那些large
buf,避免再次直接alloc内存,之前的那些large
buf如果不够用的时候再去alloc。
从上面的代码中可以看出来,当client_header_buffer_size不够用的时候会分配large
buf,然后把之前的buffer中的数据copy到large
buf中,无论是request
line还是某个header的长度都不会大于large
buf,总体的长度也会由于nbusy的限制不会超过large
buf size*num,如果超过了就会发送给客户端400错误,这个large
buf的空间的有可能是新分配的,也有可能是之前pipeline请求已经分配好的,这样对于pipeline请求来说,就极大的减少了重新分配large
buf的过程。
处理完http请求之后,需要finalize 请求的时候,就需要判断是否是pipeline请求,也就是那个buf的完整http报文后面是否还有其他的http报文,如果有的话就认为是pipeline请求,就回收那些已经分配的buf(busy数组)到free数组中,提供给下次请求使用。
下面的代码是最后ngx_http_set_keepalive函数,迁移buf的过程
上面的代码中,可以想到下一个请求就应该直接用这个busy[0]就可以了,不用重新分配那个client_header_buffer_size的buf了,因为重新分配的肯定丢掉了原来那个buf中的数据,所以在pipeline构建下一个request的时候,在初始化header_in的时候直接会用这个busy[0]。
在ngx_http_create_request函数中有下面的代码,就说明了如果是pipeline的请求就直接用busy[0]就可以了
r->header_in = hc->nbusy ?
hc->busy[0] : c->buffer;
总结:
nginx在处理request的时候,会预先分配一个client_header_buffer_size的buf,如果不够就会分配large_client_header_buffers的buf,对于request
line和每个header而言,每一个不应该超过large
buf,所有的总和也不应该超过large
buf size*num。Http
1.1的pipeline请求,如果前面的请求分配的large
buf,那么后面的请求会继承使用这个large
buf分配的空间,当large
buf 不够了再去主动分配large
buf。
client_header_buffer_size默认是1024字节。large_client_header_buffers默认最大分配4组8192字节的buf,每次分配一个buf。nginx处理http
header的过程是先处理request
line(http 请求的第一行),然后在处理每一个header,那么处理request
line的过程首先会分配client_header_buffer_size大小的空间,如果这个空间不够,那么再分配一个large_client_header_buffers的空间,然后把之前的client_header_buffer_size
copy到大buffer的前半部分中。如果在不够,nginx就会返回给客户端400的错误。每个header也是和如上的request
line一个处理步骤,所以对于request
line 和每个header的大小应该不超过1个large_client_header_buffers。对于整个request
line和所有header来讲,总大小不应该超过4*8192字节大小,否则也会产生400的错误。
在解析request
line的时候,回首先分配一个client_header_buffer_size来解析请求,当空间不足的时候,copy数据到第一个large_client_header_buffers中,如果这个buf仍然不能满足要求就返回400错误。函数ngx_http_process_request_line是解析request
line的,在解析buf中的数据后,会判断是否解析完request
line,如果buf的pos和end相等,就说明仍然未解析完成,需要再次从fd中读取数据,然后扩充buf到large
buf。
参考文章:
1.2.