通过我们知道ngx_event_core_module模块的init_process函数ngx_event_process_init()会为每个监听套接字的读事件注册处理函数ngx_event_accept(TCP)或者ngx_event_recvmsg(UDP),这里我们就来讲述一下nginx event acceptde的相关实现。
由于Nginx工作在master-worker多进程模式,若所有worker进程在同一时间监听同一个端口,当该端口有新的连接事件出现时,每个worker进程都会调用函数ngx_event_accep()试图与新的连接建立通信,即所有worker进程都会被唤醒,这就是所谓的惊群
问题,这样会导致系统性能下降。
幸好在Nginx中采用了ngx_accept_mutex
同步锁机制,即只有获得该锁的worker进程才能去处理新的连接事件,也就在同一时间只能有一个worker进程监听某个端口。虽然这样做解决了惊群
问题,但是随之会出现另一个问题,若每次出现的新连接都被同一个worker进程获得锁的权利并处理该连接事件,这样会导致进程之间出现不均衡的状态,即在所有worker进程中,某些进程处理的连接事件数量庞大,而某些进程基本上不用处理连接事件,一直处于空闲状态。因此,这样会导致worker进程之间的负载不均衡,会影响nginx的整体性能。为了解决负载失衡的问题,Nginx在已经实现同步锁的基础上定义了负载阈值ngx_accept_disabled
,当某个worker进程的负载阈值大于0时,表示该进程处于负载超重的状态,则Nginx会控制该进程,使其没机会试图与新的连接事件进行通信,这样就会为其他没有负载超重的进程创造处理新连接事件的机会,以此达到进程间的负载均衡。
1. 相关静态函数声明
1. 函数ngx_event_accept()
本函数作为基于TCPlistenning
connection读事件的处理函数,用于接受来自客户端的新连接。下面我们简要分析一下本函数:
3. 函数ngx_event_recvmsg()
本函数作为基于UDPlistenning
connection读事件的处理函数,用于接受来自客户端的UDP连接。下面我们简要分析一下本函数:
4. 函数ngx_trylock_accept_mutex()
本函数用于尝试获取accept_mutex
锁。下面详细分析一下本函数的实现:
5. 函数ngx_enable_accept_events()
此函数较为简单,把监听socket加入到对应的事件驱动机制中
6. 函数ngx_disable_accept_events()
本函数用于从事件驱动机制
中移除监听socket的读事件。这里注意,对于ls[i].reuseport
,表示复用端口,此时该socket属于worker进程,因此并不会从事件驱动机制中移除。
7. 函数ngx_close_accepted_connection()
本函数用于关闭accept到的连接对象,并将ngx_connection_t连接对象放回到ngx_cycle->free_connections中。注意,如果该connection所对应的socket fd在多个线程或进程之间共享,那么这里并不真正关闭fd。
8. 函数ngx_accept_log_error()
本函数用于格式化accept日志输出。
9. 函数ngx_debug_accepted_connection()
nginx在accept到一个新的连接之后,会将该新连接的远程地址与nginx配置指令debug_connection
所配置的地址向比较,如果相等,那么调制该连接的日志打印级别,从而实现对某些客户端连接的打印。
[参看]
-
Nginx监听套接字读事件处理函数ngx_event_accept
-
Nginx 事件驱动模块连接处理
-
从EMFILE和ENFILE说起,fd limit的问题(一)