`
deyimsf
  • 浏览: 66715 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

模块化的ngx

阅读更多

提到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目录下

分享到:
评论

相关推荐

    Angular-ngx-sharebuttons.zip

    Angular-ngx-sharebuttons.zip,角共享按钮角共享按钮,Angularjs于2016年发布,是Angularjs的重写版。它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-validators.zip

    Angular-ngx-validators.zip,角度2 NGX验证程序的验证程序库,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-echarts.zip

    Angular-ngx-echarts.zip,用于echarts(ver>=3.x)ngx echarts的角度(ver>=2.x)指令,Angularjs于2016...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-model.zip

    Angular-ngx-model.zip,角度模型。简单的状态管理,具有极简的api、单向数据流、多模型支持和作为rxjs ...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-currency.zip

    Angular-ngx-currency.zip,AngularNGX货币的货币掩码模块,Angularjs于2016年发布,是Angularjs的重写版...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-scrollreveal.zip

    Angular-ngx-scrollreveal.zip,scrollReveal的角度指令:一个javascript库,用于在元素进入/离开视区时...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-treeview.zip

    Angular-ngx-treeview.zip,带checkboxngx树视图的角度树视图组件,Angularjs于2016年发布,是Angularjs...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-countdown.zip

    Angular-ngx-countdown.zip,简单,容易和性能倒计时角度倒计时,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-quicklink.zip

    Angular-ngx-quicklink.zip,角路由的QuickLink预取策略GX QuickLink,Angularjs于2016年发布,是...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-trend.zip

    Angular-ngx-trend.zip,简单优雅的火花线,适合Angularngx潮流,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-quill-example.zip

    Angular-ngx-quill-example.zip,Ngx Quill模块高级使用的Webpack演示应用程序Ngx Quill示例,Angularjs...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-slick.zip

    Angular-ngx-slick.zip,Angular6 Slick Pluginngx Slick包装纸,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-webcam.zip

    Angular-ngx-webcam.zip,一个简单的角度4 网络摄像头组件/纯和最小,无闪光回退NGX网络摄像头,...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-amap.zip

    Angular-ngx-amap.zip,AMAP(Gaode Map)NGX AMAP的角2 分量,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-api-utils.zip

    Angular-ngx-api-utils.zip,ngx api实用程序是一个精简的实用程序和帮助程序库,用于快速将任何httpapi...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-context.zip

    Angular-ngx-context.zip,角度上下文:路由器出口和嵌套组件树的简单属性绑定。角度上下文(NGX上下文...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-graph.zip

    Angular-ngx-graph.zip,角度图可视化库,Angularjs于2016年发布,是Angularjs的重写版。它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-gallery.zip

    Angular-ngx-gallery.zip,角形画廊、旋转木马和灯箱角形画廊,Angularjs于2016年发布,是Angularjs的...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-scrollspy.zip

    Angular-ngx-scrollspy.zip,Angular ScrollSpy服务您可以使用Angular2服务从窗口或任何其他可滚动元素...它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

    Angular-ngx-auth.zip

    Angular-ngx-auth.zip,角7 认证模块角7 认证,Angularjs于2016年发布,是Angularjs的重写版。它专注于良好的移动开发、模块化和改进的依赖注入。angular的设计目的是全面解决开发人员的web应用程序工作流。

Global site tag (gtag.js) - Google Analytics