Nginx 的 event(事件)处理机制是nginx的核心功能。nginx抽象了event机制,在多个平台有不同的event调用实现方法。比如说经常用的AIO(异步IO),/dev/poll(Solaris 和 Unix 特有),epoll(Linux 特有),kqueue(BSD 特有),poll,select 等。
event 模块的功能就是,监听网络事件和定时器事件。epoll模块对事件进行注册处理接口或者取消订阅关注这类事件。当事件网络IO 可读可写的时候,相应的读写事件handler就会被唤醒,此时就会去处理事件的回调函数。
对于 Linux,Nginx 大部分 event 采用 EPOLLET(边沿触发)的方法来触发事件,只有 listen 端口的读事件是 EPOLLLT(水平触发)。两者的区别是,如果边沿触发出现了可读事件,必须及时处理,否则可能会出现读事件再也不再触发被处理的情况。
cycle是全局变量,这个cycle是跟event模块挂钩的。全局只有一个,这个变量挂载着nginx所有的连接,read_event和write_event。
struct ngx_cycle_s {
/* 对于poll,rtsig这样的事件模块,会以有效文件句柄数来预先建立这些ngx_connection t结构
体,以加速事件的收集、分发。这时files就会保存所有ngx_connection_t的指针组成的数组,files_n就是指
针的总数,而文件句柄的值用来访问files数组成员 */
ngx_connection_t **files; //sizeof(ngx_connection_t *) * cycle->files_n
ngx_connection_t *free_connections;// 可用连接池,与free_connection_n配合使用
ngx_event_t *read_events;// 指向当前进程中的所有读事件对象,每个连接分别有一个读写事件
ngx_event_t *write_events;// 指向当前进程中的所有写事件对象,每个连接分别有一个读写事件
}
nginx event框架是和connection框架结合在一起的,一个客户端的connetion对应着一个read event和write event。event结构体把该连接对应的操作方法绑定在一起。每一个事件最核心的部分是handler回调方法,它将由每一个事件消费模块实现,以此决定这个事件究竟如何“消费”和data数据结构。data数据结构是handler的参数。一般就是一个connection结构体对象。
nginx不断陷入处理网络和定时器事件的死循环中。worker进程在循环中不断调用ngx_process_events_and _timers函数。
for ( ;; ) {
ngx_process_events_and_timers(cycle);
}
ngx_process_events_and_timers需要使用互斥锁解决多进程监听同一个端口(accept竞争,惊群问题)。
抢到了 accept 锁的进程跟一般进程稍微不同的是,它被加上了 NGX_POST_EVENTS 标志,也就是说在 ngx_process_events() 函数里面只接受而不处理事件,并accept到新的连接被加入 post_events 的队列里面。直到 ngx_accept_mutex 锁去掉以后才去处理具体的事件。
这里这样做是因为具体的client 连接比较耗时,相对比来说,accept新的连接这个事件更重要,并且 ngx_accept_mutex 是全局锁,在该进程accpet完之后,会尽早是否这个锁,让给别的进程accept。而本进程去处理之前accept到的连接,也就是说,可以尽量减少该进程抢到锁以后,从 accept 开始到结束的时间,以便其他进程继续接收新的连接,提高吞吐量。
什么情况需要event。当你有异步读写事件需要 event框架帮你托管(注册/通知事件)。写法如下:
生成一个ngx_event_s 结构体,实现自己的handler函数,和handler的对象data。这个data在nginx通常是ngx_connection_t。如果是操作自己定义的结构体,可以挂在ngx_connection_t->data下面。一个最简的ngixn_event_t结构需要定义handler和data。nginx_event_t用于触发的fd源自connetion的fd。
// 代表事件的结构体
struct ngx_event_s {
...
void * data;// 事件相关的对象, 通常指向ngx_connection_t连接对象
ngx_event_handler_pt handler; // 核心,事件消费函数,定义如何处理事件
...
};
ngx_connection_s需要承载上一步创建的event,这里要创建两个,分别挂载在read和write下面。这里需要注意,即使不需要某个事件,也要声明这个event结构体。只是把该event结构体的handler函数置为空。
struct
ngx_connection_s {
// 作为空闲连接时, 指向连接池中下一个空闲连接
// 作为非空闲连接时, 其意义由使用连接的模块定义, 例如http模块将data指向一个http请求(ngx_http_request_t)
void * data;
// 连接对应的读事件
ngx_event_t * read;
// 连接对应的写事件
ngx_event_t * write;
// 连接对应的侦听对象
ngx_listening_t * listening;
// 连接的套接字句柄
ngx_socket_t fd;
... };
connetionc有了read和write的nginx_event_t的结构体,就需要调用注册读写事件。
调用ngx_handle_read_event或者ngx_handle_write_event将event事件注册的全局的event框架中。
函数还可以有些flages可以设置NGX_CLOSE_EVENT, 默认一般是0。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。