透过现象看本质——回头再看Nginx(进程模型、异步非阻塞、源码目录结构)

透过现象看本质——回头再看Nginx

Nginx的进程模型

​ 使用过nginx的朋友都知道nginx的性能很高,而其缘由可能少有人知。首先,nginx的架构就奠基了其高性能的基础。那么就先来看看nginx的基础架构吧,以下图所示:(不能彻底理清楚全部内容也不要紧,由于本小节讲述的主要内容是Nginx的进程模型)html

透过现象看本质——回头再看Nginx(进程模型、异步非阻塞、源码目录结构)

​ 本小节先来讲说Nginx基础架构中的进程模型:linux

透过现象看本质——回头再看Nginx(进程模型、异步非阻塞、源码目录结构)

​ 所谓进程模型,即Nginx响应请求或服务时程序运行(机器执行指令集)的方式,通常在nginx服务启动后,在Unix系统中会以daemon的方式在后台运行,后台进程包含一个master进程以及多个worker进程。nginx

​ 而在咱们进行调试时,能够手动关闭后台模式以及设置nginx取消master进程,使其以单进程方式运行,可是生产环境中确定不容许这样上线服务,主流的运行方式仍是默认使用多进程。c++

这里插一下关于进程和程序的区别:shell

其实这二者彻底不是一个概念,程序只是一堆等待执行的代码和部分待处理的数据,只有被加到内存中,由CPU处理执行代码,才能够发挥其做用,从而造成一个真正的“活的”动态的进程。因此两者的典型区别不言而喻,程序未被执行时是静态的,只有运行时才变成动态的进程,而且进程善始善终(进程的“生老病死”)。编程

​ 言归正传,仍是回来讲nginx的多进程模式。nginx启动后,会有如上图所示的一个master进程和多个worker进程。vim

​ master进程:负责管理worker进程,接收、发送信号,监控worker进程状态;操做控制Nginx只须要经过与master进程通讯就能够了。服务器

​ worker进程:负责处理基本网络事件,每一个worker进程都是平等且独立的(记住独立两个字)。通常worker进程数会设置为机器(服务器)的CPU核心数(缘由下文会讲到)。网络

Nginx响应链接的工做原理

​ 咱们从nginx启动时的工做流程以及启动后所响应外部的操做来理解nginx处理一个链接的工做原理及过程。数据结构

启动时:

​ Nginx启动时,会先解析配置文件,获取须要监听的ip地址与端口号(涉及网络编程以及TCP/IP理论),而后在master进程中,首先初始化好这个监控的socket,而后fork多个子进程,子进程竞争(例如互斥锁机制)accept新的链接。此时,Nginx以及启动好,等待客户端来链接本身。

fork——属于系统编程内容,原意为叉子,可能老外使用的是叉子,而决定使用这个单词来比喻fork的做用,fork函数的功能就是建立(有时候理解为派生)出多个子进程

启动后:

​ 客户机发起链接请求,经过TCP三次握手与Nginx创建一个链接,此时竞争成功的一个worker进程获取到这个创建好链接的socket,开始建立nginx对链接的封装(其实说白了就是结构体封装)。随之执行读写事件(本文所述的事件理解为网络事件便可)函数、添加读写事件来与客户端进行数据交互。最后,其中一方主动断开链接(四次挥手),到此,一个链接也就寿终正寝了(该worker进程被宣告退休)。

Nginx重启流程

​ 当咱们执行命令kill -HUP pid时,master进程是如何响应的呢?

​ 首先,master接收到kill(不要简单理解为杀死,不少人都保持着这样的理解,可是这样不许确,在linux系统中,kill表示的是用于向进程发送信号的,可使用kill -l查看能够携带的信号)的信号时,首先会重载配置文件,而后启动新的worker进程,而且向旧的worker进程发送信号,提示他们作完当前事件以后就能够光荣退休了。新的worker进程启动后,开始接收新的请求。这种方式就是直接给mater进程发送信号。

​ 而在新的版本中,可使用其余方式例如:./nginx -s reload就是重启服务,./nginx -s stop就是中止服务,执行./nginx -s reload时会启动一个新的进程(nginx),该进程解析到reload的时候,就控制nginx重载配置文件,向master进程发送信号了,随后就和上面旧版本的方式同样的过程了。

以上就是nginx内部工做(内部究竟干了啥活)了,可是此时考虑一个问题:worker进程如何处理请求的呢?

worker进程工做方式

​ 既然worker进程是由master进程fork出来的,而且每一个worker进程都是对等的,那么当一个请求过来时,任何一个worker进程都有可能处理它,这个时候怎么办呢?

​ 为了保证只有一个进程处理该链接请求,全部的worker进程会进行竞争,触发锁机制,通常是互斥锁,抢到锁的进程得到权利来处理这个请求(读事件开始调用accept接受该链接),进行读取、解析、处理请求,将结果(数据信息)返回给客户端,最后断开链接,这个请求完整走完它的人生。所以,一个请求彻底是由也仅仅由一个worker进程处理。

Nginx为何选择多进程模型?

​ 这个问题其实不是特别容易说,没有进程线程的理论基础可能也没法理解原理,这边给出其具有的”独立“特征所带来的优点吧。

​ ”独立“的进程,意味着不须要加锁,节省开销,而且多个worker进程之间互不影响,某一个出现bug后,会有新的worker进程开始工做,从而下降风险,也不会中断其余服务,同时也简化了编程和检查问题。

​ 那么,问题又来了,使用多进程模型,每一个worker进程中也只有一个主线程,如何能够处理高并发呢?

且看下节。

Nginx异步非阻塞的处理请求方式(简单说说)

简单理解阻塞与非阻塞

  • 阻塞就是线程在执行IO操做获取数据时,这个IO可能会须要必定的时间才能等到数据返回,而后才能接着执行下面的命令。那么,此时,这个线程的等待状态(通常在nginx中称做内核等待,而nginx中最忌讳阻塞的系统调用)咱们就把它称为阻塞。没有充分利用起cpu的资源。
  • 非阻塞仍是这个线程在进行 IO操做时,无需等待数据的返回,能够接着往下执行代码命令,会返回一个结果给你,你可使用cpu资源作其余的事情。

举个例子:阻塞就比如下课你去食堂吃饭,但去的时候人太多了,你就傻傻地在原地排队等。

非阻塞就比如是你去食堂吃饭,人依旧不少,可是你能够先去上个洗手间,看会儿资讯,不影响你干这些事情。充分利用时间,这个时间就比如是CPU的使用率,非阻塞的存在能够避免浪费CPU资源。

为何须要异步方式?

上面说的非阻塞,在nginx应用时,虽然不阻塞了,但你得不时地过来检查一下事件的状态,你能够作更多的事情了,但带来的开销也是不小的。因此,才会采起异步方式。

  • 同步:同步指的当线程进行IO操做请求数据时,是你主动"关心"数据的返回。
  • 异步:当前线程无需主动关心数据是否返回,当数据返回时,会有相关的事件通知你。

举个例子:同步就是你有不懂的问题问同事,他给你开始讲解解决思路或方案,你一直在主动听取他的内容,异步就是一样你问同事问题,他可能说我先考虑考虑,想出来了主动来告诉你。

异步方式+非阻塞处理请求能够避免浪费CPU资源,同时提升响应速度,工做效率。其实本质上就是说worker进程,在循环执行异步请求(事件),从而处理高并发。

所以,设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会致使进程来竞争cpu资源了,从而带来没必要要的资源浪费。

结尾给出Nginx源码目录介绍吧。

Nginx代码的目录结构

解压nginx软件,进入其目录就能够看到它的目录结构以下:

[root@localhost nginx-1.16.1]# tree -d
.
├── auto                        #自动编译安装相关目录
│   ├── cc                      #针对各类编译器进行相应的编译配置目录,包括gcc、ccc等
│   ├── lib                     #程序依赖的各类库,包括openssl、pcre 、perl等
│   │   ├── geoip               
│   │   ├── google-perftools    
│   │   ├── libatomic           
│   │   ├── libgd               
│   │   ├── libxslt             
│   │   ├── openssl             
│   │   ├── pcre                
│   │   ├── perl                
│   │   └── zlib                
│   ├── os                      #针对不一样的操做系统所作的编译配置目录
│   └── types                   #与数据类型相关的一些辅助脚本
├── conf                        #存放默认配置文件,在make install后,会拷贝到安装目录中去
├── contrib                     #存放一些实用工具,如geo配置生成工具(geo2nginx.pl
│   ├── unicode2nginx           #
│   └── vim                     #
│       ├── ftdetect            #
│       ├── ftplugin            #
│       ├── indent              #
│       └── syntax              #
├── html                        #存放默认的网页文件,在make install后,会拷贝到安装目录中去
├── man                         #手册
└── src                         #存放nginx的源代码
    ├── core                    #nginx的核心源代码,包括经常使用数据结构的定义,以及nginx初始化运行的核心代码如main函数
    ├── event                   #对系统事件处理机制的封装,以及定时器的实现相关代码
    │   └── modules             #不一样事件处理方式的模块化,如select、poll、epoll、kqueue等
    ├── http                    #nginx做为http服务器相关的代码
    │   ├── modules             #包含http的各类功能模块
    │   │   └── perl            #
    │   └── v2                  #
    ├── mail                    #nginx做为邮件代理服务器相关的代码
    ├── misc                    #一些辅助代码,测试c++头的兼容性,以及对google_perftools的支持
    ├── os                      #主要是对各类不一样体系统结构所提供的系统函数的封装,对外提供统一的系统调用接口
    │   └── unix                #
    └── stream                  #实现四层协议的转发、代理或者负载均衡等第三方模块

对于使用者而言最关键的是conf目录,html目录,对于开发者而言可能须要看其源码文件:目录为src,这就涉及到nginx的核心部分,包括模块、模块对应的功能等。

参考连接:tengine.taobao.org

相关文章
相关标签/搜索