本节主要说明:
configure
编译脚本,nginx.conf
配置文件,nginx命令与main()
函数主流程。configure
不同的参数决定了编译出的nginx拥有哪些功能,nginx.conf
配置文件决定了这些功能具体的使用方式。nginx命令决定了如何与nginx交互,包括但不限于:以何种方式启动nginx, 停止服务, 重新读取配置文件与平滑升级等。
关于Nginx的基本使用参考之前写过的文章:nginx的基本使用说明。
学习目标
- 配置
nginx
运行环境,源码安装nginx
并正常运行。 - 了解
configure
文件的作用,如何影响nginx
程序的组成。 - 学习
nginx
的配置文件结构。 - 认识
nginx
常用命令以及这些命令在nginx
启动过程中的作用。 - 对
nginx
中main()
函数的功能有个流程上的认识,但是对细节不深究。
相关环境配置
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
。
使用了两种编辑器:
- 通过xshell/iTerm2远程 + vim 8.0.1453(部分系统自带7版本,建议更新到vim 8版本才能使用某些插件), vim插件基本配置参考文章: vim插件配置。
- 另一些时候使用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 |
|
2 |
|
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 | ... |
- 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
- events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
- http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
- server块:配置虚拟主机的相关参数,一个http中可以有多个server。
- location块:配置请求的路由,以及各种页面的处理情况。
关于Nginx的配置文件说明参考之前写过的文章:nginx的基本使用说明。这里不重复叙述了。
nginx命令
nginx命令可以通过nginx -h
或nginx -?
查看:
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服务的情况下来完成新版本的升级):
- 通知正在运行的旧版本nginx准备升级:
kill -s SIGUSR2 pid
。运行中的nginx会将pid文件重命名。 - 启动新版本的nginx。
- 通过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_cycle
的prefix
, 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)
函数,这里分配了工作进程,开始进程了主进程循环与工作进程循环,分别开始各自的工作,这部分也放在后续来讲。