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

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

Nginx的进程模型

? 使用过nginx的朋友都知道nginx的性能很高,而其原因可能少有人知。首先,nginx的架构就奠定了其高性能的基础。那么就先来看看nginx的基础架构吧,如下图所示:(不能完全理清楚所有内容也没关系,因为本小节讲述的主要内容是Nginx的进程模型)

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

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

? 而在我们进行调试时,可以手动关闭后台模式以及设置nginx取消master进程,使其以单进程方式运行,但是生产环境中肯定不允许这样上线服务,主流的运行方式还是默认使用多进程。

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

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

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

? 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软件,进入其目录就可以看到它的目录结构如下:

[[email protected] 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

原文地址:https://blog.51cto.com/14557673/2482941

时间: 2024-11-20 16:56:04

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

nginx学习(二)——基础概念之异步非阻塞

上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的. 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的.想想apache的常用工作方式(apache也有异步非阻塞版本,但因其与自带某些模块冲突,所以不常用),每

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

菜鸟nginx源码剖析架构篇(二) nginx进程模型

Author:Echo Chen(陈斌) Email:[email protected] Blog:Blog.csdn.net/chen19870707 Date:Nov 17th, 2014 1.nginx进程模型 nginx采用的是多进程模型,典型的master-worker方式,采用一个master process(监控进程,也叫做主进程)和多个woker process(工作进程)的设计方式,此外,还有1个可选的chache manager和 1 个可选的cache loader进程.

Nginx:异步非阻塞IO

在使用socket编程中,经常会看到阻塞.非阻塞.同步.异步,那么它们之间到底有什么关系跟区别呢? 本次将那Nginx的异步非阻塞的事件驱动模型来解释一下它们之间的关系. 阻塞IO 在linux中,默认所有socket都是阻塞的. 这意味着使用该socket调用诸如recv的函数时,在没有数据到达之前,该函数将不会返回,导致线程被阻塞,直到数据到达. 非阻塞IO 我们可以使用fcntl把socket设置为非阻塞的. 这意味着使用该socket调用诸如recv的函数时,该函数将立刻返回,可以根据返

nginx进程模型 master/worker

nginx有两类进程,一类称为master进程(相当于管理进程),另一类称为worker进程(实际工作进程).启动方式有两种: (1)单进程启动:此时系统中仅有一个进程,该进程既充当master进程的角色,也充当worker进程的角色. (2)多进程启动:此时系统有且仅有一个master进程,至少有一个worker进程工作. master进程主要进行一些全局性的初始化工作和管理worker的工作:事件处理是在worker中进行的. 首先简要的浏览一下nginx的启动过程,如下图: 2.实现原理

python + flask + uwsgi + gevent + nginx 环境搭建(非阻塞)

Flask是Python中一个微型的Web开发框架.在debug 模式 或 单纯的 uwsgi模式下,flask是阻塞模式的,也就是说一次只能效应一个请求,或者在uwsgi 开启多进程,响应已知的请求个数:我们这里使用  uwsgi 和 gevent 配合nginx 解决flask的阻塞模式. 1.环境 CentOS Linux release 7.0.1406 (Core) Python 2.7.5 2.安装类库 yum install python-devel zlib-devel bzip

回头再看C语言-iOS之而今迈步从头越(一)

关于目前比较流行的编程语言的一个情况来看,到2014年8月之前,大体上的结果是: *数据来自网络 名列三甲的分别是:C,Java,Objective-C,从这个表里面可以看出,C语言在编程界还是被普遍的认为非常重要的,并且作为很多大学计算机系的必修基础科目之一,可见C语言还是非常有群众基础的. 话再说回到iOS开发上来,因为苹果系统基本都是使用Objective-C(后面简称为OC)来做第一语言来使用的,但在一些深处的机制(比如运行时runtime)以及图像处理以及通讯录访问方面,还是会经常使用

zoj 3634 Bounty hunter(dp,没完全想清楚,需要回头再看_20151027)

M - Bounty hunter Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Description Bounty hunter is a hero who always moves along cities to earn money by his power. One day he decides to N cities one by one At the

回头再看N层架构(图解)

不知不觉来博客园已经快两半了,时间过的真快. 这次的目标是再回顾一下传统的N层架构并且分析一下在DDD中的N层架构. 一.先来看一看传统的N层架构 N-层架构的出现,主要是由于观注点的分离而产生,这三层中BL层的职责一般比较难理清,具体请看下图. 接下来我们关注一下层间交互(分离出角色与职责以后,如何交互将成为架构或模式的主要特征) 然而在实际项目中我们也会出现这样的状况 这里存在一个反模式 二.传统的N层架构与DDD中N层架构的关系 三.分层架构的一个重要原则是:每层只能与位于其下方的层发生耦