1.概述
nginx默认采用的是多进程的架构方式。和haproxy有很大的不同,haproxy作者是推荐使用但进程的方式的,因为作者认为单进程的性能能应付大部分的case。而且多进程会带来很复杂的管理面问题,所以也不得宠。
但nginx采用单master+多worker进程的架构方式,天然就是为了多进程服务。
很多人看nginx代码,都迫不及待的看什么样的io模型,怎么快速做的http解析和收发等数据平面的东西,但当面临使用的时候,管理面遇到的问题远远比数据面严重的多,比如我们要是在云环境中使用nginx为用户做负载均衡或者cdn/waf之类的,必然要考虑如下的问题
- 如何做到横向扩展,比如一台机器启动多少进程?多加机器能解决性能问题么?
- m台机器,一台机器n个nginx进程,如何管理这些m*n个进程?比如加载新配置,比如重启死循环或者hang住的进程
- 当升级重启的时候,如何做到真正的0宕机?
- m*n的nginx集群,他们的统计怎么搞?比如访问的top 10 域名是什么?
master进程可以认为是管理平面的东西:
- 加载/更新配置文件
- 管理所有worker进程的创建,重启
然后我们看一下nginx是如何管理worker进程和配置文件的更新的
2.关于worker进程的管理
worker进程是从master进程fork出来的进程,nginx提供了几种不同的fork方式:
- NGX_PROCESS_NORESPAWN
- NGX_PROCESS_JUST_SPAWN
- NGX_PROCESS_RESPAWN
- NGX_PROCESS_JUST_RESPAWN
- NGX_PROCESS_DETACHED
我们一个一个理一下
2.1.NGX_PROCESS_RESPAWN
这个是最常规的操作,fork worker进程的时候设置这个标志,当worker进程因为意外退出的时候,master进程会执行再生(respawn)操作。
1 | static ngx_uint_t |
所以可以认为初次启动master的时候(比如刚启动,比如更新二进制了)都用以这个参数启动worker
2.2.NGX_PROCESS_JUST_RESPAWN
just是刚刚的意思,刚刚spawn出来的,用于更新配置的时候,因为更新配置执行如下的步骤
1.master加载新配置文件
2.fork新的worker进程
3.给使用旧配置文件的worker进程发QUIT信号
第二步fork进程的时候腰加上NGX_PROCESS_JUST_RESPAWN这个标志,用于给第三步区分哪些是旧进程,哪些是新欢。
2.3.NGX_PROCESS_JUST_SPAWN
这个和上一个差不多,用于cache manager,我不喜欢
这里注意一下,上面提到的3个类型,其实是转化成2个标志的,即respawn和just。
just:刚刚搞出来的,别动我,只动就的,用于区分新旧
respawn:本进程被master管理,死的时候可以自动拉起
spwawn由于前面没有re,只是fork出来就拉倒,所以JUST_SPAWN只有just是有含义的
2.4.NGX_PROCESS_DETACHED
这是说fork出来的进程和父进程没有管理的关系,比如nginx的master升级(老版本有bug),新的master从旧的mastr fork出来,就需要这样的标志,fork出来后和父进程没啥关系
2.5.NGX_PROCESS_NORESPAWN
cache loader会用到,当第一次启动的时候,使用NGX_PROCESS_NORESPAWN,就是启动一个进程执行ngx_cache_manager_process_cycle.但需要注意和上面的DETACHED的区别,因为在nginx里,一般父子进程都有很多管道通讯,只有DETACHED的模式下没有pipe通讯,这个NORESPAWN是保留了和父进程的管道通讯的
但是当重新加载配置的时候,还是继续使用NGX_PROCESS_JUST_SPAWN来区分新欢旧爱的
3.关于配置文件的加载过程
修改完配置文件后,通过如下的步骤让配置文件生效
- 给master进程发送HUP信号
master收到信号后会设置
1 | ngx_reconfigure = 1; |
然后下个周期检查ngx_reconfigure,调用ngx_init_cycle重新解析配置文件,生成一个cycle,注意一个cycle可以理解对应一个配置文件的周期。
在ngx_init_cycle里会做一些listner的bind和unbind操作,即旧的listener和新的listener的merge,当然还有其他配置的merge。
- fork worker进程
worker进程里当然会能访问前面的cycle对象
- 给所有旧的worker发送NGX_SHUTDOWN_SIGNAL信号
旧的worker进程收到后,会关闭listen socket,然后等所有连接断开后,进程退出。
4.关于二进制的升级
写代码难免有bug,有bug就得改,改了后想生效就得升级。
给master发送一个USR2信号,ngx_change_binary会设置为1.
然后在那个ngx_init_cycle里,master进程会fork进程执行新的二进制(ngx_execute_proc)
ngx_new_binary会赋值为新master的进程id。
master起来后就是全新的master,会自动拉起新的worker进程,注意老master和新master都监听相同的listen socket,因为是fork出来执行execv的所以一样,nginx的listen socket的merger是它的killer feature。
这时候2套master和worker进程都在了,然后给旧的master发送WINCH信号,master会给worker发送graceful shutdown通知
这样就剩下旧的master+新的master+新的worker了,
为啥要留着旧的master呢?因为怕新的二进制有问题,如果有问题的话,
- 发送HUP给旧的master,旧的worker就起来了
- 发送TERM给新的master,刚来起来的worker就被干掉了
<div class="ds-thread" data-thread-key="nginx_1" data-title="nginx进程架构" data-url=""></div>