worker进程循环执行以下步骤,其中第5步是nginx真正处理http请求的过程
1、抢锁(非阻塞,没抢到锁就只处理旧连接上的http请求,不接收新连接)
- 抢到锁:
- 若上次抢到锁,意味着监听套接字已经在epoll中了,do nothing
- 若上次没抢到锁,意味着监听套接字不在epoll中,需要将监听套接字添加到epoll中
- 没抢到锁:
- 若上次抢到锁,意味着监听套接字还在epoll中,需要从epoll中删除监听套接字
- 若上次没抢到锁,意味着监听套接字已经不在epoll中了,do nothing
2、调用ngx_epoll_process_events()
- 调用epoll_wait(),把得到的读写事件分发到两个队列中
- 监听套接字(listen fd)上的读事件被添加到ngx_posted_accept_events队列中
- 已连接套接字(connected fd)上的读写事件被添加到ngx_posted_events队列中
3、处理ngx_posted_accept_events队列中的读事件
- 依次调用每个读事件的回调函数ngx_event_accept()
- ngx_event_accept():调用accept()得到已连接套接字,调用ngx_http_init_connection()
- ngx_http_init_connection():若已连接套接字上的读事件准备就绪,将该套接字上的读事件添加到ngx_posted_events队列中,否则,将已连接套接字上的读事件添加到epoll中
4、释放锁
- 锁一旦被释放,其它worker进程就可以抢锁了
5、处理ngx_posted_events队列中的读写事件
- 依次调用每个读写事件的回调函数,新连接上第一次读事件的回调函数为ngx_http_wait_request_handler()
- ngx_http_wait_request_handler():调用recv()接收数据,创建ngx_http_request_t结构体并初始化部分参数,修改读事件的回调函数为ngx_http_process_request_line()并调用该函数
- ngx_http_process_request_line():接收http请求行,修改读事件的回调函数为ngx_http_process_request_headers()并调用该函数
- ngx_http_process_request_headers():接收http请求头,调用ngx_http_process_request()
- ngx_http_process_request():处理http请求,修改读写事件的回调函数为ngx_http_request_handler()