腹泻原因

首页 » 常识 » 问答 » Nginx架构原理科普
TUhjnbcbe - 2025/5/17 21:32:00
北京治疗白癜风哪家医院权威 http://pf.39.net/bdfyy/bdfzj/

Nginx(发音为“engineX”)是由俄罗斯人IgorSysoev编写的一个免费的、开源的、高性能的HTTP服务器和反向代理,也是一个电子邮件(IMAP/POP3/SMTP)代理服务器,其特点是占有内存少,并发能力强。Nginx因为它的稳定性、丰富的模块库、灵活的配置和较低的资源消耗而闻名。目前Nginx已经被F5收购。

Nginx由内核和一系列模块组成,内核提供web服务的基本功能,如启用网络协议,创建运行环境,接收和分配客户端请求,处理模块之间的交互。Nginx的各种功能和操作都由模块来实现。Nginx的模块从结构上分为核心模块、基础模块和第三方模块。这样的设计使Nginx方便开发和扩展,也正因此才使得Nginx功能如此强大。

Nginx社区分支:

Openresty:由章宜春开发的,最大特点是引入了ngx_lua模块,支持使用Lua开发插件,并且集合了很多丰富的模块,以及Lua库。Tengine:主要是淘宝团队开发。特点是融入了因淘宝自身的一些业务带来的新功能。Nginx官方版本,更新迭代比较快,并且提供免费版本和商业版本。

linux视频教程运维NginxshellNosqlOpenStackrhel7在线课程淘宝¥19¥60购买

进程模型与架构原理

Nginx服务器启动后,产生一个Master进程(MasterProcess),Master进程执行一系列工作后产生一个或者多个Worker进程(WorkerProcesses)。其中,Master进程用于接收来自外界的信号,并向各Worker进程发送信号,同时监控Worker进程的工作状态。当Worker进程退出后(异常情况下),Master进程也会自动重新启动新的Worker进程。Worker进程则是外部请求真正的处理者。

多个Worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个Worker进程中处理,一个Worker进程不可能处理其它进程的请求。Worker进程的个数是可以设置的,一般我们会设置与机器CPU核数一致。同时,Nginx为了更好地利用多核特性,具有CPU绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效(CPUaffinity)。所有的进程的都是单线程(即只有一个主线程)的,进程之间通信主要是通过共享内存机制实现的。

Nginx在启动后,在系统中会以后台模式(daemon)运行,后台进程包含一个Master进程和多个Worker进程。我们也可以手动地关掉后台模式,让Nginx在前台运行,并且通过配置让Nginx取消Master进程,从而可以使Nginx以单进程方式运行。很显然,生产环境下我们肯定不会这么做,所以关闭后台模式,一般是用来调试用的。Nginx是以多进程的方式来工作的,当然Nginx也是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是Nginx的默认方式。Nginx采用多进程的方式有诸多好处,本文主要讲解Nginx的多进程模式。

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

在Nginx启动后,如果我们要操作Nginx,要怎么做呢?前面我们说过,Master进程用来管理Worker进程,所以我们只需要与Master进程通信就行了。Master进程会接收来自外界发来的信号,再根据信号做不同的事情。所以我们要控制Nginx,只需要通过kill命令向Master进程发送信号就行了。比如kill-HUPpid,则是告诉Nginx从容地重启。我们一般用这个信号来重启Nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。

Master进程在接收到HUP信号后是怎么做的呢?首先Master进程在接到信号后,会先重新加载配置文件,然后再启动新的Worker进程,并向所有老的Worker进程发送信号,告诉他们可以光荣退休了。新的Worker在启动后,就开始接收新的请求,而老的Worker在收到来自Master的信号后就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后再退出。

当然,直接给Master进程发送信号,这是比较老的操作方式,Nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如./nginx-sreload就是来重启Nginx的,./nginx-sstop就是来停止Nginx的运行。如何做到的呢?我们还是以reload为例,我们看到在执行命令时启动一个新的Nginx进程,而新的Nginx进程在解析到reload参数后,就知道我们的目的是控制Nginx来重新加载配置文件了,它会向Master进程发送信号,然后接下来的动作,就和我们直接向Master进程发送信号一样了。

现在,我们知道了当我们在操作Nginx的时候,Nginx内部做了些什么事情,那么,Worker进程又是如何处理请求的呢?我们前面有提到,Worker进程之间是平等的,每个进程处理请求的机会也是一样的。当我们提供80端口的HTTP服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?

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

Nginx采用这种进程模型有什么好处呢?首先,对于每个Worker进程来说,独立的进程不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,Master进程则很快启动新的Worker进程。当然,Worker进程异常退出,肯定是程序出现了bug,异常退出会导致当前Worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。好处还有很多,大家可以慢慢体会。

其实除了Master进程和Woker进程之外,Nginx中还有两个特殊用途的进程:缓存加载器进程(CacheLoader)和缓存管理器进程(CacheManager)。CacheLoader进程是在Nginx服务启动一段时间后由主进程生成,在缓存元数据重建完成后就自动退出。CacheManager进程一般存在于主进程的整个生命周期,负责对缓存索引进行管理。通过缓存机制,可以提高对请求的响应效率,进一步降低网络压力。

根据上面的分析,我们可以将Nginx服务器的结构大致分为主进程、工作进程、后端服务器和缓存等部分。下图中展示了各个部分之间的联系和交互:

热升级

热升级是指在不停止服务的情况下更换Nginx的binary文件。热升级会经历以下几个步骤:

第一步是把旧的Nginxbinary文件替换为新的,之所以说只替换binary文件是因为大部分场景下,我们新编译的nginx文件所指定的相应的配置选项,比如说配置文件的目录在哪里?log的所在目录在哪里?必须保持和老的Nginx是一致的,否则的话没有办法复用nginx.conf文件,如果我们仅仅替换binary文件,请注意要备份,另外在新版本的Linux中,会要求在覆盖一个正在使用的文件时需要用cp-f才能够替换。

第二步,我们向现有老的Master(Old)进程发生USR2信号,之后Master(Old)进程会将修改pid文件名,添加后缀.oldbin。这一步是在为新的Master进程让路,虽然Master、Worker进程都可以接受信号,但是为了管理方便,通常不对Worker进程直接发送信号,所以我们依赖于Master进程,他必须把他的pid保存下来,为了新的Master使用pid.bin这个文件名,所以把老的pid文件改为pid.oldbin。

第三步,使用新的binary文件启动新的Master(New)进程。所以到现在为止,会出现两个Master进程:Master(Old)和Master(New),如下图所示。Master(New)进程会自动启动新的Worker进程。这里新的Master(New)进程是怎么样启动的呢?它其实是老的Master(Old)进程的子进程,不过这个子进程是使用了新的binary文件带入来启动的。

第四步,向Master(Old)进程发送QUIT信号。怎么样找到Master(Old)进程呢?我们可以根据ps命令或者通过.oldbin文件查找到Master(Old)进程的进程号,然后向这个进程号发送QUIT信号,那么Master(Old)进程会优雅的关闭老Worker进程,这样我们的热升级就结束。

整个过程中,Master(Old)进程是一直存活的,这是为了方便我们进行回滚,也就是发现新的Nginx程序有问题了,这个时候因为Master(Old)进程还在,可以向Master(Old)进程发送HUP信号,相当于执行了一次reload,会启动新的Worker进程,然后再向Master(New)进程发送QUIT信号,也就是要求新的Worker进程优雅退出,就实现了回滚。当退出老Master(Old)进程以后不能进行回滚。如果想回滚,就需要再走一次热升级流程,用备份好的老Nginx文件作为新的热升级文件(因此建议备份旧的Nginx文件)。

在一个父进程退出,而它的一个或多个子进程还在运行时,那么这些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。所以老Master(Old)进程退出后,新的Master(Old)进程并不会退出。

以上就是热升级流程,我们可以通过他实现不停机更新我们的Nginx,这为我们持续使用Nginx的最新特性提供了帮助。

模块化

Nginx的内部结构是由内核和一系列的功能模块所组成,高度模块化的设计是Nginx的架构基础。内核的设计非常微小和简洁,完成的工作也非常简单。Nginx的各种功能和操作都由模块来实现,每个模块就是一个功能模块,只负责自身的功能,模块之间严格遵循“高内聚,低耦合”的原则。

模块从结构上分为:核心模块(HTTP模块、EVENT模块和MAIL模块)、基础模块(HTTPAccess模块、HTTPFastCGI模块、HTTPProxy模块和HTTPRewrite模块)、第三方模块(HTTPUpstreamRequestHash模块、Notice模块和HTTPAccessKey模块)。

模块从功能上还可以分为以下几种:

Handlers(处理器模块):此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。Filters(过滤器模块):此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。Proxies(代理类模块):此类模块是Nginx的HTTPUpstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。

Nginx(内核)本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个locationblock,而此location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做Nginx真正的劳动工作者。通常一个location中的指令会涉及一个Handler模块和多个Filter模块(当然,多个location可以复用同一个模块)。Handler模块负责处理请求,完成响应内容的生成,而Filter模块对响应内容进行处理。

常用使用场景

Nginx的应用场景非常的广泛,下面就以几种常见的为例做一下简单的介绍。

正向代理

正向代理其实就是说客户端无法主动或者不打算主动去向某服务器发起请求,而是委托了Nginx代理服务器去向服务器发起请求,并且获得处理结果,返回给客户端。

举个栗子:广大社会主义接班人都知道,为了保护祖国的花朵不受外界的乌烟瘴气熏陶,国家对网络做了一些“优化”,正常情况下是不能外网的,但作为程序员的我们如果没有谷歌等搜索引擎的帮助,再销魂的代码也会因此失色,因此,网络上也曾出现过一些fanqiang技术和软件供有需要的人使用,如某VPN等,其实VPN的原理大体上也类似于一个正向代理,也就是需要访问外网的电脑,发起一个访问外网的请求,通过本机上的VPN去寻找一个可以访问国外网站的代理服务器,代理服务器向外国网站发起请求,然后把结果返回给本机。

正向代理配置实例:

resolver是配置正向代理的DNS服务器,listen是正向代理的端口,配置好了就可以在浏览器上面或者其他代理插件上面使用服务器ip+端口号进行代理了。

反向代理

反向代理(ReverseProxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。正向代理在客户端侧,反向代理在服务端侧。

简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。下面贴上一段简单的实现反向代理的配置:

负载均衡

负载均衡其意思就是分摊到多个操作单元上进行执行,例如:Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。

而Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略:

轮询(RR):默认的策略。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器宕掉,能自动剔除。权重(weight):可以给不同的后端服务器设置一个权重值(weight),用于调整不同的服务器上请求的分配率。权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的。ip_hash:每个请求按照发起客户端的ip的hash结果进行匹配,这样的算法下一个固定ip地址的客户端总会访问到同一个后端服务器,这也在一定程度上解决了集群部署环境下Session共享的问题。fair:智能调整调度算法,动态的根据后端服务器的请求处理到响应的时间进行均衡分配。响应时间短处理效率高的服务器分配到请求的概率高,响应时间长处理效率低的服务器分配到的请求少。Nginx默认不支持fair算法,如果要使用这种调度算法,请安装upstream_fair模块。url_hash:按照访问的URL的hash结果分配请求,每个请求的URL会指向后端固定的某个服务器,可以在Nginx作为静态服务器的情况下提高缓存效率,示例如下。同样要注意Nginx默认不支持这种调度算法,要使用的话需要安装Nginx的hash软件包。Nginx支持同时设置多组的负载均衡,用来给不同的server来使用。与此同时,upstream可以设定每个后端服务器在负载均衡调度中的状态,相关配置示例如下:

HTTP服务器

Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器。

这样如果访问localhost就会默认访问到/root/website/目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。

动静分离

动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。

这样我们就可以把html、图片、css、js等放到/root/website/目录下,而Tomcat只负责处理jsp和请求。例如当我们后缀为gif的时候,Nginx默认会从/root/website/获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器。我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了。只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活。

1
查看完整版本: Nginx架构原理科普