继续上一章NGINX源码之:event与epoll的内容,在注册完accept后的连接socket fd对应的读事件后,后续由ngx_http_wait_request_handler()处理请求读事件:
首先先看下在ngx_event_accept()方法中给连接c绑定了接收和发送的处理器:
接下来进入正文:
这里有个NGINX请求丢失的问题可以思考下:NGINX维护了一个空闲连接队列,和一个可复用队列,当一个请求从空闲连接队列取出一个连接时,这个连接也会放入可复用连接队列,表示还没数据读之前,这个连接都能够被复用。
当空闲连接队列有总连接十六分之一个连接数可用的时候,新请求就从空闲连接队列拿连接,否则就要从可复用连接队列释放1-32个不等的连接放回到空闲连接队列。
但是,当一个请求被epoll监听到占用一个连接,并从流读数据并读完数据后,NGINX才将当前占用的连接从可复用连接队列剔除,说明在空闲连接队列不足的时候,有可能会将读有数据的连接还没完成数据处理响应给请求方就从可复用队列释放到空闲连接队列给其他的请求用,这样就导致后面的请求将前面的请求挤出,前面的请求会丢失,这在抢票这些类型的场景会有问题。那么这个问题是NGINX有意为之觉得请求丢失无所谓,还是有其他机制保证请求不丢失呢?
一、ngx_http_process_request_line与ngx_http_process_request_headers处理
先来看看请求内容:
GET /static/ HTTP/1.1
Host: 127.0.0.1:8083
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Tue, 06 Sep 2022 04:45:31 GMT
If-None-Match: \"6316d06b-d\"
Cache-Control: max-age=0
ngx_http_process_request_line中调用ngx_http_parse_request_line完成第一行“GET /static/ HTTP/1.1”的解析,接触处http method类型,请求uri,http请求版本等内容。
之后开始调用ngx_http_process_request_headers处理请求头:
ngx_http_process_request_headers中首先完成对请求头的解析
处理完请求头后,正式开始处理请求:ngx_http_process_request()–>ngx_http_handler()
接下来就进入ngx_http_core_run_phases()NGINX请求11个阶段处理
二、ngx_http_core_run_phases()
1、nginx11个阶段处理在之前的篇章中已经做过介绍NGINX源码之:phase与handlers
2、其中NGX_HTTP_FIND_CONFIG_PHASE中查找location相关内容在篇章NGINX源码之:location有更详细的介绍
3、除此之前,重写与认证校验相关的phase,这里就不详细解读了。
4、这里来重点看下NGX_HTTP_CONTENT_PHASE阶段:
此阶段默认主要涉及的handler有:ngx_http_index_handler、ngx_http_autoindex_handler、ngx_http_static_handler、另外还有ngx_http_proxy_handler等,ngx_http_proxy_handler主要设计upstream与代理方面的内容,打算后面单独一章解读。
4.1、ngx_http_index_handler
先来看下root和index命令的配置:
location /test/{
root html;
#alias /usr/data; #alias命令与root命令只能选其一,alias命令生成路径不拼接uri,而root命令则会拼接rui的路径。
index index.$geo.html index.0.html /index.html;
#Nginx按顺序遍历index配置的多个文件,使用第一个遇到的对应目录里有的文件;文件名可以使用$变量,绝对路径文件需放置最后
}
在ngx_http_core_root()解析root配置时,ngx_get_full_name()会判断配置的路径是否已 / 开头,若以/ 开头,则该路径是绝对路径直接使用该路径为根路径,当不以/ 开头且不是正则时,则该路径是相对路径,根路径=NGINX工作路径+相对路径;
工作路径来源如下:
那么按实例配置:root配置的 /test/ location匹配路径为:/usr/local/nginx/html/test/ , 若root配置为 /html,则匹配路径为 /html/test/;而alias配置的匹配路径直接为: /usr/data/
重定向后,最后会在ngx_http_static_handler中真正打开对应的静态文件读取,并响应到客户端。
4.2、ngx_http_autoindex_handler
这个handler主要的作用是将/ 结尾的请求,找到对应的目录,生成目录下的文件列表,将非/结尾的请求转至下一个handler。使用场景通常是用于静态文件列出提供浏览下载等。
其配置如下:表示index.html不存在时,nginx将会生成目录下文件列表返回至客户端。
location / {
index index.html;
autoindex on;
}
更多可参考Module ngx_http_autoindex_module
此处就不过多解读这一部分,有兴趣再另外补充
4.3、ngx_http_static_handler
实际上到这里,客户端就已经收到静态文件的响应了
其中ngx_open_cached_file()、ngx_http_send_header®、ngx_http_output_filter(r, &out);几个方法打算另立篇章解读
NGINX源码之:ngx_open_cached_file
NGINX源码之:filter机制。
三、收尾处理:ngx_http_finalize_request
这个方法中,我们先只关注正常处理完成的情况:
这里我目前只关心两个方法:
ngx_http_keepalive_handler
判断是否为Keep_alive的connection,设置rev->handler = ngx_http_keepalive_handler;
那么下次有请求的时候,不用创建新的连接socket,复用旧的连接socket,同时read事件的处理器不再为ngx_http_wait_request_handler,而是使用ngx_http_keepalive_handler
ngx_http_close_request():
通过ngx_http_free_request(),释放请求占用的资源,比如内存池
通过ngx_http_close_connection();将连接放回空闲连接队列,并关闭对应socket的文件描述符fd
关于ngx_http_finalize_request更详细的解读,后续有时间的话,我也打算另立篇章