1. ngx_listening_t与ngx_connection_t
ngx_listening_t保存监听配置信息,如地址、端口等。Init_cycle.listening[]数组保存了所有的ngx_listening_t实例。
ngx_connection_t保存连接,如socket句柄等。Init_cycle.connections[]数组保存所有的ngx_connection_t实例,init_cycle.free_connections[]数组保存其中空闲的ngx_connection_t实例。init_cycle.connections[]的大小由init_cycle.connection_n指定,这个值缺省为512,也可以在配置文件中改变。
ngx_connection_t使用ngx_event_t保存读写事件的配置,如事件处理函数。
2. ngx_http_optimize_servers()
如前所述,http{}节点的ngx_http_main_conf_t.ports[]数组指向server{}的端口数组。nginx服务器绑定这些端口,监听连接请求。
ngx_http_optimize_servers() 根据从这些端口创建ngx_listening_t实例。
- 遍历ngx_http_core_main_conf_t.ports[],调用ngx_http_init_listening()。
3. ngx_http_init_listening()
ngx_http_init_listening() 从ngx_http_core_main_conf_t.ports[]创建ngx_listenging_t实例,包括从ip地址到ngx_http_core_srv_conf的映射关系。
在ngx_http_init_listening()中,遍历ngx_http_core_main_conf_t.ports.addrs[],
-
调用ngx_http_add_listening()。在ngx_http_add_listening()中,
- 调用ngx_create_listening()。其中调用ngx_array_push()从init_cycle.listening[]数组中得到一个空闲的ngx_listening_t,然后初始化。
- 将新的ngx_listening_t的请求处理函数设置为ngx_http_init_connection()。当客户端的连接请求到达时,读请求事件ngx_event_t.handler()被调用,这个handler()又调用ngx_http_init_connection()。
给ngx_listeing_t.servers[]分配空间,这是一个ngx_http_port_t结构。
调用ngx_http_add_addrs(),将addrs[]中的元素,加入ngx_http_port_t.addrs[]。这是一个ngx_http_in_addr_t数组。这里将ngx_http_in_addr_t.conf.default_server指向了ngx_http_core_main_conf_t.ports.addrs[].default_server。
4. ngx_open_listening_sockets()
ngx_open_listening_sockets()遍历init_cycle.listening[]数组,创建socket,绑定到指定的地址上,并开始监听。
5. ngx_configure_listening_sockets()
ngx_configure_listening_sockets()遍历init_cycle.listening[],对之前创建的socket继续进行选项配置。
6. ngx_event_process_init()
ngx_event_process_init()前面已经列出。
这里进一步详细说明的是,
调用ngx_palloc()给init_cycle.connections[]数组分配空间。
调用ngx_palloc()为init_cycle.connections[]中的每个ngx_connection_t实例分配两个ngx_event_t,一个用于监听读、一个用于监听写。
遍历init_cycle.connections[],将它与相应的两个ngx_event_t实例绑定到ngx_connection_t.read和ngx_connection_write。
当前init_cycle.connections[]中的ngx_connection_t实例都还没使用,所以将它链入init_cycle.free_connections。
-
遍历init_cycle.listening[],调用ngx_get_connection(),为每个ngx_listening_t实例绑定一个ngx_connection_t实例,开始监听它。
- 这里将ngx_connetion_t.read的处理函数指定为ngx_event_accept(),然后调用ngx_add_event()将ngx_connection_t.read加入监听列表。
7. ngx_process_events()
ngx_process_events()前面已经列出。
这里进一步说明的是,调用epoll_wait() 得到的事件epoll_event,与ngx_connetion_t实例相关联。这里遍历event_list[],对其中的epoll_event,
- 得到关联的ngx_connection_t实例,根据事件类型,得到读/写的ngx_event_t实例。调用ngx_event_t::handler()。对于服务器监听端口的ngx_connection_t实例,是ngx_event_accept()。
8. ngx_event_accept()
服务器监听端口有连接请求到达时,调用ngx_event_accept()。
- 得到与ngx_event_t关联的ngx_listening_t实例。
- 调用accept()得到新的连接Socket句柄。
- 调用ngx_get_connection(),得到可用的ngx_connection_t实例,绑定socket句柄,并与ngx_listening_t实例绑定。
- 调用ngx_listening_t::handler(),这里是ngx_http_init_connection()。
9. 回顾ngx_http_add_listen()
在继续说明ngx_http_init_connection()之前,先回顾一下ngx_http_add_listen()。
创建server{}的配置数据结构时,调用ngx_http_core_listen()。得到server{}中的绑定地址后,调用ngx_http_add_listen() 加入ngx_http_core_main_conf.ports[]中。
这里涉及的数据结构如下。
- ngx_http_listen_opt_t保存地址信息,它的成员sockaddr包括协议族、端口和IP地址。
- ngx_http_conf_addr_t保存ngx_http_listen_opt_t,和对应的ngx_http_core_srv_conf_t实例,包括成员servers[]数组,和一个缺省的default_server。
- ngx_http_conf_port_t保存一组端口相同的地址信息。它的成员family是协议族,成员port是端口值,成员addrs[]则是一个ngx_http_conf_addr_t的数组。
- ngx_http_core_main_conf_t的成员ports[]保存一组不同的端口信息,也就是ngx_http_conf_port_t。
10. 回顾ngx_http_init_listening()
在继续说明ngx_http_init_connection()之前,先回顾一下ngx_http_init_listening()。
ngx_http_init_listening() 从ngx_http_core_main_conf_t.ports[]创建ngx_listenging_t实例,包括从ip地址到ngx_http_core_srv_conf的映射关系。
这里涉及的数据结构如下。
- ngx_http_in_addr_t的成员addr保存地址,成员conf是ngx_http_addr_conf_t,它的成员default_server,又指向ngx_http_core_srv_conf_t实例。
- ngx_http_port_t保存一组ngx_http_in_addr_t。ngx_listening_t又保存一组ngx_http_port_t。
所以,遍历ngx_listengint_t.servers[],可以根据地址找到对应的ngx_http_core_srv_conf_t实例。
11. ngx_http_init_connection()
ngx_http_init_connection()初始化新的ngx_connection_t实例。
- 分配ngx_http_connection_t实例,并与ngx_connection_t实例关联。
- 如前所述,在ngx_http_core_main_conf_t.listening[].servers[]中,保存了地址和对应的ngx_http_core_srv_conf_t实例。遍历这个数组,找到与当前ngx_connection_t对应的ngx_http_in_addr_t实例。将ngx_http_connection_t.addr_conf指向ngx_http_core_main_conf中的相应值。
hc->addr_conf = &addr[i].conf;
hc->conf_ctx = hc->addr_conf->default_server->ctx;
- 设置ngx_connection_t的用于读的ngx_event_t实例。设置其ngx_event_::handler()为ngx_http_wait_request_handler()。
- 调用ngx_handle_read_event(),其中调用ngx_add_event(),开始监听ngx_connection_t的读事件。
12. ngx_process_events()
再一次回到ngx_process_events()。这一次当数据连接socket有数据可读时,调用ngx_event_t::handler(),这里是ngx_http_wait_request_handler()。ngx_http_wait_request_handler()就开始处理http请求了。
13. ngx_http_wait_request_handler()
当http请求数据到达时,调用ngx_http_wait_request_handler()。它接收请求数据并处理。
- 调用ngx_create_temp_buf()分配空间,作为接收数据的缓存。
- 调用ngx_connect_t::recv()接收数据。这里是ngx_unix_recv()。Ngx_unix_recv()调用recv()。
- 调用ngx_http_create_request(),创建ngx_http_request_t实例。
- 调用ngx_http_process_request_line()开始数据处理。
14. ngx_http_create_request()
ngx_http_create_request() 创建ngx_http_request_t实例。它调用ngx_http_alloc_request()分配ngx_http_request_t实例。其中设置ngx_http_request_t.main_conf、ngx_http_request_t.srv_conf、ngx_http_request_t.loc_conf。这是后面的ngx_http_request_t的上下文。
15. ngx_http_request_t
ngx_http_request_t是http请求处理过程中使用的结构。解析请求字符串得到的信息保存在这里。
如下是一个请求字符串的例子。
- 第一行GET /index.html HTTP/1.1是请求的地址,包括
- method: GET。保存在ngx_http_request_t.method.
- uri: /index.html。保存在ngx_http_request.uri_start
- protocol: HTTP。保存在ngx_http_request.http_protocol
- http version: 1.1。保存在ngx_http_request.http_major和ngx_http_request_.http_minor。
这行调用ngx_http_parse_request_line()解析。
后续行为header行,每一行是一个header,包括名字和值。这里的header名字有User-Agent、Accept、Host和Connection。保存在ngx_http_request.headers_in.headers。Header行调用ngx_http_parse_header_line()解析。
16. ngx_http_headers_in
全局数组ngx_http_headers_in[]中,保存与header行对应的处理函数。如下面的Host、User-Agent等。
ngx_http_header_t ngx_http_headers_in[] = {
{ ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
ngx_http_process_host },
{ ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
ngx_http_process_user_agent },
......
};
17. ngx_http_init_headers_in_hash()
在ngx_http_init_headers_in_hash()中,
- 遍历ngx_http_headers_in[],对其中的ngx_http_header_t实例,
- 创建ngx_hash_key_t,保存到本地数组headers_in中。成员key设置为ngx_http_header_t.name;根据key计算成员key_hash;设置成员value指向ngx_http_header_t
ngx_array_t headers_in;
- 将本地变量hash关联到headers_in。并用它初始化ngx_http_core_main_conf_t.headers_in_hash,这是一个ngx_hash_t结构,也就是hash树。
ngx_hash_init_t hash;
所以最后的结果就是,全局数组ngx_http_headers_in[]的元素保存在ngx_http_core_main_conf_t.headers_in_hash中,访问速度变快了。
18. ngx_http_process_request_line ()
ngx_http_process_request_line()负责处理请求字符串。
在一个循环中,执行如下步骤:
- 调用ngx_http_read_request_header(),读取更多数据。
- 调用ngx_http_parse_request_line(),分解请求字符串各部分,并保存在ngx_http_request_t实例中。
- 调用ngx_http_process_request_headers(),继续处理http请求。
19. ngx_http_process_request_headers()
ngx_http_process_request_headers() 继续处理http请求。
- 在一个循环中,调用ngx_http_parse_header_line()解析header字符串,得到所有header,保存在ngx_http_request_t.headers_in。至此完成http请求字符串的解析。
- 调用ngx_hash_find(),根据http请求的名字,从ngx_http_core_main_conf_t.headers_in_hash,查询对应的ngx_http_header_t实例。前面已经说过,这里的headers_in_hash,来自全局变量ngx_http_headers_in[]。
- 然后调用处理函数ngx_http_header_t.handler()。如header名字为User-Agent,对应的处理函数是ngx_http_process_user_agent()。
- 调用ngx_http_process_request(),正式处理http请求。
20. ngx_http_process_request()
ngx_http_process_request() 处理http请求。它调用ngx_http_handler(),后者又调用ngx_http_core_run_phases()。
nginx将数据处理过程划分为几个阶段(phase),每个阶段可以挂接若干处理函数,ngx_http_core_run_phase() 按阶段调用这些函数。Nginx自身的nginx_module,用户自己的ngx_module,都可以挂接自己的处理函数。
相关链接
nginx 1.18 源代码分析 (一)
nginx 1.18 源代码分析 (二)
nginx 1.18 源代码分析 (三)
nginx 1.18 源代码分析 (四)
nginx 1.18 源代码分析 (五)
nginx 1.18 源代码分析 (六)