本节主要说明:configure编译脚本,nginx.conf配置文件,nginx命令与main()函数主流程。configure不同的参数决定了编译出的nginx拥有哪些功能,nginx.conf配置文件决定了这些功能具体的使用方式。nginx命令决定了如何与nginx交互,包括但不限于:以何种方式启动nginx, 停止服务, 重新读取配置文件与平滑升级等。

关于Nginx的基本使用参考之前写过的文章:nginx的基本使用说明

学习目标

  1. 配置nginx运行环境,源码安装nginx并正常运行。
  2. 了解configure文件的作用,如何影响nginx程序的组成。
  3. 学习nginx的配置文件结构。
  4. 认识nginx常用命令以及这些命令在nginx启动过程中的作用。
  5. nginxmain()函数的功能有个流程上的认识,但是对细节不深究。

相关环境配置

Nginx版本下载地址: nginx-1.14.2

建立了GitHub源码学习地址,主要是一些注释记录:nginx-1.14.2-annotated

编译环境是一台云服务器:Ubuntu Server 18.04.1 LTS 64位 1核 2GB 1Mbps,编译工具为: gcc version 7.4.0

使用了两种编辑器:

  1. 通过xshell/iTerm2远程 + vim 8.0.1453(部分系统自带7版本,建议更新到vim 8版本才能使用某些插件), vim插件基本配置参考文章: vim插件配置
  2. 另一些时候使用vscode+remote插件。

源码目录结构

目录 说明
src目录 存放nginx源码。
man目录 存放nginx帮助文档。
html目录 存放默认网站文件。
contrib目录 存放其它机构或组织贡献的文档资料。
conf目录 存放nginx服务器的配置文件。
auto目录 存放大量的脚本文件,和configure脚本程序相关。
configure文件 Nginx自动安装脚本,用于环境检查,生成编译代码需要的makefile文件。
CHANGES,CHANGES.ru,LICENSE和README 都是Nginx服务器的相关资料。

编译安装

依赖库安装(默认安装了gcc):

1
sudo apt-get update
2
sudo apt-get install build-essential
3
sudo apt-get install libtool
4
sudo apt-get install libpcre3 libpcre3-dev zlib1g-dev openssl

极简安装步骤,不添加过多配置项, 下一节会说明configure文件的作用。

1
tar -zxvf nginx-1.14.2.tar.gz
2
cd nginx-1.14.2
3
./configure --prefix=/usr/local/nginx
4
make && make install

configure脚本

在编译安装(上一节)时可以看到, 执行./configure --prefix=/usr/local/nignx指定了nginx编译安装的位置,除了该参数外configure还支持非常多的参数,可以通过./configure --help查看。由于支持的参数太多,这里不一一列举了。并不需要记住所有的参数,只需要知道在执行./configure时,可以通过添加配置这些参数来决定configure的具体行为。

nginx的configure由shell脚本编写,执行过程中会调用/auto/目录下的脚本。

1
#options脚本定义后续工作需要用到的变量
2
#根据本次调用configure时携带的参数与默认值设置这些变量
3
. auto/options
4
#init脚本初始化后续工作需要的一些文件路径
5
. auto/init
6
#sources分析nginx源码结构,该脚本将.c/.h代码文件分类,并定义为对应的变量数组
7
. auto/sources
8
9
# 测试并创建$NGX_OBJS目录,该目录用来存储编译过程中所有目标文件的路径
10
test -d $NGX_OBJS || mkdir -p $NGX_OBJS
11
12
# 以下两个变量定义在auto/init脚本里,分别是ngx_auto_headers.h,autoconf.err文件
13
echo > $NGX_AUTO_HEADERS_H
14
echo > $NGX_AUTOCONF_ERR
15
# 向ngx_auto_headers.h文件写入命令行参数
16
echo "#define NGX_CONFIGURE \"$NGX_CONFIGURE\"" > $NGX_AUTO_CONFIG_H
17
18
#......
19
20
# 线程相关的支持设置
21
. auto/threads
22
##########################
23
# 最核心的构造部分
24
# 生成ngx_modules.c文件
25
# 定义了ngx_modules数组
26
##########################
27
. auto/modules
28
# 在链接时需要的第三方静态,动态库或者目标文件是否存在
29
. auto/lib/conf
30
31
# 创建编译时使用的objs/Makefile文件
32
. auto/make
33
# 为objs/Makefile加入需要连接的第三方静态库,动态库或者目标文件
34
. auto/lib/make
35
# 为objs/Makefile加入install功能,当执行make
36
# install时将编译生成的必要文件复制到安装路径,建立必要的目录
37
. auto/install
38
#......

configure生成的文件

configure执行成功后会生成objs目录,并在该目录下生成以下目录和文件:

1
├── autoconf.err        //保存configure执行过程中产生的结果
2
├── Makefile            //用于编译nginx工程及install参数安装nginx程序
3
├── ngx_auto_config.h   //会被src/core/ngx_config.h和
4
├── ngx_auto_headers.h  //src/os/unix/ngx_linux_config.h文件引用
5
├── ngx_modules.c       //这是一个关键文件,下边展开讲
6
└── src                 //src目录用于存放编译时产生的目标文件
7
    ├── core
8
    ├── event
9
    │   └── modules
10
    ├── http
11
    │   ├── modules
12
    │   │   └── perl
13
    │   └── v2
14
    ├── mail
15
    ├── misc
16
    ├── os
17
    │   ├── unix
18
    │   └── win32
19
    └── stream

ngx_modules.c文件

configure除了生成Makefile文件外,还生成了ngx_modules.c文件,该文件定义了ngx_modules数组,该数组指明了每个模块在nginx中的优先级,当一个请求同时符合多个模块的处理规则时,将按照ngx_modules数组中的顺序选择最靠前的模块优先处理。

1
#include <ngx_config.h>
2
#include <ngx_core.h>
3
4
extern ngx_module_t  ngx_core_module;
5
extern ngx_module_t  ngx_errlog_module;
6
//更多参数....
7
8
ngx_module_t *ngx_modules[] = {
9
    &ngx_core_module,
10
    &ngx_errlog_module,
11
    &ngx_conf_module,
12
    &ngx_regex_module,
13
    &ngx_events_module,
14
    &ngx_event_core_module,
15
    &ngx_epoll_module,
16
    &ngx_http_module,
17
    &ngx_http_core_module,
18
    &ngx_http_log_module,
19
    NULL    //省略....
20
};
21
//其它.....

nginx配置文件

nginx.conf通用配置结构:

1
...         # 全局块
2
events {    # events块
3
    ...
4
}
5
6
http {      # http块
7
    ...     # http全局块
8
    server {    # server块
9
        ...     # server全局块
10
        location [PATTERN] {    # location块
11
            ...
12
        }
13
        location [PATTERN] {
14
            ...
15
        }
16
    }
17
    server {
18
        ...
19
    }
20
    ...     # http全局块
21
}
22
...
  1. 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
  2. events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
  3. http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
  4. server块:配置虚拟主机的相关参数,一个http中可以有多个server。
  5. location块:配置请求的路由,以及各种页面的处理情况。

关于Nginx的配置文件说明参考之前写过的文章:nginx的基本使用说明。这里不重复叙述了。

nginx命令

nginx命令可以通过nginx -hnginx -?查看:

1
nginx -?
2
nginx version: nginx/1.14.2
3
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
4
5
Options:
6
  -?,-h         : this help
7
  -v            : show version and exit
8
  -V            : show version and configure options then exit
9
  -t            : test configuration and exit
10
  -T            : test configuration, dump it and exit
11
  -q            : suppress non-error messages during configuration testing
12
  -s signal     : send signal to a master process: stop, quit, reopen, reload
13
  -p prefix     : set prefix path (default: /usr/local/nginx/)
14
  -c filename   : set configuration file (default: conf/nginx.conf)
15
  -g directives : set global directives out of configuration file

nginx启动命令

  • 默认启动方式:/usr/local/nginx/sbin/nginx
  • 指定配置文件启动:/usr/local/nginx/sbin/nginx -c /tmp/nginx.conf
  • 另行指定安装目录的启动方式: nginx -p /usr/local/nginx
  • 另行指定全局配置项的启动方式: nginx -g "pid /var/nginx/test.pid;"

测试配置信息与显示查看信息

1
nginx -t        //不启动nginx的情况下,测试配置文件是否有错误
2
nginx -t -q     //测试配置选项时,-q参数屏蔽error级别以下的错误到屏幕上
3
nginx -v        //显示nginx版本信息
4
nginx -V        //显示编译阶段的参数

停止nginx

快速停止服务:

1
nginx -s stop   //快速关闭nginx
2
//也可以执行kill命令来停止
3
kill -s SIGTERM pid
4
kill -s SIGINT pid

优雅的(nginx服务正常地处理完当前所有请求在)停止服务:

1
nginx -s quit   //处理完当前所有请求在停止服务
2
kill -s SIGOUIT pid
3
//如果希望优雅的停止某个worker进程
4
kill -s SIGWINCH pid

其它

重新读取配置文件并生效

1
nginx -s reload
2
kill -s SIGHUP pid

日志回滚:

1
nginx -s reopen
2
kill -s SIGUSR1 pid

平滑升级(不重启nginx服务的情况下来完成新版本的升级):

  1. 通知正在运行的旧版本nginx准备升级: kill -s SIGUSR2 pid。运行中的nginx会将pid文件重命名。
  2. 启动新版本的nginx。
  3. 通过kill命令向旧版本的master进程发生SIGQUIT信号来优雅的关闭旧版本的nginx,随后只有新版本的nginx服务运行。

从main()函数看nginx启动过程

main()函数在src/core/nginx.c文件中,是nginx程序启动的入口函数。这一章我们只看main()函数里的一层函数,不过多的深入到内部实现,只用对nginx的流程有个大致的认识就行,避免一开始过多的深入,陷入各个嵌套的实现,导致混乱与迷茫。

ngx_get_options函数解析命令行参数,根据参数的含义,赋值对应的全局变量,后续函数通过全局变量的取值决定是否执行相应的操作。

当命令行参数是-g -c -p -s时,参数后面的信息将存储在对应的变量中,供后续程序使用。

1
if (ngx_get_options(argc, argv) != NGX_OK) {
2
    return 1;
3
}

日志初始化,这里的日志是临时日志,而非nginx.conf配置文件中的日志。该日志主要用来处理nginx主进程启动过程中的一些日志记录。

1
log = ngx_log_init(ngx_prefix);
2
if (log == NULL) {
3
    return 1;
4
}

ngx_cycle_t结构体是nginx中一个非常核心的结构体,许多功能都是该结构体的成员。这里初始化一个该类型的局部变量init_cycle并使一个全局指针ngx_cycle指向它。在将上一节初始化的临时日志赋予该结构的log成员。

1
/*
2
* init_cycle->log is required for signal handlers and
3
* ngx_process_options()
4
*/
5
ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
6
init_cycle.log = log;
7
ngx_cycle = &init_cycle;

将命令行参数保存到全局变量ngx_argv数组中。如果保存过程中有异常发送,记录到init_cycle.log日志中。

1
if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
2
    return 1;
3
}

始化ngx_cycleprefix, conf_prefix, conf_file, conf_param等字段。

1
if (ngx_process_options(&init_cycle) != NGX_OK) {
2
    return 1;
3
}

初始化操作系统相关信息,并保存到对应的全局变量中。如内存页面大小ngx_pagesize,ngx_cacheline_size,最大连接数ngx_max_sockets等。

1
if (ngx_os_init(log) != NGX_OK) {
2
    return 1;
3
}

又是一个初始化,当前还不知道是什么作用。

1
/*
2
* ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
3
*/
4
if (ngx_crc32_table_init() != NGX_OK) {
5
    return 1;
6
}

slab内存算法相关初始化,具体原理当前不清楚。

1
/*
2
* ngx_slab_sizes_init() requires ngx_pagesize set in ngx_os_init()
3
*/
4
ngx_slab_sizes_init();

这里处理平滑升级相关(不重启升级),该新master进程通过环境变量继承相关信息。

1
if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
2
    return 1;
3
}

处理configure生成的ngx_modules模块,按顺序排序赋值index和name字段。

1
if (ngx_preinit_modules() != NGX_OK) {
2
    return 1;
3
}

核心初始化部分,配置文件解析也发生在这里。

1
/* nginx核心初始化 */
2
cycle = ngx_init_cycle(&init_cycle);
3
if (cycle == NULL) {
4
    if (ngx_test_config) {
5
         ngx_log_stderr(0, "configuration file %s test failed",
6
                init_cycle.conf_file.data);
7
    }
8
    return 1;
9
}

处理nginx -s xxx命令,该功能打开配置文件,找到对应.pid文件,读取pid,调用ngx_os_signal_process()发送信号

1
if (ngx_signal) {   /* 处理-s参数相关的业务 */
2
    return ngx_signal_process(cycle, ngx_signal);
3
}

初始化信号处理相关。

1
if (ngx_init_signals(cycle->log) != NGX_OK) {
2
    return 1;
3
}
4
5
if (!ngx_inherited && ccf->daemon) {
6
    if (ngx_daemon(cycle->log) != NGX_OK) {
7
        return 1;
8
    }
9
10
    ngx_daemonized = 1;
11
}
12
13
if (ngx_inherited) {
14
    ngx_daemonized = 1;
15
}

关闭临时log日志。

1
if (log->file->fd != ngx_stderr) {
2
    if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {
3
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
4
                    ngx_close_file_n " built-in log failed");
5
    }
6
}

进入nginx.conf中配置的工作模式。

1
if (ngx_process == NGX_PROCESS_SINGLE) {
2
    ngx_single_process_cycle(cycle);
3
    } else {    /* 多进程模式 */
4
    ngx_master_process_cycle(cycle);
5
    }
6
    return 0;
7
}

至此main()函数这一层函数执行到了末尾。其中比较关键的是ngx_init_cycle()函数,这里进行了配置文件的解析过程。将放在下一节来讲,还有一个就是最后的ngx_master_process_cycle(cycle)函数,这里分配了工作进程,开始进程了主进程循环与工作进程循环,分别开始各自的工作,这部分也放在后续来讲。