nginx源码分析--框架设计 & master-worker进程模型

Nginx的框架设计—进程模型

在这之前,我们首先澄清几点事实:

nginx作为一个高性能服务器的特点,其实这也是所有的高性能服务器的特点,依赖epoll系统调用的高效(高效是相对select/poll这些系统调用的,底层有一个链表和红黑树,避免了轮询,减少了用户空间和系统空间之间的数据传递等),非阻塞(所有的操作都是非阻塞,这样),多进程(master-slave进程模型),这些事实使得nginx成为一个高性能服务器的前提条件。

既然作为一个软件(http服务器),相对于一个网络库而言肯定有更完善的功能。它可以在不需要关闭的前提下实现更新系统;可以接受外部的信号,根据不同的信号做相应的处理;可定制,用户可以开发第三方的模块,根据自我需要将模块添加到7个处理阶段(HTTP请求本来是有11个阶段,但是其中的4个阶段是不支持添加用户自定义的模块的),它可以作为反向代理;它是可以支持高并发的,它的工作模式是基于配置文件的,一个配置文件决定了nginx的工作模式,配置文件解析完成也就完成了初始化的所有工作,配置文件的解析在nginx中占了举足轻重的地位。基于上述几点事实慢慢阐述nginx的设计理念。

首选从大的框架说起,既然是多进程,那么如何分配各个进程的任务,针对CPU的核数来开启进程数目(当然是否为多进程可以通过配置文件来配置的),分为master-worker模型,master负责开启worker进程,master进程负责和worker进程通信,master进程负责接收外部的信号,master进程负责将某些信号(总共8个信号,但是对应着worker进程关注的三个变量,三个变量分别为ngx_quit/ngx_reopen/ngx_terminate等)传递给worker进程,所以worker进程是不需要关注外部信号的,master进程需要关注worker进程的工作情况,比如接受到了SIGCHLD信号,也就是worker进程退出,在需要避免worker进程成为僵尸进程外还需要检查worker进程是否是正常死亡,如果非正常死亡还需要重启这个worker进程。那么worker进程做了什么呢?进程间通信就这些么?

其实master进程和worker进程时间是有通信,worker进程之间也是可以有通信的,只不过这里没有通信,通信的方式是通过socketpair,也有共享内存,两者各有不同的作用。Worker做了什么呢!负责监听,接受连接,接受完以后就负责和这个连接进程交互,直到这个连接最终结束!其实进程模型到这里就已经结束了,只不多worker进程怎么做就是另一回事了!

下面从代码上来分析上面提到的一切:

在src/core/nginx.c中有入口Main函数,大体了解main函数都做了些什么。

1)、ngx_get_options函数是根据用户输入的参数来设置一些全局变量,比如用户输入了-s,那么就设置ngx_signal标志,这个标志说明用户需要给ngxin输入一个信号,诸如此类的全局变量。往下再根据全局变量再进行处理。

2)、接下来的if(ngx_show_version)就是在步骤1)来设置的。表示用户是否想看版本信息。(当然这是第一个在ngx_get_options中被设置的全局变量)

3)ngx_time_init()函数设置了表示时间的初始化,函数中的最后一行ngx_time_update是一个很重要的函数,是用来更新时间的函数,根据配置文件,决定了更新时间的方式,以后再讨论,由于每次调用gettimeofday()这个系统调用很浪费时间,那么就需要一个时间缓存,更新时间的有两个, ngx_time_sigsafe_update和ngx_time_update,前者指负责err_log的时间,后者负责更新很多时间

4)接下来就是ngx_getpid(),为master进程创建一个文件,单独保存master进程的PID.

5)接下来就是准备初始化全局变量ngx_cycle,这个一个大块头,先创建一个内存池,保存相关用户传递进来的参数ngx_save_argv,

6)接下来就是处理选择项的问题,就是看看步骤5中根据用户输入的参数都保存了什么参数。

7)ngx_add_inherited_sockets函数是处理集成的套接字,这些套接字是根据NGINX这个环境变量来设置的,如果没有这个环境变量,那么就不进行操作,这些套接字是需要保存到cycle中的listening中,作为监听套接字。

8)ngx_init_cycle是初始化全局大块头,这里做了很多很多的配置,尤其是其中的ngx_conf_parse()函数,这个函数解析配置文件。

9)接下来就是处理步骤1中根据用户输入的参数都设置了什么全局变量,比如ngx_test_config和ngx_signal,如果是信号就需要相应的操作ngx_signal_process.

10)如果不是信号处理,那么就需要初始化master进程需要关注的信号。

11)判断是否需要设置为daemon进程,最后进入开启worker进程的模型。ngx_single_process_cycle和ngx_master_process_cycle。

到此为止,main函数已经分析完了,其中有三点比较重要,一个ngx_init_cycle()

Ngx_master_process_cycle和ngx_init_signals()函数。咱们从简单的来看吧

Nginx中信号的处理方式:

在ngx_init_signals函数中,遍历signals全局数组,这个数组中保存着nginx需要关注的所用信号,为每一个信号赋值相应的信号处理函数。所有的信号都对应同一个信号处理函数ngx_signal_handler()函数,在这个函数有看到了前面提到的一个函数,和更新时间有关,ngx_time_sigsafe_update,这个函数是更新错误时间的,也就是说,在信号处理中有一个更新时间的操作,虽说只更新错误信息的时间。看看信号处理的方式,首先根据ngx_process来判断当前的传递信号的方式,是给master进程传递的?还是给所有的进程来传的?

总之有一句话,master进程从外部接受到信号,那么master进程就会被激活,所有的信号都对应同一个信号处理函数ngx_signal_handler处理,这个处理只是处理相应的全局标志位,那么master进程再根据标志位来做相应的处理,大部分信号处理就是ngx_signal_worker_process函数,也就是需要将master进程接收到的信号也给worker进程传递一份,这里只有三个信号需要传递给worker进程,也就是说worker进程只需关注三个信号(其实不是三个信号,可能是好几个信号,只不过有多个信号对应着同一个处理,最终worker进程只需要关注ngx_reopen/ngx_quit/ngx_terminate这三个变量即可)。传递信号的方式可能是用socketpait,也可能是用kill系统调用。

再简单点介绍信号的处理就是:既然nginx是多进程的,那么不可能所有进程都可以接受外部信号的,那么这个任务交给master进程算了,master进程就需要注册自己关注的信号,同时相应的信号处理函数,那么在fork出子进程后,子进程就需要清空集成的信号(因为外部信号处理交给master进程了),父子进程的通信通过socketpair来处理,那么子进程在这个通道上的读取事件处理就是ngx_channel_handler,在这个函数中,根据master进程发送的命令来修改全局变量。注意进程被信号激活接下来的操作都是判断全局变量,那么全局变量的修改对于不同的进程又不相同,master进程是通过信号处理函数(ngx_signal_handler处理),worker进程是通过socketpair创造的套接字对应的函数(ngx_channel_handler)

父子进程再被信号激活以后,都是根据全局变量来判断做什么处理的。那么谁来修改全局变量呢?那么对于master进程来说,修改全局变量的任务交给了ngx_signal_handler函数,对于子进程来说,这个任务就是ngx_channel_handler来处理的。而且master进程关注的信号比子进程关注的信号多,所有有的信号是不需要给子进程来传递的,所以有了ngx_process这个变量。比如在ngx_get_options函数中,如果发现了-s选项,那么说明用户是准备给nginx发送信号,如果信号是stop之类,就需要将ngx_process设置为NGX_PROCESS_SIGNALLER,说明这个信号需要给子进程发送。Ngx_process被赋值的地方不多,在nginx.c中,ngx_cycle.c中都有被赋值的操作。Ngx_process在ngx_get_options中被赋值,如果用户输入的信号是stop之类的信号等。在main函数中,如果有master配置,那么就被赋值为NGX_PROCESS_MASTER。

时间: 2024-10-31 14:56:24

nginx源码分析--框架设计 & master-worker进程模型的相关文章

nginx源码分析--高性能服务器开发 常见进程模型

1.高性能服务器 对一个高性能服务器来说,处理速度快和资源占用小是典型特性,尤其是当服务器遇到C10K问题的时候(网络服务器在处理数以万计的客户端连接时,往往出现效率低下甚至完全瘫痪,这被称为C10K问题).要做到处理速度足够快,其并发模型的设计相当关键,而要做到资源尤其是内存资源的占用少,就要依赖于其资源分配和资源管理的方案设计. 服务器的并发模型设计是网络编程中很关键的一个部分,服务器的并发量取决于两个因素,一个是提供服务的进程数量,另外一个是每个进程可同时处理的并发连接数量.相应的,服务器

Nginx源码分析—架构设计思想

Nginx源码分析-架构设计思想 我任务nginx的源码可以分为三个部分,一个是在ngx_init_cycle之前,这个也算是为了重新启动nginx而准备的代码,比如说在这个时候可以接受外部的信号,也可以保存传递的参数,等等,当然在以后的函数中也考虑了是否正在重启nginx. 至于ngx_init_cycle这个函数,是一个很庞大的函数,在这个函数中可以看到调用了各个模块的钩子函数,这里又设计到了nginx结构体的使用,比如所有的模块都是ngx_module_t这个结构体,但是这个结构体中的ct

nginx源码分析--从源码看nginx框架总结

nginx源码总结: 1)代码中没有特别绕特别别扭的编码实现,从变量的定义调用函数的实现封装,都非常恰当,比如从函数命名或者变量命名就可以看出来定义的大体意义,函数的基本功能,再好的架构实现在编码习惯差的人实现也会黯然失色,如果透彻理解代码的实现,领悟架构的设计初衷,觉得每块代码就想经过耐心雕琢一样,不仅仅实现了基本的功能给你,为其他人阅读也会提供很好的支持.细致恰当的命名规则就可以看出作者的功力. 2)更好更高的软件性能体现在架构设计上,好的架构会让软件更加稳定.容易维护.便于扩展.从核心模块

nginx源码分析--进程间通信机制 & 同步机制

Nginx源码分析-进程间通信机制 从nginx的进程模型可以知道,master进程和worker进程需要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号,master进程主要就是监控.接受外部信号,将有必要的信号传递给worker进程,master进程大部分时间都是阻塞在sigsuspend()函数调用上.Worker进程屏蔽了所有的外部信号,那么Master进程就通过套接字和worker进程通信,worker进程修改全局变量,使得worker进程接受

Nginx源码分析 - Nginx启动以及IOCP模型

Nginx 源码分析 - Nginx启动以及IOCP模型 版本及平台信息 本文档针对Nginx1.11.7版本,分析Windows下的相关代码,虽然服务器可能用linux更多,但是windows平台下的代码也基本相似 ,另外windows的IOCP完成端口,异步IO模型非常优秀,很值得一看. Nginx启动 曾经有朋友问我,面对一个大项目的源代码,应该从何读起呢?我给他举了一个例子,我们学校大一大二是在紫金港校区,到了 大三搬到玉泉校区,但是大一的时候也会有时候有事情要去玉泉办.偶尔会去玉泉,但

[nginx] nginx源码分析--健康检查模块锁分析

健康检查模块 见前文:[nginx] nginx源码分析--健康检查模块 其中有一张框架图, 接下来的内容,将会利用到这个图中的内容. [classic_tong @ https:////www.cnblogs.com/hugetong/p/12274125.html ]  描述 我们知道nginx是多进程的,每个进程都保存了相同的配置.但是实际上, 并不需要每一个进程对每一个后端服务器进行. 于是健康检查模块在这里需要一个进程间同步机制,用来协商哪一个进程对 哪一个后端服务器进行检查. 接下来

nginx源码分析--nginx外部信号 命令参数

nginx命令行参数 不像许多其他软件系统,Nginx 仅有几个命令行参数,完全通过配置文件来配置 -c </path/to/config> 为 Nginx 指定一个配置文件,来代替缺省的. -t 不运行,而仅仅测试配置文件.nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件. -v 显示 nginx 的版本. -V 显示 nginx 的版本,编译器版本和配置参数. nginx控制信号 可以使用信号系统来控制主进程.默认,nginx 将其主进程的 pid 写入到 /u

Nginx源码分析:3张图看懂启动及进程工作原理

编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器开发,阅读优秀源代码是一种有效的方式,nginx 是业界知名的高性能 Web 服务器实现,如何有效的阅读及理解 nginx?本文用图解的方式帮助大家来更好的阅读及理解 nginx 关键环节的实现.   陈科,十年行业从业经验,曾在浙江电信.阿里巴巴.华为.五八同城任开发工程及架构师等职,目前负责河狸

nginx源码分析--GDB调试

利用gdb[i]调试nginx[ii]和利用gdb调试其它程序没有两样,不过nginx可以是daemon程序,也可以以多进程运行,因此利用gdb调试和平常会有些许不一样.当然,我们可以选择将nginx设置为非daemon模式并以单进程运行,而这需做如下设置即可: daemon off; master_process off; 这是第一种情况: 这种设置下的nginx在gdb下调试很普通,过程可以[iii]是这样: 执行命令: [email protected]:/usr/local/nginx/