《深入理解Nginx:模块开发与架构解析》一3.3 如何将自己的HTTP模块编译进Nginx

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 本节书摘来自华章出版社《深入理解Nginx:模块开发与架构解析》一书中的第3章,第3.3节,作者 陶辉,更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.3 如何将自己的HTTP模块编译进Nginx

Nginx提供了一种简单的方式将第三方的模块编译到Nginx中。首先把源代码文件全部放到一个目录下,同时在该目录中编写一个文件用于通知Nginx如何编译本模块,这个文件名必须为config。它的格式将在3.3.1节中说明。
这样,只要在configure脚本执行时加入参数--add-module=PATH(PATH就是上面我们给定的源代码、config文件的保存目录),就可以在执行正常编译安装流程时完成Nginx编译工作。
有时,Nginx提供的这种方式可能无法满足我们的需求,其实,在执行完configure脚本后Nginx会生成objs/Makefile和objs/ngx_modules.c文件,完全可以自己去修改这两个文件,这是一种更强大也复杂得多的方法,我们将在3.3.3节中说明如何直接修改它们。

3.3.1 config文件的写法

config文件其实是一个可执行的Shell脚本。如果只想开发一个HTTP模块,那么config文件中需要定义以下3个变量:
ngx_addon_name:仅在configure执行时使用,一般设置为模块名称。
HTTP_MODULES:保存所有的HTTP模块名称,每个HTTP模块间由空格符相连。在重新设置HTTP_MODULES变量时,不要直接覆盖它,因为configure调用到自定义的config脚本前,已经将各个HTTP模块设置到HTTP_MODULES 变量中了,因此,要像如下这样设置:

"$HTTP_MODULES ngx_http_mytest_module"

NGX_ADDON_SRCS:用于指定新增模块的源代码,多个待编译的源代码间以空格符相连。注意,在设置NGX_ADDON_SRCS时可以使用$ngx_addon_dir变量,它等价于configure执行时--add-module=PATH的PATH参数。
因此,对于mytest模块,可以这样编写config文件:

ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"

注意 以上3个变量并不是唯一可以在config文件中自定义的部分。如果我们不是开发HTTP模块,而是开发一个HTTP过滤模块,那么就要用HTTP_FILTER_MODULES替代上面的HTTP_MODULES变量。事实上,包括$CORE_MODULES、$EVENT_MODULES、$HTTP_MODULES、 $HTTP_FILTER_MODULES、$HTTP_HEADERS_FILTER_MODULE等模块变量都可以重定义,它们分别对应着Nginx的核心模块、事件模块、HTTP模块、HTTP过滤模块、HTTP头部过滤模块。除了NGX_ADDON_SRCS变量,或许还有一个变量我们会用到,即$NGX_ADDON_DEPS变量,它指定了模块依赖的路径,同样可以在config中设置。

3.3.2 利用configure脚本将定制的模块加入到Nginx中

在1.6节提到的configure执行流程中,其中有两行脚本负责将第三方模块加入到Nginx中,如下所示。

. auto/modules
. auto/make

下面完整地解释一下configure脚本是如何与3.3.1节中提到的config文件配合起来把定制的第三方模块加入到Nginx中的。
在执行configure --add-module=PATH命令时,PATH就是第三方模块所在的路径。在configure中,通过auto/options脚本设置了NGX_ADDONS变量:

--add-module=*)                  NGX_ADDONS="$NGX_ADDONS $value" ;;

在configure命令执行到auto/modules脚本时,将在生成的ngx_modules.c文件中加入定制的第三方模块。

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"

        if test -f $ngx_addon_dir/config; then
            #在这里执行自定义的config脚本
            . $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_ADDONS可以包含多个目录,对于每个目录,如果其中存在config文件就会执行,也就是说,在config中重新定义的变量都会生效。之后,auto/modules脚本开始创建ngx_modules.c文件,这个文件的关键点就是定义了ngx_module_t *ngx_modules[]数组,这个数组存储了Nginx中的所有模块。Nginx在初始化、处理请求时,都会循环访问ngx_modules数组,确定该用哪一个模块来处理。下面来看一下auto/modules是如何生成数组的,代码如下所示:

modules="$CORE_MODULES $EVENT_MODULES"

if [ $USE_OPENSSL = YES ]; then
    modules="$modules $OPENSSL_MODULE"
    CORE_DEPS="$CORE_DEPS $OPENSSL_DEPS"
    CORE_SRCS="$CORE_SRCS $OPENSSL_SRCS"
fi

if [ $HTTP = YES ]; then
    modules="$modules $HTTP_MODULES $HTTP_FILTER_MODULES \
             $HTTP_HEADERS_FILTER_MODULE \
             $HTTP_AUX_FILTER_MODULES \
             $HTTP_COPY_FILTER_MODULE \
             $HTTP_RANGE_BODY_FILTER_MODULE \
             $HTTP_NOT_MODIFIED_FILTER_MODULE"

    NGX_ADDON_DEPS="$NGX_ADDON_DEPS \$(HTTP_DEPS)"
fi

首先,auto/modules会按顺序生成modules变量。注意,这里的$HTTP_MODULES等已经在config文件中重定义了。这时,modules变量是包含所有模块的。然后,开始生成ngx_modules.c文件:

cat << END                                    > $NGX_MODULES_C

#include <ngx_config.h>
#include <ngx_core.h>

$NGX_PRAGMA

END

for mod in $modules
do
    echo "extern ngx_module_t  $mod;"         >> $NGX_MODULES_C
done

echo                                          >> $NGX_MODULES_C
echo 'ngx_module_t *ngx_modules[] = {'        >> $NGX_MODULES_C

for mod in $modules
do
    #向ngx_modules数组里添加Nginx模块
    echo "    &$mod,"                         >> $NGX_MODULES_C
done

cat << END                                    >> $NGX_MODULES_C
    NULL
};

END

这样就已经确定了Nginx在运行时会调用自定义的模块,而auto/make脚本负责把相关模块编译进Nginx。
在Makefile中生成编译第三方模块的源代码如下:

if test -n "$NGX_ADDON_SRCS"; then

    ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"

    for ngx_src in $NGX_ADDON_SRCS
    do
        ngx_obj="addon/`basename \`dirname $ngx_src\``"

        ngx_obj=`echo $ngx_obj/\`basename $ngx_src\` \
            | sed -e "s/\//$ngx_regex_dirsep/g"`

        ngx_obj=`echo $ngx_obj \
            | sed -e 
              "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
                  -e 
              "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
                  -e 
              "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
                  -e 
              "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`

        ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`

        cat << END                                            >> $NGX_MAKEFILE

$ngx_obj:    \$(ADDON_DEPS)$ngx_cont$ngx_src
    $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX

END
     done

fi

下面这段代码用于将各个模块的目标文件设置到ngx_obj变量中,紧接着会生成Makefile里的链接代码,并将所有的目标文件、库文件链接成二进制程序。

for ngx_src in $NGX_ADDON_SRCS
do
    ngx_obj="addon/`basename \`dirname $ngx_src\``"

    test -d $NGX_OBJS/$ngx_obj || mkdir -p $NGX_OBJS/$ngx_obj

    ngx_obj=`echo $ngx_obj/\`basename $ngx_src\` \
        | sed -e "s/\//$ngx_regex_dirsep/g"`

    ngx_all_srcs="$ngx_all_srcs $ngx_obj"
done

…

cat << END                                                    >> $NGX_MAKEFILE

$NGX_OBJS${ngx_dirsep}nginx${ngx_binext}:    
    $ngx_deps$ngx_spacer \$(LINK) 
    ${ngx_long_start}${ngx_binout}$NGX_OBJS${ngx_dirsep}nginx$ngx_long_cont$ngx_objs$ngx_libs$ngx_link
    $ngx_rcc
${ngx_long_end}
END

综上可知,第三方模块就是这样嵌入到Nginx程序中的。

3.3.3 直接修改Makefile文件

3.3.2节中介绍的方法毫无疑问是最方便的,因为大量的工作已由Nginx中的configure脚本帮我们做好了。在使用其他第三方模块时,一般也推荐使用该方法。
我们有时可能需要更灵活的方式,比如重新决定ngx_module_t *ngx_modules[]数组中各个模块的顺序,或者在编译源代码时需要加入一些独特的编译选项,那么可以在执行完configure后,对生成的objs/ngx_modules.c和objs/Makefile文件直接进行修改。
在修改objs/ngx_modules.c时,首先要添加新增的第三方模块的声明,如下所示。

extern ngx_module_t  ngx_http_mytest_module;

其次,在合适的地方将模块加入到ngx_modules数组中。

ngx_module_t *ngx_modules[] = {
  …
  &ngx_http_upstream_ip_hash_module,
  &ngx_http_mytest_module,
  &ngx_http_write_filter_module,
    …
    NULL
};

注意,模块的顺序很重要。如果同时有两个模块表示对同一个请求感兴趣,那么只有顺序在前的模块会被调用。
修改objs/Makefile时需要增加编译源代码的部分,例如:

objs/addon/httpmodule/ngx_http_mytest_module.o: $(ADDON_DEPS) \
        ../sample/httpmodule//ngx_http_mytest_module.c
        $(CC) -c $(CFLAGS)  $(ALL_INCS) \
                -o objs/addon/httpmodule/ngx_http_mytest_module.o \
                ../sample/httpmodule//ngx_http_mytest_module.c

还需要把目标文件链接到Nginx中,例如:

objs/nginx:     objs/src/core/nginx.o \
...
     objs/addon/httpmodule/ngx_http_mytest_module.o \
  objs/ngx_modules.o

    $(LINK) -o objs/nginx \
    objs/src/core/nginx.o \
    ...
    objs/addon/httpmodule/ngx_http_mytest_module.o \
  objs/ngx_modules.o \
  -lpthread -lcrypt -lpcre -lcrypto -lcrypto -lz

请慎用这种直接修改Makefile和ngx_modules.c的方法,不正确的修改可能导致Nginx工作不正常。

相关文章
|
2月前
|
安全 网络协议 应用服务中间件
AJP Connector:深入解析及在Apache HTTP Server中的应用
【9月更文挑战第6天】在Java Web应用开发中,Tomcat作为广泛使用的Servlet容器,经常与Apache HTTP Server结合使用,以提供高效、稳定的Web服务。而AJP Connector(Apache JServ Protocol Connector)作为连接Tomcat和Apache HTTP Server的重要桥梁,扮演着至关重要的角色
75 2
|
2月前
|
Shell
HTTP状态码解析:在Haskell中判断响应成功与否
HTTP状态码解析:在Haskell中判断响应成功与否
|
21天前
|
负载均衡 应用服务中间件 nginx
基于Nginx和Consul构建自动发现的Docker服务架构——非常之详细
通过使用Nginx和Consul构建自动发现的Docker服务架构,可以显著提高服务的可用性、扩展性和管理效率。Consul实现了服务的自动注册与发现,而Nginx则通过动态配置实现了高效的反向代理与负载均衡。这种架构非常适合需要高可用性和弹性扩展的分布式系统。
24 4
|
22天前
|
负载均衡 应用服务中间件 nginx
基于Nginx和Consul构建自动发现的Docker服务架构——非常之详细
通过使用Nginx和Consul构建自动发现的Docker服务架构,可以显著提高服务的可用性、扩展性和管理效率。Consul实现了服务的自动注册与发现,而Nginx则通过动态配置实现了高效的反向代理与负载均衡。这种架构非常适合需要高可用性和弹性扩展的分布式系统。
30 3
|
24天前
|
弹性计算 安全 API
HTTP 405 Method Not Allowed:解析与解决
本文详细解析了HTTP 405 &quot;Method Not Allowed&quot; 错误,包括其定义、常见原因、示例代码及解决方案。通过检查API文档、修改请求方法或更新服务器配置,可有效解决此错误,提升Web开发效率。
|
2月前
|
应用服务中间件 nginx Docker
Docker镜像-基于DockerFile制作编译版nginx镜像
这篇文章介绍了如何基于Dockerfile制作一个编译版的nginx镜像,并提供了详细的步骤和命令。
415 17
Docker镜像-基于DockerFile制作编译版nginx镜像
http数据包抓包解析
http数据包抓包解析
|
1月前
|
缓存 前端开发 安全
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
106 0
|
2月前
|
前端开发 JavaScript 安全
深入解析 http 协议
HTTP(超文本传输协议)不仅用于传输文本,还支持图片、音频和视频等多种类型的数据。当前广泛使用的版本为 HTTP/1.1。HTTPS 可视为 HTTP 的安全增强版,主要区别在于添加了加密层。HTTP 请求和响应均遵循固定格式,包括请求行/状态行、请求/响应头、空行及消息主体。URL(统一资源定位符)用于标识网络上的资源,其格式包含协议、域名、路径等信息。此外,HTTP 报头提供了附加信息,帮助客户端和服务端更好地处理请求与响应。状态码则用于指示请求结果,如 200 表示成功,404 表示未找到,500 表示服务器内部错误等。
61 0
深入解析 http 协议
|
2月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
67 3

热门文章

最新文章

推荐镜像

更多