Nginx 允许引入第三方模块来扩展 Nginx 的功能。官方网站 NGINX 3rd Party Modules 列出了 Nginx 很多的第三方模块。除此之外,很多很有用的模块也能在 github 等网站上找到。
添加模块
接下来通过添加 njs 模块为例来介绍如何添加第三方模块。njs 是 Nginx + JavaScript 的缩写,简单来说,就是 Nginx 里面可以运行 JavaScript,用 JavaScript 来构建动态的 Web 应用。Nginx NJS 包含两个 Nginx 扩展模块:ngx_http_js_module 和 ngx_stream_js_module。
首先克隆 njs 模块的代码:
git clone https://github.com/nginx/njs.git
查看当前已安装的 Nginx 的编译信息:
[root@nginx-plus1 nginx-1.14.2]# /usr/local/nginx/sbin/nginx -V nginx version: nginx/1.14.2 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled configure arguments: --with-debug --with-stream --prefix=/usr/local/nginx --with-http_ssl_module
进入 Nginx 源代码目录,执行 ./configure,在最后通过 --add-module 指令加上 njs 模块。执行完成后会在当前目录生成编译所需的相关文件(例如 Makefile 和 objs 目录等)。
cd /root/nginx-1.14.2 # 复制前面nginx -V看到的参数,在最后--add-module加上njs模块的路径 ./configure --with-debug --with-stream --prefix=/usr/local/nginx --with-http_ssl_module --add-module=/root/njs/nginx
执行完上面命令后,objs 目录下的 ngx_modules.c 文件中的 ngx_modules 数组中可以看到新增的 njs 模块:
cat /root/nginx-1.14.2/objs/ngx_modules.c
make
编译完成后注意不需要执行 make install 命令,只需要将生成的 Nginx 二进制文件覆盖原先的二进制文件即可。
cp /root/nginx-1.14.2/objs/nginx /usr/local/nginx/sbin/nginx -f
现在展示如何使用 njs 模块的功能。在 nginx 的 conf 目录下新建 js 目录,用于存放 javascript 代码。在 js 目录下创建 http.js 文件,内容如下:
function summary(r) { var a, s, h; s = "JS summary\n\n"; //打印请求方法,HTTP版本,Host,客户端地址,URI s += "Method: " + r.method + "\n"; s += "HTTP version: " + r.httpVersion + "\n"; s += "Host: " + r.headersIn.host + "\n"; s += "Remote Address: " + r.remoteAddress + "\n"; s += "URI: " + r.uri + "\n"; //打印请求头 s += "Headers:\n"; for (h in r.headersIn) { s += " header '" + h + "' is '" + r.headersIn[h] + "'\n"; } //打印参数内容 s += "Args:\n"; for (a in r.args) { s += " arg '" + a + "' is '" + r.args[a] + "'\n"; } return s; } function baz(r) { //设置响应状态码 r.status = 200; //设置响应头 r.headersOut.foo = 1234; r.headersOut['Content-Type'] = "text/plain; charset=utf-8"; r.headersOut['Content-Length'] = 15; r.sendHeader(); //设置响应内容 r.send("nginx"); r.send("java"); r.send("script"); r.finish(); } //export default用于导出常量、函数、文件、模块等 export default {foo, summary, baz, hello};
编写 Nginx 配置文件 /usr/local/nginx/conf/nginx.conf:
events {} http { #导入js文件 js_import js/http.js; #设置变量,变量值为调用js文件的相应函数的返回值 js_set $foo http.foo; js_set $summary http.summary; server { listen 8000; location / { add_header X-Foo $foo; #将变量foo的结果添加到响应头中 js_content http.baz; #执行其中 JS 内容并输出 } location = /summary { return 200 $summary; #返回变量summary的结果 } } }
启动 Nginx:
/usr/local/nginx/sbin/nginx
客户端请求 Nginx:
[root@nginx-plus1 ~]# curl localhost:8000 -i HTTP/1.1 200 OK Server: nginx/1.14.2 Date: Tue, 15 Jun 2021 14:04:39 GMT Content-Type: text/plain; charset=utf-8 Connection: keep-alive foo: 1234 Content-Length: 15 X-Foo: foo nginxjavascript [root@nginx-plus1 ~]# curl localhost:8000/summary?name=chengzw JS summary Method: GET HTTP version: 1.1 Host: localhost:8000 Remote Address: 127.0.0.1 URI: /summary Headers: header 'User-Agent' is 'curl/7.29.0' header 'Host' is 'localhost:8000' header 'Accept' is '*/*' Args: arg 'name' is 'chengzw'
动态模块
Nginx 在 Nginx 1.9.11(release at 2016-02-09) 版本中新增了动态模块(Dynamic Module) 的支持。动态模块在第一次通过 ./configure --add-dynamic-module 编译后,之后如果要对动态模块进行升级,只需要重新编译动态模块,然后替换在 modules 目录内该动态模块的 .so
文件即可,无需替换 Nginx 二进制文件。
下面还是以 njs 模块的例子来演示如何添加动态模块。使用 --add-dynamic-module 指令以动态模块的方式添加 njs 模块:
./configure --with-debug --with-stream --prefix=/usr/local/nginx --with-http_ssl_module --add-dynamic-module=/root/njs/nginx # 编译 make
编译完成后注意不需要执行 make install 命令,只需要将生成的 Nginx 二进制文件覆盖原先的二进制文件即可。
cp /root/nginx-1.14.2/objs/nginx /usr/local/nginx/sbin/nginx -f
如果是使用 make install 命令安装,会自动在 Nginx 运行目录下创建 modules 目录,这里我们需要手动创建 modules 目录:
mkdir /usr/local/nginx/modules
查看 modules 目录,可以看到有 http 和 stream 两个 njs 模块对应的文件:
[root@nginx-plus1 nginx]# ll /usr/local/nginx/modules total 7392 -rwxr-xr-x 1 root root 3825792 Jun 11 08:03 ngx_http_js_module.so -rwxr-xr-x 1 root root 3738944 Jun 11 08:03 ngx_stream_js_module.so
使用 load_module 指令加载动态模块,注意 load_module 指令必须在所有 block (包括 events、http、stream、mail)之前使用:
# 加载动态模块 load_module modules/ngx_http_js_module.so; load_module modules/ngx_stream_js_module.so; events {} http { #导入js文件 js_import js/http.js; #设置变量,变量值为调用js文件的相应函数的返回值 js_set $foo http.foo; js_set $summary http.summary; server { listen 8000; location / { add_header X-Foo $foo; #将变量foo的结果添加到响应头中 js_content http.baz; #执行其中 JS 内容并输出 } location = /summary { return 200 $summary; #返回变量summary的结果 } } }
自定义 HTTP 模块
我们定义一个简单的 HTTP 模块,当客户端发送请求时,返回 Hello World!
。
模块开发有以下步骤:
- 1.编写模块基本结构。包括模块配置项,模块上下文,模块配置信息。
- 2.实现 handler 的挂载函数。根据模块的需求选择正确的挂载方式。
- 3.编写 handler 处理函数。模块的功能主要通过这个函数来完成。
- 4.编写编译的文件 config。
定义模块配置项
模块配置项 ngx_command_s 结构体定义在 src/core/ngx_conf_file.h 文件中,其结构如下所示:
struct ngx_command_s { // 配置项的名称,例如gzip ngx_str_t name; // 配置项类型,type将指定配置项可以出现的位置。例如出现在location{}或者server{}中, // 以及它可以携带的参数个数 ngx_uint_t type; // 出现了name中定义的配置项后,将会调用该方法处理配置项的参数 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //该字段被NGX_HTTP_MODULE类型模块所用 (我们编写的基本上都是NGX_HTTP_MOUDLE,只有一些nginx核心模块是非NGX_HTTP_MODULE), // 该字段指定当前配置项存储的内存位置。实际上是使用哪个内存池的问题。因为http模块对所有http模块所要保存的配置信息, // 划分了main, server和location三个地方进行存储,每个地方都有一个内存池用来分配存储这些信息的内存。 // 这里可能的值为 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。当然也可以直接置为0,就是NGX_HTTP_MAIN_CONF_OFFSET。 ngx_uint_t conf; //指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。因为对于配置信息的存储,一般我们都是定义个结构体来存储的。 // 那么比如我们定义了一个结构体A,该项配置的值需要存储到该结构体的b字段。那么在这里就可以填写为offsetof(A, b)。 // 对于有些配置项,它的值不需要保存或者是需要保存到更为复杂的结构中时,这里可以设置为0。 ngx_uint_t offset; // 该字段存储一个指针。可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理。 // 大多数时候,都不需要,所以简单地设为NULL即可。 void *post; };
我们定义了一个配置项,该配置项可以出现在 location 配置块中,该配置项没有参数。当在某个配置块中出现 mytest 配置时,Nginx 将会调用 ngx_http_mytest 方法。
/* 定义模块配置项 */ static ngx_command_t ngx_http_mytest_commands[] = { { ngx_string("mytest"), NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS, // 当在某个配置块中出现 mytest 配置时,Nginx 将会调用 ngx_http_mytest 方法。 ngx_http_mytest, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command //在ngx_http_hello_commands这个数组定义的最后,都要加一个ngx_null_command作为结尾。 };
定义模块上下文
模块上下文 ngx_http_module_t 结构体定义在 src/http/ngx_http_config.h 文件中。这是一个 ngx_http_module_t 类型的静态变量,这个变量实际上是提供一组回调函数指针,这些函数将被 Nginx 在合适的时间进行调用。其结构如下所示:
typedef struct { // 解析配置文件前调用 ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 完成配置文件的解析后调用 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 当需要创建数据结构用于存储main级别(直属于http{...}块的配置项)的全局配置项时, // 可以通过create_main_conf回调方法创建存储全局配置项的结构体 void *(*create_main_conf)(ngx_conf_t *cf); // 常用于初始化main级别配置项 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); // 当需要创建数据结构用于存储srv级别(直属于server{...}块的配置项)的配置项时, // 可以通过create_srv_conf回调方法创建存储srv级别配置项的结构体 void *(*create_srv_conf)(ngx_conf_t *cf); // 主要用于合并main级别和srv级别下同名的配置项 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); // 当需要创建数据结构用于存储loc级别(直属于location{...}块的配置项)的配置项时, // 可以通过create_loc_conf回调方法创建存储loc级别配置项的结构体 void *(*create_loc_conf)(ngx_conf_t *cf); // 主要用于合并srv级别和loc级别下同名的配置项 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;
HTTP 框架在读取,重载配置文件时定义了由 ngx_http_module_t 接口描述的 8 个阶段,HTTP 框架在启动过程中会在每个阶段中调用 ngx_http_module_t 中相应的方法。当然,如果 ngx_http_module_t 中的某个回调方法设置为 NULL 空指针时,那么 HTTP 框架是不会调用它的。
本例中我们没有实现 HTTP 框架初始化时会调用的 ngx_http_module_t 中的 8 个方法。
/* 定义模块上下文 */ static ngx_http_module_t ngx_http_mytest_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ };
定义模块
对于开发一个模块来说,我们都需要定义一个 ngx_module_t 类型的变量来说明这个模块本身的信息,从某种意义上来说,这是这个模块最重要的一个信息,它告诉了 Nginx 这个模块的一些信息,上面定义的配置信息,还有模块上下文信息,都是通过这个结构来告诉 Nginx 系统的,也就是加载模块的上层代码,都需要通过定义的这个结构,来获取这些信息。
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1 #define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0 // src/core/ngx_core.h typedef struct ngx_module_s ngx_module_t; // src/core/ngx_conf_file.h struct ngx_module_s { // 下面的 ctx_index,index,spare0,spare1,spare2,spare3,version 变量不需要在定义时赋值, // 可以用 Nginx 准备好的宏 NGX_MODULE_V1 来定义,它已经定义好了这7个值 ngx_uint_t ctx_index; ngx_uint_t index; ngx_uint_t spare0; ngx_uint_t spare1; ngx_uint_t spare2; ngx_uint_t spare3; ngx_uint_t version; // 指向模块的上下文结构体 void *ctx; // 模块配置项 ngx_command_t *commands; // 模块的类型,它与ctx指针是紧密相关的。在官方Nginx中,它的取值范围是以下5种: // NGINX_HTTP_MODULE,NGINX_CORE_MODULE,NGINX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MAIL_MODULE ngx_uint_t type; // 在Nginx启动、停止的过程中,以下7个函数指针表示有7个执行点会分别调用这7种方法。 // 如果不需要在Nginx启动或者停止的过程中执行它,就简单设置为NULL即可。 ngx_int_t (*init_master)(ngx_log_t *log); ngx_int_t (*init_module)(ngx_cycle_t *cycle); ngx_int_t (*init_process)(ngx_cycle_t *cycle); ngx_int_t (*init_thread)(ngx_cycle_t *cycle); void (*exit_thread)(ngx_cycle_t *cycle); void (*exit_process)(ngx_cycle_t *cycle); void (*exit_master)(ngx_cycle_t *cycle); // 以下8个字段为保留字段,目前没有使用,可以使用Nginx提供的NGX_MODULE_V1_PADDING 宏来填充。 uintptr_t spare_hook0; uintptr_t spare_hook1; uintptr_t spare_hook2; uintptr_t spare_hook3; uintptr_t spare_hook4; uintptr_t spare_hook5; uintptr_t spare_hook6; uintptr_t spare_hook7; };
定义 mytest 模块,这样 mytest 模块在编译时将会被加入到 ngx_modules 全局数组中。Nginx 在启动时,会调用所有模块的初始化回调方法,这个例子中我们没有实现它们。
/* 定义模块 */ ngx_module_t ngx_http_mytest_module = { NGX_MODULE_V1, // 模块上下文 &ngx_http_mytest_module_ctx, /* module context */ // 模块配置项 ngx_http_mytest_commands, /* module directives */ // 模块类型 NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING };
模块可以提供一些回调函数给 Nginx,当 Nginx 在创建进程线程或者结束进程线程时进行调用。但大多数模块在这些时刻并不需要做什么,所以都简单赋值为 NULL。
定义 handler 挂载函数
在 Nginx 的 ngx_http_phases 中定义了 HTTP 框架对请求进行处理的 11 个阶段,结构体定义在 src/http/ngx_http_core_module.h :
typedef enum { // 1.在接收到完整的HTTP头部后读取请求内容的阶段 NGX_HTTP_POST_READ_PHASE = 0, // 2.在将请求的URI与location表达式匹配之前,修改请求的URI的阶段(所谓的重定向) NGX_HTTP_SERVER_REWRITE_PHASE, // 3.根据请求的URI寻找匹配的location表达式, // 这个阶段只能由ngx_http_core_module模块实现,不建议其他HTTP模块重新定义这一阶段的行为 NGX_HTTP_FIND_CONFIG_PHASE, // 4.寻找到匹配的location之后再次利用rewrite修改请求的URI NGX_HTTP_REWRITE_PHASE, // 5.这一阶段是用于在rewrite重写URI后,防止错误的nginx.conf配置导致死循环(递归地修改URI) // 因此,这一阶段仅由ngx_http_core_module模块处理。目前,控制死循环的方式很简单,首先检查rewrite的次数, // 如果一个请求超过10次重定向,就认为进入了死循环,这时候就会向用户返回500,表示服务器内部错误 NGX_HTTP_POST_REWRITE_PHASE, // 6.决定请求访问权限之前,HTTP模块可以介入处理的阶段 NGX_HTTP_PREACCESS_PHASE, // 7.这个阶段用于让HTTP模块判断是否允许这个请求访问Nginx服务器 NGX_HTTP_ACCESS_PHASE, // 8.在NGX_HTTP_ACCESS_PHASE阶段中,当HTTP模块的handler处理函数返回不允许访问的错误代码时(实际就是NGX_HTTP_FORBIDDEN或者NGX_HTTP_UNAUTHORIZED), // 这里将负责向用户发送拒绝服务的错误响应,因此,这个阶段实际上是用于给NGX_HTTP_ACCESS_PHASE阶段收尾的 NGX_HTTP_POST_ACCESS_PHASE, // 9.这个阶段完全是为了给try_files配置项而设立的,当Nginx请求静态文件资源时, // try_files配置项可以使这个请求顺序地访问多个静态文件资源,如果某一次访问失败,则继续访问try_files中指定的下一个静态资源。 NGX_HTTP_TRY_FILES_PHASE, // 10.处理HTTP请求内容的阶段,这是大部分HTTP模块最愿意介入的阶段 NGX_HTTP_CONTENT_PHASE, // 11.处理完请求后记录日志的阶段 NGX_HTTP_LOG_PHASE } ngx_http_phases;
一般情况下,我们自定义的模块,大多数是挂载在 NGX_HTTP_CONTENT_PHASE 阶段的,默认也就是属于这个模块。
有几个阶段是特例,它们没有 Hook 挂载点(也就意味着,在这几个阶段不允许挂载任何第三方处理逻辑),它们仅由 HTTP 框架实现:
- NGX_HTTP_FIND_CONFIG_PHASE
- NGX_HTTP_POST_ACCESS_PHASE
- NGX_HTTP_POST_REWRITE_PHASE
- NGX_HTTP_TRY_FILES_PHASE
函数的挂载分为两种方式:一种方式就是按处理阶段挂载,以这种方式挂载的 handler 被称为 content phase handlers;另外一种挂载方式就是按需挂载,以这种方式挂载的 handler 被称为 content handler。
按处理阶段挂载
按处理阶段挂载的动作一般是在模块上下文调用的 postconfiguration 函数中。本例介绍按需挂载,这里不展开说明。
按需挂载
当一个请求进来以后,Nginx 从 NGX_HTTP_POST_READ_PHASE 阶段开始依次执行每个阶段中所有 handler。执行到 NGX_HTTP_CONTENT_PHASE 阶段的时候,如果这个 location 有一个对应的 content handler 模块,那么就去执行这个 content handler 模块真正的处理函数。否则继续依次执行 NGX_HTTP_CONTENT_PHASE 阶段中所有 content phase handlers,直到某个函数处理返回 NGX_OK 或者 NGX_ERROR。
换句话说,当某个 location 处理到 NGX_HTTP_CONTENT_PHASE 阶段时,如果有 content handler 模块,那么 NGX_HTTP_CONTENT_PHASE 挂载的所有 content phase handlers 都不会被执行了。
但是使用这个方法挂载上去的 handler 有一个特点是必须在 NGX_HTTP_CONTENT_PHASE 阶段才能执行到。如果你想自己的 handler 在更早的阶段执行,那就不要使用这种挂载方式。
那么在什么情况会使用这种方式来挂载呢?一般情况下,某个模块对某个 location 进行了处理以后,发现符合自己处理的逻辑,而且也没有必要再调用 NGX_HTTP_CONTENT_PHASE 阶段的其它 handler 进行处理的时候,就动态挂载上这个 handler。
本例我们使用按需挂载的方式。当 Nginx 接收完 HTTP 请求的头部信息时,就会调用 HTTP 框架处理请求。在 ngx_http_mytest 方法中,我们定义了请求的处理方法为 ngx_http_mytest_handler,
/* 按需挂载,ngx_http_mytest_handler方法是真正处理请求的方法 */ static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; //首先找到mytest配置项所属的配置块,clcf貌似是location块内的数据 //结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个 //http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果 //请求的主机域名、URI与mytest配置项所在的配置块相匹配,就将调用我们 //实现的ngx_http_mytest_handler方法处理这个请求 clcf->handler = ngx_http_mytest_handler; return NGX_CONF_OK; }
定义 handler 处理函数
当出现 mytest 配置项时,ngx_http_mytest 方法会被调用,这时将 ngx_http_core_loc_conf_t 结构的 handler 成员指定为 ngx_http_mytest_handler,另外,HTTP 框架在接收完 HTTP 请求的头部后,会调用 handler 挂载函数指向的 handler 处理函数。下面看一下 handler 成员的原型 ngx_http_handler_pr:
// src/http/ngx_http_request.h typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
从上面这段代码可以看出,实际处理请求的方法 ngx_http_mytest_handler 在接收一个 ngx_http_request_t 类型的参数 r,返回一个 ngx_int_t 类型的结果。
/* 定义handler处理函数,是真正处理请求的方法 */ static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) { //必须是GET或者HEAD方法,否则返回405 Not Allowed if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } //丢弃请求中的包体 ngx_int_t rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } //设置返回的Content-Type。注意,ngx_str_t有一个很方便的初始化宏 //ngx_string,它可以把ngx_str_t的data和len成员都设置好 ngx_str_t type = ngx_string("text/plain"); //返回的包体内容 ngx_str_t response = ngx_string("Hello World!"); //设置返回状态码 r->headers_out.status = NGX_HTTP_OK; //响应包是有包体内容的,所以需要设置Content-Length长度 r->headers_out.content_length_n = response.len; //设置Content-Type r->headers_out.content_type = type; //发送http头部 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } //构造ngx_buf_t结构准备发送包体 ngx_buf_t *b; b = ngx_create_temp_buf(r->pool, response.len); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //将Hello World拷贝到ngx_buf_t指向的内存中 ngx_memcpy(b->pos, response.data, response.len); //注意,一定要设置好last指针 b->last = b->pos + response.len; //声明这是最后一块缓冲区 b->last_buf = 1; //构造发送时的ngx_chain_t结构体 ngx_chain_t out; //赋值ngx_buf_t out.buf = b; //设置next为NULL out.next = NULL; //最后一步发送包体,http框架会调用ngx_http_finalize_request方法 //结束请求 return ngx_http_output_filter(r, &out); }
定义 config 文件
config 文件其实就是一个可执行的 Shell 脚本。如果只想开发一个 HTTP 模块,那么 config 文件中需要定义以下 3 个变量:
- ngx_addon_name:仅在configure 执行时使用,一般设置为模块名称。
- HTTP_MODULES:保存所有的 HTTP 模块名称,每个 HTTP 模块间由空格符相连。在重新设置 HTTP_MODULES 变量时,不要直接覆盖它,因为 configure 调用到自定义的 config 脚本前,已经将各个 HTTP 模块设置到 HTTP_MODULES 变量中了。
- NGX_ADDON_SRCS:用于指定新增模块的源代码,多个待编译的源代码间以空格符相连。注意:在设置 NGX_ADDON_SRCS 时可以使用
$ngx_addon_dir
变量,它等价于 configure 时执行 --add-module=PATH 的 PATH 参数。
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"
编译和使用模块
将模块源代码文件 ngx_http_mytest_module.c 和 config 文件放在以下目录中:
[root@nginx-plus1 ~]# ll /root/mymodules/mytest/ total 12 -rw-r--r-- 1 root root 164 Jun 16 10:20 config -rw-r--r-- 1 root root 4567 Jun 16 10:45 ngx_http_mytest_module.c
编译时使用 --add-module 指令添加我们自定义的模块,这里使用 make install 重新安装 Nginx:
cd /root/nginx-1.14.2/ ./configure --prefix=/usr/local/nginx --add-module=/root/mymodules/mytest # 删除原先的Nginx文件 rm -rm /usr/local/nginx # 编译 make # 安装 make install
编辑 Nginx 配置文件 /usr/local/nginx/conf/nginx.conf:
worker_processes 1; events { worker_connections 1024; } http { server { listen 80; location / { mytest; #使用我们自定义的配置项 } } }
启动 Nginx:
/usr/local/nginx/sbin/nginx
客户端请求,可以看到响应了我们自定义的内容:
[root@nginx-plus1 ~]# curl localhost Hello World!
参考链接
- http://tengine.taobao.org/book/chapter_03.html
- https://tangocc.github.io/2018/07/05/nginx-modul-myself/
- 深入理解 Nginx 模块开发与结构解析