您的位置:首页 > 运维架构 > Nginx

nginx架构初探

2016-01-11 14:11 627 查看
注:个人学习的一些知识点笔记

参考:《Nginx开发从入门到精通》,《深入理解Nginx》

1.nginx可以后台运行,也可以前台运行(一般用于调试);

2.nginx支持多线程,只是我们主流的方式还是多进程的方式,也是nginx的默认方式;

3.nginx在启动后,会有一个master进程和多个worker进程。

master进程主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。

而基本的网络事件,则是放在worker进程中来处理了。

多个worker进程之间是对等的,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理。

worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。

nginx的进程模型,可以由下图来表示:



4.当一个http连接请求过来,每个进程都有可能处理这个连接。首先,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。

我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

5.异步非阻塞:当事件没准备好时,放到epoll里面,事件准备好了,我们就去读写,当读写返回EAGAIN时,我们将它再次加入到epoll里面。这样,只要有事件准备好了,我们就去处理它,只有当所有事件都没准备好时,才在epoll里面等着。这样,我们就可以并发处理大量的并发了,当然,这里的并发请求,是指未处理完的请求,线程只有一个,所以同时能处理的请求当然只有一个了,只是在请求间进行不断地切换而已,切换也是因为异步事件未准备好,而主动让出的。这里的切换是没有任何代价,可以理解为循环处理多个准备好的事件。与多线程相比,这种事件处理方式是有很大的优势的,不需要创建线程,每个请求占用的内存也很少,没有上下文切换,事件处理非常的轻量级。并发数再多也不会导致无谓的资源浪费(上下文切换)。更多的并发数,只是会占用更多的内存而已。现在的网络服务器基本都采用这种方式,这也是nginx性能高效的主要原因。

6.事件通常有三种类型,网络事件、信号、定时器。

网络事件:通过异步非阻塞解决;

信号:如果nginx正在等待事件(epoll_wait时),如果程序收到信号,在信号处理函数处理完后,epoll_wait会返回错误,然后程序可再次进入epoll_wait调用。

定时器:nginx的定时器事件是放在一棵维护定时器的红黑树里面,每次在进入epoll_wait前,先从该红黑树里面拿到所有定时器事件的最小时间,在计算出epoll_wait的超时时间后进入epoll_wait。所以,当没有事件产生,也没有中断信号时,epoll_wait会超时。这时,nginx会检查所有的超时事件,将他们的状态设置为超时,然后再去处理网络事件。由此可以看出,当我们写nginx代码时,在处理网络事件的回调函数时,通常做的第一个事情就是判断超时,然后再去处理网络事件。

nginx的事件处理模型总结:

[code]while (true) {
    for t in run_tasks:
        t.handler();
    update_time(&now);
    timeout = ETERNITY;
    for t in wait_tasks: /* sorted already */
        if (t.time <= now) {
            t.timeout_handler();
        } else {
            timeout = t.time - now;
            break;
        }
    nevents = poll_function(events, timeout);
    for i in nevents:
        task t;
        if (events[i].type == READ) {
            t.handler = read_handler;
        } else { /* events[i].type == WRITE */
            t.handler = write_handler;
        }
        run_tasks_add(t);
}


7.如上,nginx是一个事件驱动架构的web服务器;它将一个网络事件划分为多个阶段,比如 请求建立TCP连接事件,TCP连接可读事件,TCP连接关闭事件,TCP连接可写事件,异步读磁盘成功事件……这些事件由事件收集分发器(如epoll)进行分发。因此一个worker可以处理多个连接请求,通过异步非阻塞,来压榨性能;






nginx处理事件的简单模型

8.划分请求的原则:

先找到请求处理流程中的阻塞方法或阻塞代码段,按照下面方式划分:

a.将阻塞进程的方法按照相关的触发事件分解为两个阶段:阻塞方法改为非阻塞,调用非阻塞方法后将进程归还给事件分发器;第二阶段处理非阻塞方法最终返回的结果;

b.将阻塞方法调用按照事件分解为多个阶段的方法调用:比如要读取10MB内容,且需要到驱动硬盘寻址,那么可以将其划分为每次读取10KB,这个时间是可控的,使得整个系统可以及时处理其他请求。

c.使用定时器划分阶段,避免不必要的标志位轮询;

d.如果阻塞方法完全无法继续划分,则必须使用独立的进程执行这个阻塞方法;

9.






nginx启动流程图

10.






worker进程正常工作,退出时的流程图

11.






master进程的工作循环
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: