Nginx 模块开发 Hello World

最近在研究如何利用 Nginx 实现高性能网关,这里记录一下开发 Nginx 扩展模块 Hello World。

编译安装 Nginx

下载 Nginx 源代码,解压,进入源代码目录:

1
2
3
wget http://nginx.org/download/nginx-1.13.10.tar.gz
tar zvxf nginx-1.13.10.tar.gz
cd nginx-1.13.10

编译,安装 Nginx 到指定目录:

1
2
3
./configure --prefix=/home/lihao/code/nginx/nginx-1.13.10/bin
make
make install

configure 命令中使用了参数 --prefix=/home/lihao/code/nginx/nginx-1.13.10/bin 是指将 Nginx 安装到目录 /home/lihao/code/nginx/nginx-1.13.10/bin

修改 /home/lihao/code/nginx/nginx-1.13.10/bin/conf/nginx.conf,调整 Nginx 监听端口为 5123

1
2
3
server {
listen 5123;
server_name localhost;

启动 Nginx:

1
2
cd /home/lihao/code/nginx/nginx-1.13.10/bin
./sbin/nginx

打开浏览器,地址栏输入:http://服务器 ip:5123/ 可以正常看到 Nginx 的欢迎页面。

开发 Nginx 模块

创建目录 ngx_http_hello_world_module,然后在该目录创建两个文件 configngx_http_hello_world_module.c

config 文件

config 文件是个 shell 文件,用于告诉 Nginx 如何编译模块源代码。在 config 文件中,检测了当前 Nginx 版本是否支持动态模块,并采取不同的添加模块的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ngx_addon_name=ngx_http_hello_world_module

if test -n "$ngx_module_link"; then
# The New Way
ngx_module_type=HTTP
ngx_module_name=ngx_http_hello_world_module
ngx_module_srcs="$ngx_addon_dir/ngx_http_hello_world_module.c"

. auto/module
else
# The Old Way
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"
fi

C 模块文件

ngx_http_hello_world_module.c 用于实现自定义模块。

首先是函数 ngx_http_hello_world 的声明,这个函数会在源代码最后进行定义。

ngx_http_hello_world_commands 是个静态数组,用于定义 Nginx 指令。这里只定义一个指令 print_hello_world,指令不需要参数,故定义为 NGX_CONF_NOARGS

ngx_http_hello_world_module_ctx 是个静态数组,用于定义函数,这些函数会在特定时刻执行,这里不需要,故都定义为 NULL

ngx_http_hello_world_module 是个静态数组,用于定义模块相关信息。

ngx_http_hello_world_handler 是模块的核心,实现在浏览器打印 Hello World!

最后是 ngx_http_hello_world 的定义,clcf->handler = ngx_http_hello_world_handler; 用于告诉 Nginx 自定义 handler ngx_http_hello_world_handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_command_t ngx_http_hello_world_commands[] = {
{
ngx_string("print_hello_world"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_hello_world,
0,
0,
NULL
},
ngx_null_command
};

static ngx_http_module_t ngx_http_hello_world_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};

ngx_module_t ngx_http_hello_world_module = {
NGX_MODULE_V1,
&ngx_http_hello_world_module_ctx,
ngx_http_hello_world_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};

static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r)
{
u_char *ngx_hello_world = (u_char *) "Hello World!";
size_t sz = strlen((const char *)ngx_hello_world);

r->headers_out.content_type.len = strlen("text/html") - 1;
r->headers_out.content_type.data = (u_char *) "text/html";
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = sz;
ngx_http_send_header(r);

ngx_buf_t *b;
ngx_chain_t *out;

b = ngx_calloc_buf(r->pool);

out = ngx_alloc_chain_link(r->pool);

out->buf = b;
out->next = NULL;

b->pos = ngx_hello_world;
b->last = ngx_hello_world + sz;
b->memory = 1;
b->last_buf = 1;

return ngx_http_output_filter(r, out);
}

static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_hello_world_handler;
return NGX_CONF_OK;
}

编译模块源代码

在 Nginx 源代码目录,执行命令:

1
2
3
./configure --prefix=/home/lihao/code/nginx/nginx-1.13.10/bin --add-dynamic-module=/home/lihao/code/nginx/ngx_http_hello_world_module
make
make install

可以看到 Nginx 安装目录下 modules 目录,增加了 ngx_http_hello_world_module.so 文件。

使用模块

修改 Nginx 安装目录下的 nginx.conf 文件,在 server 下添加:

1
2
3
location /test {
print_hello_world;
}

打开浏览器,地址栏输入:http://服务器 ip:5123/test,可以看到页面输出:Hello World!

参考资料