提到ngx,一个永远绕不开的概念就是“模块”,模块在ngx中扮演者举足轻重的角色,你甚至可以认为在ngx中一切都是模块,它是一种或多种功能的一个组合体,是ngx实现灵活扩展的一个基本保障。
其实这个模块的概念跟积木及其相似,在生活中,我们可以用一堆各式各样的积木拼成各种东西,比如房子、动物、飞船等,如果现有的积木无法完成你想要拼成的东西,你并不需要否定现有的积木,而是可以引入新的积木并与之进行配合。比如在拼装汽车时缺少一个轮胎形状的积木,那我们可以单独造一个这样的积木,而不是修改现有的积木(有可能修改完后又无法拼成之前的了)。
在ngx配置文件中无法明确看出模块的影子,但其实一切皆模块,配置文件中任何一条指令都属于某个模块,之前所有文章中的任何例子都有很多模块参与,但因侧重点不同并没有一一列出,这次我们找几个例在来一一说明。
来看一个最简单的:
location / {
return 200 “hello”;
}
这个例子有两条指令,它背后正好有两个模块参与其中。第一条指令location属于ngx_http_core_module模块,从名字就可以看出来,它是ngx处理http协议的核心模块;另一条指令return属于ngx_http_rewrite_module模块。
假设现在有这样一个需求,我们想在某个请求返回的数据前和后加上一些特殊的数据,而这些数据则有其它location提供,但目前指令return是无法完成的,不过我们可以对其进行改造,比如改造成这样:
return 200 “#{/before} hello #{/after}”
我们可以把这个指令进行改造,让他可以从“#{}”中解析数据,并把解析出的数据当成location,然后再内部把解析的location数据拿过来,看起来这种方案是行得通的,但它看起来并不优雅。比如无法完全确保修改完后是否对原功能产生何种影响;比如种语法看起来也很怪;再比如原数据本身如何就包含这种“#{}”字符,那我们就需要对原数据进行字符转义。
总之,糟糕透了。
对于这种问题,在ngx中一个正规思路是:另外加模块来完成这个功能。而ngx正好有一个ngx_http_addition_module模块可以完成这个需求,所以我们也不必单独搞一个模块了,它用起来像这样:
location / {
add_before_body /before;
add_after_body. /after;
// *表示拦截所有的mime-type,默认text/html
addition_types *;
return 200 “hello”;
}
这样一来这个配置就涉及到三个模块了。
为了避免过多的干扰,所以基本上以往的例子都是ngx的一个片段配置,它们是无法独立工作的,这次我们拿一个可以完整工作的配置,来看看它都涉及到了哪些模块:
worker_processes 1;
events {
worker_connections 1024;
}
http{
listen 80;
location / {
return 200 “hello”;
}
}
这个配置有7个指令,涉及两种模块类型,一个是核心类型的,一个是http类型的。其中worker_processes、events、worker_connections、http属于核心类型,而listen、location、return则属于http类型的。更具体一点的话listen属于ngx_http_core_module模块,location和return属于ngx_http_rewrite_module模块。
而另外四个核心指令,ngx在官网上统一把他们归到了ngx_core_module(http://nginx.org/en/docs/ngx_core_module.html)中,但其实在ngx内部并没有一个ngx_core_module模块或.c文件与之对应,比如worker_processes指令实际对应到src/core/nginx.c,events和worker_connections对应到src /event/ngx_event.c,http则是对应到src/http/ngx_http.c,如果不是特殊情况,后续为了方便,我们也统一把这些指令的归属模块叫成ngx_core_module。
通过这个稍微完整点的例子,我们基本可以感觉到,ngx的运作模式就是有核心模块,以及其它非核心模块配合完成的,比如正确匹配http请求需要http_core模块,设置自定变量需要rewrite模块,做反向代理又需要proxy模块,所以除了模块还是模块。
1如何表示一个模块
ngx用下面的结构体表示一个模块:
(为了避免累赘并没有贴出所有字段)
/src/core/ngx_conf_file.h
struct ngx_module_s {
// …
ngx_uint_t ctx_index;
ngx_uint_t index;
// …
void *ctx;
ngx_command_t *commands;
// …
ngx_uint_t type;
// …
}
/src/core/ngx_core.h
typedef struct ngx_module_s ngx_module_t;
ngx中的所有模块都用这个结构体来定义,它有点类似于面向Java语言中的Object,但毕竟C语言并非面向对象语言,所以对于子模块的扩展也没有类似严格的继承语法,它的扩展方式类似于口头约定。比如在ngx中,要扩展一个模块,一般需要约定三种数据,一种是该结构体的ctx字段,另一种是type字段,还有一个是该模块提供的指令集commands,但除了type之外其它两个都不是必须的。
其中type用来指定是哪种类型的模块,比如ngx中的核心模块类型使用一个宏定义/src/core/ngx_conf_file.h#NGX_CORE_MODULE,这个宏表示这是一个核心模块。
而ctx一般使用一个结构体来表示,具体信息由各个模块自行描述,比如核心模块就使用下面的结构体:
typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
它定义了三个字段,name用来表示某个核心模块的名字,比如http、email等。另外两个是该类型模块的回调函数,ngx会在某个时间点对其进行回调。
指令集commands是一个数组,具体有哪些指令也是有各个模块自行描述,但指令的格式是固定的,每个指令都需要用ngx_command_t结构体来表述,比如核心http模块(ngx_http.c)有如下指令集:
static ngx_command_t ngx_http_commands[] = {
{ ngx_string("http"),
XXX|XXX,
ngx_http_block,
0,
0,
NULL },
ngx_null_command
};
该数组表示该模块只有一个指令,名字叫“http”,该指令对应的方法是ngx_http_block(当解析到指令http时要执行的方法),其它字段含义等后续实际用到再做解释。
目前在ngx中有如下几个核心模块:
src/core/ngx_thread_pool.c
src /core/ngx_log.c
src /core/ngx_regex.c
src /core/nginx.c
src /event/ngx_event_openssl.c
src /event/ngx_event.c
src /http/ngx_http.c
src /mail/ngx_mail.c
src /misc/ngx_google_perftools_module.c
src /stream/ngx_stream.c
他们的共同特点是都实现了ngx_core_module_t结构体,比如我们在ngx配置文件中最熟悉的指令http{}所属的模块ngx_http.c就是一个核心模块,它的定义是这样的:
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};
ngx_module_t ngx_http_module = {
NGX_MODULE_V1,
&ngx_http_module_ctx, /* module context */
ngx_http_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
// …
};
可以看到,它直接声明了一个ngx_core_module_t类型的结构体(ngx内部管这种结构体叫模块上下文—module context),并初始化该模块的名字为“http”,另外由于他不需要ngx执行回调函数,所以将其设置为NULL,这样ngx主框架在启动的时候就不会执行对应位置的回调函数了。然后再用ngx_module_t来声明一个该类型的结构体,把对应的http模块上下文(ngx_http_module_ctx)设置到该结构体ctx位置,type位置则是设置为NGX_CORE_MODULE,这样就基本“实现”了一个核心模块,并且该模块的名字为“http”。
基本上ngx模块的表示都是这种套路,如果你要定义新模块类型,或“实现”现有模块类型,按照上面的例子搞一套就可以了。
2ngx中的模块类型
目前在ngx中共有六种模块类型,具体如下:
src/core/ngx_conf_file.h#NGX_CORE_MODULE
src/core/ngx_conf_file.h#NGX_CONF_MODULE
src/http/ngx_http_config.h#NGX_HTTP_MODULE
src/mail/ngx_mail.h#NGX_MAIL_MODULE
src/event/ngx_event.h#NGX_EVENT_MODULE
src/stream/ngx_stream.h#NGX_STREAM_MODULE
基本上每种类型都有各自ctx描述和各种实现,比如核心模块的ctx为ngx_core_module_t,它的实现有ngx_http.c、ngx_event.c等。
不过目前有一个特例,NGX_CONF_MODULE类型的模块并没有定义自己的ctx,而且对它的实现也只有一个“模块”,具体描述如下:
/src/core/ngx_conf_file.c
ngx_module_t ngx_conf_module = {
NULL, /* module context */
ngx_conf_commands, /* module directives */
NGX_CONF_MODULE, /* module type */
};
也就是说他除了不需要回调函数外,连名字也懒得定义了,不过还好它有一个指令“include”,该指令承包了该模块对外的一切功能。
3执行回调函数(加载模块)
除了ngx中自带的六种模块类型,用户还可定义自己的模块类型,而且每种模块类型又可以有很多“实现”,所以这么多模块,ngx是如何执行这些模块的回调函数的呢?
实际上ngx主流程在启动的时候只关心一种模块类型:NGX_CORE_MODULE。在开始解析配置文件之前,它会先回调各个核心模块的create_conf方法(在ngx_core_module_t结构体中指定),具体调用方式如下:
src/core/ngx_cycle.c
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
// 排除非NGX_CORE_MODULE类型的模块
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_conf) {
// 回调模块的create_conf
rv = module->create_conf(cycle);
// …
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
等配置文件解析完毕后再调用核心模块的init_conf方法(在ngx_core_module_t结构体中指定),具体调用方式也是一个循环:
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
// 排除非核心模块
continue;
}
module = ngx_modules[i]->ctx;
if (module->init_conf) {
// 回调init_conf方法
if(module->init_conf(cycle,xxx)== yyy) {
// …
return NULL;
}
}
}
其它模块类型的回调函数则有对应的核心模块的实现来完成,比如NGX_HTTP_MODULE类型的模块都有ngx_http.c这个核心模块的实现完成,它的回调入口是http指令对应的/src/http/ngx_http.c/ngx_http_block()方法。
NGX_HTTP_MODULE类型的模块对应的ctx定义如下:
src/http/ngx_http_config.h
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
所以在ngx_http_block()方法会直接或间接回调该结构体中的方法,具体会回调方式仍然以循环所有模块开始:
for (m = 0; ngx_modules[m]; m++) {
// 过滤掉非NGX_HTTP_MODULE类型的模块
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[m]->ctx;
if (module->create_main_conf) {
// 回调create_main_conf
ctx->main_conf[mi] = module->create_main_conf(cf);
// …
}
}
以此类推,其它回调方法也是如此。
目前除了NGX_CONF_MODULE类型模块外,其它非核心模块类型的加载方式(执行回调函数)都跟NGX_HTTP_MODULE类似。比如NGX_EVENT_MODULE以src/event/ngx_event.c#ngx_event_process_init()方法为入口,NGX_MAIL_MODULE以src/email/ngx_mail.c#ngx_mail_block()方法为入口,NGX_STREAM_MODULE则以src/stream/ngx_stream.c#ngx_stream_block()方法为入口。
4ngx现有的模块分布
为了便于读者查阅代码,这里把各个模块类型在源代码目中的分布做个简单介绍:
NGX_CONF_MODULE:
src/core/ngx_conf_file.c
NGX_CORE_MODULE:
具体模块在第二节已经列举,这里不再赘述。
NGX_HTTP_MODULE:
分布在src/http目录及其子目录下
NGX_MAIL_MODULE:
分布在src/email目录下
NGX_EVENT_MODULE:
分布在src/event目录及其子目录下
NGX_STREAM_MODULE:
分布在src/stream目录下
相关推荐
ngx_devel_kit(通常缩写为 NDK)是一个针对Nginx的模块开发工具集,它为构建自定义Nginx模块提供了便利。在Nginx生态系统中,NDK是一个重要的扩展工具,允许开发者利用C语言直接操作Nginx的内部结构,以实现更高级...
这个版本2.4.2的压缩包包含了ngx_cache_purge模块的所有源代码及相关文件,以便开发者在自己的Nginx环境中集成和使用。 Nginx是一款高性能、轻量级的Web服务器和反向代理服务器,广泛应用于互联网服务。其内置的...
Nginx 支持模块化设计,允许开发者通过编写模块来扩展其功能。ngx_http_consistent_hash 模块是这样的一个例子,它将一致性哈希的功能集成到 Nginx 中。 4. **配置和使用:** 在 Nginx 配置文件中,用户需要引入...
Nginx是一个高性能的HTTP和反向代理服务器,广泛应用于互联网服务,其模块化的架构允许用户通过加载不同的模块来扩展功能。ngx_log_if模块的引入,使得我们可以更加灵活地定制日志输出策略,提高日志管理的效率和...
ngx_lua 是一个强大的 Lua 脚本绑定模块,用于 ngx_openresty,这是一个全面的、高性能的、企业级的 Lua 开发平台,基于 Nginx。ngx_lua 允许开发者在 Nginx 的事件驱动、非阻塞 I/O 模型下编写服务器端的 Lua 应用...
ngx_lua_module是一款强大的扩展模块,专为Nginx服务器设计,允许在Nginx配置文件中直接嵌入Lua脚本,极大地增强了Nginx的功能和灵活性。这个"ngx_lua_module-windows-1.1.2.0"是该模块的一个Windows版本,适应于...
Angular-ngx-sharebuttons.zip,角共享按钮角共享按钮,Angularjs于2016年发布,是Angularjs的重写版。它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
首先,`ngx` 前缀是 Angular 社区中常见的命名约定,通常用于表示某个库或模块是专门为 Angular 框架设计的。ngx-datatable 与 Angular 的集成非常紧密,利用了 Angular 的组件化和数据绑定特性,使得表格的创建和...
在测试CPU密集型任务时,ngx_lua与传统的Nginx C模块相比,表现出较高的性能,尤其在处理高并发场景时优势明显。 总结 ngx_lua是Nginx的一个模块,它通过Lua语言和协程机制解决了传统架构并发度低、内存开销大和...
Angular-ngx-validators.zip,角度2 NGX验证程序的验证程序库,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-echarts.zip,用于echarts(ver>=3.x)ngx echarts的角度(ver>=2.x)指令,Angularjs于2016...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-model.zip,角度模型。简单的状态管理,具有极简的api、单向数据流、多模型支持和作为rxjs ...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-currency.zip,AngularNGX货币的货币掩码模块,Angularjs于2016年发布,是Angularjs的重写版...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
ngx_devel_kit是Nginx的一个重要扩展模块,它为Nginx提供了一系列的开发工具,方便开发者在Nginx上构建自定义模块。这个0.2.19版本的压缩包“ngx_devel_kit-0.2.19.tar.gz”包含了用于编译和开发Nginx模块所需的源...
Angular-ngx-scrollreveal.zip,scrollReveal的角度指令:一个javascript库,用于在元素进入/离开视区时...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-treeview.zip,带checkboxngx树视图的角度树视图组件,Angularjs于2016年发布,是Angularjs...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-countdown.zip,简单,容易和性能倒计时角度倒计时,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。
Angular-ngx-quicklink.zip,角路由的QuickLink预取策略GX QuickLink,Angularjs于2016年发布,是...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。