if test -n "$NGX_ADDONS"; then
echo configuring additional modules
for ngx_addon_dir in $NGX_ADDONS
do
echo "adding module in $ngx_addon_dir"
ngx_module_type=
ngx_module_name=
ngx_module_incs=
ngx_module_deps=
ngx_module_srcs=
ngx_module_libs=
ngx_module_order=
ngx_module_link=ADDON
if test -f $ngx_addon_dir/config; then
. $ngx_addon_dir/config
echo " + $ngx_addon_name was configured"
else
echo "$0: error: no $ngx_addon_dir/config was found"
exit 1
fi
done
fi
概述
ngx中http有11个阶段,其中有7个阶段可以添加模块,例如NGX_HTTP_ACCESS_PHASE和NGX_HTTP_CONTENT_PHASE阶段,这两个阶段有明显的先后顺序,先执行access阶段再执行content阶段。但是相同阶段的不同模块的执行顺序是什么样的呢?和什么有关系呢?
执行阶段
解析完请求行、请求头以后就会调用ngx_http_process_request函数,该函数又调用了ngx_http_handler,ngx_http_handler又调用ngx_http_core_run_phases执行http的10个阶段,代码如下。
通过以上代码可以看处,10个阶段的执行基本就是循环执行ph这个数组中的函数指针指向的函数。执行阶段的顺序性就是ph这个数组的顺序,那么看下这个数组是什么时候生成的,他的顺序是如何的。
在解析http块的配置文件时,会调用ngx_http_block函数,该函数在解析完http块下配置后,又调用ngx_http_init_phases初始化了cmcf->phases 这11个阶段的数组,又循环调用所有http模块的postconfiguration函数指针向11个阶段添加回调函数,代码如下。然后再调用ngx_http_init_phase_handlers函数初始化ph数组,代码如下。
由上述代码可知,模块的顺序和cycle->modules数组的顺序有关,相同阶段模块的调用顺序和添加到cycle->modules数组中的顺序是相反的。
那么,cycle->modules数组是如何赋值的呢? 在初始化cycle调用ngx_init_cycle函数中调用了ngx_cycle_modules函数,该函数是把ngx_modules这个数组的模块复制了一份,代码:
ngx_memcpy(cycle->modules, ngx_modules, ngx_modules_n * sizeof(ngx_module_t *));
。这个ngx_modules数组是在objs/ngx_modules.c文件中定义的,编译的时候由./auto/configure生成的。通常情况下模块会按照上述顺序执行,但是也有可能会按照阶段跳过本阶段其余的模块跳到下个阶段。 初始化函数
ngx_http_init_phase_handlers
中ph->next = n
的赋值就是下个阶段的数组下标。有一点不同的是NGX_HTTP_ACCESS_PHASE
的模块会把NGX_HTTP_POST_ACCESS_PHASE
阶段也跳过:模块编译
官方的模块通过
./auto/configure
的with-参数开启或通过without-参数关闭。例如:--with-http_geoip_module
和--without-http_gzip_module
。 第三方模块通过./auto/configure
的--add-module指定。例如指定我github上clone下来的helloworld模块,--add-module=./github.com/vislee/ngx_http_hello_world_module
。看下编译的脚本吧,通过./auto/configure 检测编译的系统以及该系统可以支持的一些功能并生成makefile和模块文件
./objs/ngx_modules.c
。在
./auto/configure
中:会调用
./auto/options
,该文件提供了configure --help的参数,通过全局变量记录编译时的输入。并把输入的所有参数赋给NGX_CONFIGURE,生成objs/ngx_auto_config.h文件时定义成宏,在nginx -V 作为configure arguments的输出。 例如: --with-http_geoip_module 是通过HTTP_GEOIP全局变量记录,默认HTTP_GEOIP=NO。 --without-http_gzip_module 是通过HTTP_GZIP全局变量记录,默认HTTP_GZIP=YES。 --add-module= #是通过NGX_ADDONS变量记录,最终NGX_ADDONS是添加的模块的字符串,空格分割。会调用
./auto/init
,该文件中定义了一些需要生成的文件的变量,以及ngx_n和ngx_c这两个特殊的变量,这两个变量就是为了echo命令输出不换行。如:echo -n 'hello' 或者echo 'test\c' 如果不这样写输出会换行。 例如: NGX_MAKEFILE=$NGX_OBJS/Makefile NGX_MODULES_C=$NGX_OBJS/ngx_modules.c还会调用
./auto/sources
文件,该文件定义了代码文件、代码模块、代码目录相关的一些常量。 例如: CORE_MODULES="ngx_core_module ngx_errlog_module ngx_conf_module" CORE_INCS="src/core" CORE_DEPS="src/core/nginx.h \ src/core/ngx_config.h 。。。。。。执行uname 获取编译平台相关的信息。
会调用
./auto/cc/conf
文件,该文件定义了一些编译相关的变量。./auto/cc/name
文件检测编译器类型,赋值给NGX_CC_NAME。例:在mac上编译 NGX_CC_NAME=clang,在centos上NGX_CC_NAME=gcc。如果不是win系统的编译会调用
./auto/headers
检测几个头文件。例如sys/filio.h 和 limits.h。检测的结果会赋值NGX_INCLUDE_xxx=#include还会调用
./auto/os/conf
检测支持的事件驱动类型并把代码目录添加到CORE_SRCS这个变量、检测支持的一些特性如sendfile,并在ngx_auto_config.h文件定义宏。该脚本实际上又根据编译机器的操作系统类型分别调用不同的脚本。例如mac系统下会调用auto/os/darwin
,linux系统下会调用./auto/os/linux
。如果不是win32平台,还会调用
./auto/unix
脚本,该脚本检测了Linux 的group,检测了系统的一些特性,例如:TCP_FASTOPEN,如果支持该特性会定义一个宏在ngx_auto_config.h文件中。调用
./auto/threads
定义开启线程池的宏和指定线程池的代码文件。调用
auto/modules
处理./auto/options
脚本设置的变量,根据设置的变量的值添加对应的模块的代码文件到HTTP_MODULES HTTP_FILTER_MODULES HTTP_INIT_FILTER_MODULES HTTP_SRCS HTTP_INCS HTTP_DEPS 这几个变量。调用
./auto/lib/conf
检测依赖的第三方库是否安装。例如:pcre会调用auto/lib/pcre/conf
, 调用openssl库会调用auto/lib/openssl/conf
文件,zlib会调用auto/lib/zlib/conf
。然后会在ngx_auto_config.h文件中定义一系列文件路径的前缀的常量。
调用
./auto/make
,生成objs下的Makefile文件。调用
./auto/lib/make
调用
./auto/install
调用
./auto/stubs
在ngx_auto_config.h定义user和group常量宏。
综上, 其中,objs/ngx_modules.c文件是由modules数组生成的,grep下该数组是由以下数组赋值的。其中HTTP_MODULES数组是由auto/modules文件调用auto/module添加的。因此如果是添加的第三方模块,先调用--add-module=的反而后执行。
测试脚本: