nginx 事件驱动模型

文章目录

1、概述
2、nginx 高并发能力的原因
3、nginx 事件驱动模块代码介绍

1、概述

    本贴讲述 nginx 事件驱动模型工作原理及工作流程介绍。

2、nginx 高并发能力的原因

    首先,为什么 nginx 可以采用异步非阻塞的方式来处理呢,或者异步非阻塞到底是怎么回事呢?我们先回到原点,看看一个请求的完整过程。首先,请求过来,要建立连接,然后再接收数据,接收数据后,再发送数据。具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待, cpu 就会让出去给别人用了,对单线程的 worker 来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu 空闲下来没人用,cpu 利用率自然上不去了,更别谈高并发了。好吧,你说加进程数,这跟 apache 的线程模型有什么区别,注意,别增加无谓的上下文切换。所以,在 nginx 里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,马上返回 EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的。所以,才会有了异步非阻塞的事件处理机制,具体到系统调用就是像 select/poll/epoll/kqueue 这样的系统调用。它们提供了一种机制,让你可以同时监控多个事件,调用他们是阻塞的,但可以设置超时时间,在超时时间之内,如果有事件准备好了,就返回。这种机制正好解决了我们上面的两个问题,拿 epoll 为例(在后面的例子中,我们多以 epoll 为例子,以代表这一类函数),当事件没准备好时,放到 epoll 里面,事件准备好了,我们就去读写,当读写返回 EAGAIN 时,我们将它再次加入到 epoll 里面。这样,只要有事件准备好了,我们就去处理它,只有当所有事件都没准备好时,才在 epoll 里面等着。这样,我们就可以并发处理大量的并发了,当然,这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求当然只有一个了,只是在请求间进行不断地切换而已,切换也是因为异步事件未准备好,而主动让出的。这里的切换是没有任何代价,你可以理解为循环处理多个准备好的事件,事实上就是这样的。与多线程相比,这种事件处理方式是有很大的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费(上下文切换)。更多的并发数,只是会占用更多的内存而已。 我之前有对连接数进行过测试,在 24G 内存的机器上,处理的并发请求数达到过 200 万。现在的网络服务器基本都采用这种方式,这也是 nginx 性能高效的主要原因。下图是 nginx 时间驱动模型的示意图:

nginx 事件驱动模型_第1张图片


3、nginx 时间驱动模块代码介绍

(1)模块代码
nginx-1.9.12/src/event/modules/ngx_epoll_module.c。
(2)workprocess 进程调用事件驱动接口流程介绍
nginx-1.9.12/src/os/unix/ngx_process_cycle.c:358 -- ngx_process_events_and_timers(cycle),事件等待及处理函数调用;
nginx-1.9.12/src/event/ngx_event.c:194 -- ngx_process_events_and_timers(ngx_cycle_t *cycle);事件等待及处理函数;
nginx-1.9.12/src/event/ngx_event.h:400 -- #define ngx_process_events   ngx_event_actions.process_events,ngx_event_actions.process_events 对应 work_process 执行业务的入口,为结构体指针,其赋值在:
nginx-1.9.12/src/event/modules/ngx_epoll_module.c:359 -- ngx_event_actions = ngx_epoll_module_ctx.actions;
其最终执行的接口为:
nginx-1.9.12/src/event/modules/ngx_epoll_module.c:701:ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
nginx-1.9.12/src/event/modules/ngx_epoll_module.c:822 -- rev->handler(rev);执行读事件,该事件注册位置见下段。
注意:其中的 ngx_epoll_add_event 等接口对应不同的事件功能操作。
(3)workprocess 进程触发 http 请求的事件注册代码
nginx-1.9.12/src/http/ngx_http.c:1766:    ls->handler = ngx_http_init_connection,通过该行代码注册事件,然后在 nginx-1.9.12/src/event/modules/ngx_epoll_module.c:822 -- rev->handler(rev);触发时进入 http 业务处理流程。

你可能感兴趣的:(nginx 事件驱动模型)