nginx模块开发入门(三)-2 Components of an Nginx Module

本文介绍了Nginx模块的组成及其配置方法,包括模块配置结构体、指令定义及其实现细节等内容,帮助读者理解Nginx模块的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[size=x-large]2. Components of an Nginx Module[/size]
[size=x-large]2.Nginx模块的组成[/size]
我说过,Nginx模块的构建是很灵活的。这一节讲描述的东西会经常出现。它可以帮助你理解模块,也可以作为开发模块的手册。

提示:本节讨论的module源文件:[url]http://www.evanmiller.org/nginx/ngx_http_circle_gif_module.c.txt[/url]

[size=large]2.1. 模块配置Struct(s)[/size]

模块的配置struct有三种,分别是main,server和location。绝大多数模块仅需要一个location配置。
名称约定如下:[color=blue]ngx_http_<module name>_(main|srv|loc)_conf_t[/color]. 例子:

typedef struct {
u_int max_radius;
u_int min_radius;
u_int step_radius;
unsigned char** circle_templates;
size_t* circle_sizes;
ngx_flag_t enable;
} ngx_http_circle_gif_loc_conf_t;


注意到上面展示了Nginx的一些特殊类型:(ngx_uint_t 和 ngx_flag_t); 这些只是基本类型的别名而已。(如果想知道具体是什么的别名,可以参考 [url=http://lxr.evanmiller.org/http/source/core/ngx_config.h#L79]core/ngx_config.h[/url] ). 这些类型用在配置结构体中的情形很多。

[size=large]2.2. 模块指令(Module Directives)[/size]

模块的指令是定义在一个叫做[color=blue]ngx_command_t[/color]的静态数组中的。下面举个我自己写的小模块中的例子,来告诉你模块指令是如何声明的:

static ngx_command_t ngx_http_circle_gif_commands[] = {
{ ngx_string("circle_gif"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_circle_gif,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },

{ ngx_string("circle_gif_min_radius"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),
NULL },
...
ngx_null_command
};


下面是结构体[color=blue]ngx_command_t[/color](静态数组里的每一个元素)的定义 , 你可以在 [url=http://lxr.evanmiller.org/http/source/core/ngx_conf_file.h#L77]core/ngx_conf_file.h[/url]找到它:

struct ngx_command_t {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};

结构体成员是多了点,不过各司其职,都有用处。

1)结构体成员 [color=blue]name[/color] 是指令的字符串(顾名思义就是指令名称),不能包含空格. 它的类型是[color=blue]ngx_str_t[/color], 通常都是以像(e.g.) [color=blue]ngx_str("proxy_pass")[/color]这样的方式来实例化. 注意: [color=blue]ngx_str_t[/color]包含一个存放字符串内容的[color=blue]data[/color]字段,和一个存放字符串长度的[color=blue]len[/color]字段。Nginx广泛地使用这个类型来存放字符串。

2)结构体成员[color=blue]type[/color]是标识的集合,表明这个指令在哪里出现是合法的、指令的参数有几个。应用中,标识一般是下面多个值的BIT或:
* NGX_HTTP_MAIN_CONF: 指令出现在main配置部分是合法的
* NGX_HTTP_SRV_CONF: 指令在server配置部分出现是合法的 config
* NGX_HTTP_LOC_CONF: 指令在location配置部分出现是合法的
* NGX_HTTP_UPS_CONF: 指令在upstream配置部分出现是合法的

* NGX_CONF_NOARGS: 指令没有参数
* NGX_CONF_TAKE1: 指令读入1个参数
* NGX_CONF_TAKE2: 指令读入2个参数
* ...
* NGX_CONF_TAKE7: 指令读入7个参数

* NGX_CONF_FLAG: 指令读入1个布尔型数据 ("on" or "off")
* NGX_CONF_1MORE: 指令至少读入1个参数
* NGX_CONF_2MORE: 指令至少读入2个参数
这里还有很多其他的选项:[url=http://lxr.evanmiller.org/http/source/core/ngx_conf_file.h#L1]core/ngx_conf_file.h[/url].

3)结构体成员 [color=blue]set[/color] 是一个函数指针,它指向的函数用来进行模块配置;这个设定函数一般用来将配置文件中的参数传递给程序,并保存在配置结构体中。设定函数有三个入参:
*(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
1. 指向结构体 ngx_conf_t 的指针, 这个结构体里包含需要传递给指令的参数
2. 指向结构体 ngx_command_t 的指针
3. 指向模块自定义配置结构体的指针

设定函数会在遇到指令时执行,Nginx提供了多个函数用来保存特定类型的数据,这些函数包含有:

* ngx_conf_set_flag_slot: 将 "on" or "off" 转换成 1 or 0
* ngx_conf_set_str_slot: 将字符串保存为 ngx_str_t
* ngx_conf_set_num_slot: 解析一个数字并保存为int
* ngx_conf_set_size_slot: 解析一个数据大小(如:"8k", "1m") 并保存为size_t

当然还有其他的,在[url=http://lxr.evanmiller.org/http/source/core/ngx_conf_file.h#L329]core/ngx_conf_file.h[/url]中很容易查到。如果你觉得现有这些内置的函数还不能满足你,当然也可以传入自己的函数引用。

这些内置函数是如何知道把数据存放在哪里的呢?这就是接下来两个结构体[color=blue]ngx_command_t[/color]的成员 [color=blue]conf[/color] 和 [color=blue]offset[/color]要做的事了.

4)[color=blue]conf[/color] 告诉Nginx把数据存在模块的哪个配置中,是main配置、server 配置, 还是 location 配置 ?(通过 NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET, 或者 NGX_HTTP_LOC_CONF_OFFSET).

5)[color=blue]offset[/color] 确定到底是保存在结构体的哪个位置。

6)最后, [color=blue]post[/color]指向模块在读配置的时候需要的一些零碎变量。一般它是NULL。

ngx_command_t数组以ngx_null_command 为终结符(就好像字符串以'\0'为终结符一样).

本例中,对应的实现为:

static char *
ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
ngx_http_circle_gif_loc_conf_t *cglcf = conf;

clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_circle_gif_handler;

cglcf->enable = 1;

return NGX_CONF_OK;
}
### 配置 Nginx 使用 `ngx_stream_geoip2_module` 模块 #### 安装依赖项 为了成功编译和运行 `ngx_stream_geoip2_module`,需要先安装必要的开发工具和库文件。这通常包括 MaxMind 的数据库支持库。 ```bash yum install libmaxminddb-devel -y ``` #### 下载并准备模块源码 获取最新的 `ngx_http_geoip2_module` 和 `ngx_stream_geoip2_module` 源代码: ```bash cd /usr/local/src/ git clone https://github.com/leev/ngx_http_geoip2_module.git ``` 注意:虽然这里提到的是 `ngx_http_geoip2_module`,但是 GitHub 上该项目也包含了流模式的支持即 `ngx_stream_geoip2_module`. #### 编译安装 NGINX 并添加自定义模块 在编译 NGINX 之前,确保已经下载了官方发布的稳定版本,并将其解压缩到合适的位置。接着,在配置阶段通过命令行参数指定要加入的新功能模块路径。 ```bash tar -zxvf nginx-1.20.1.tar.gz cd nginx-1.20.1/ ./configure \ --with-stream \ --add-dynamic-module=../ngx_http_geoip2_module \ --prefix=/etc/nginx \ --sbin-path=/usr/sbin/nginx \ --modules-path=/usr/lib/nginx/modules \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/run/nginx.lock \ --http-client-body-temp-path=/var/cache/nginx/client_temp \ --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ --user=nginx \ --group=nginx \ --with-compat \ --with-file-aio \ --with-http_ssl_module \ --with-http_v2_module \ --with-http_realip_module \ --with-http_addition_module \ --with-http_xslt_module=dynamic \ --with-http_image_filter_module=dynamic \ --with-http_geoip_module=dynamic \ --with-http_sub_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_mp4_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_random_index_module \ --with-http_secure_link_module \ --with-http_degradation_module \ --with-http_slice_module \ --with-http_stub_status_module \ --with-mail=static \ --with-mail_ssl_module \ --with-pcre \ --with-pcre-jit \ --with-stream=dynamic \ --with-stream_ssl_module \ --with-stream_realip_module \ --with-stream_geoip_module=dynamic \ --with-stream_ssl_preread_module \ --with-debug make && make install ``` 上述过程会将 `ngx_stream_geoip2_module` 动态加载至 NGINX 中[^3]。 #### 获取 IP 地理位置数据集 (MaxMind DB 文件) 从官方网站或其他可信资源处获得最新版的 GeoLite2 数据包,并按照说明放置于适当位置供 NGINX 访问。 ```bash mkdir -p /etc/nginx/geoip cd /tmp wget https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=YOUR_LICENSE_KEY&suffix=tar.gz tar xf geoip_download\?edition_id\=GeoLite2-City* -C /etc/nginx/geoip --strip-components=1 ``` 请注意替换 URL 参数中的 `YOUR_LICENSE_KEY` 为你自己的许可证密钥[^2]。 #### 修改 NGINX 配置以启用 Stream GeoIP 支持 编辑 `/etc/nginx/nginx.conf` 或者相应的虚拟主机配置文件来引入新的指令用于处理 TCP/UDP 请求的同时解析客户端的真实地理位置信息。 ```nginx stream { upstream backend { server 192.168.1.1:80; } map $remote_addr $country_code { default "unknown"; include /etc/nginx/geoip/country.map; # 自定义映射表可选 } log_format stream_combined '$remote_addr [$time_local] ' '"$protocol $status" $bytes_sent "$upstream_addr"' ' country=$country_code'; access_log /var/log/nginx/stream-access.log stream_combined; server { listen 12345; proxy_pass backend; geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb { auto_load on; $city city.names.en; $country_code country.iso_code; } set $allowed_countries "CN US CA"; # 允许访问的国家列表 if ($country_code !~* ^($allowed_countries)$ ) { return 503; } } } ``` 此段配置实现了基于地理区域限制服务可用性的基本逻辑,仅当请求来自特定几个允许的国家时才转发流量给后端服务器;否则返回 HTTP 503 错误响应表示暂时不可用[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值