免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5825 | 回复: 4
打印 上一主题 下一主题

Nginx模块开发入门 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-05-15 20:15 |只看该作者 |倒序浏览
本帖最后由 cu_Cbear 于 2011-05-15 20:17 编辑

Nginx模块开发入门



   Nginx是当前最流行的HTTP Server之一,根据W3techs的统计,目前世界排名前100万的网站。有6%是用的Nginx。与Apache相比呢,Nginx最大的优势就是高并发。
    Nginx属于典型的微内核设计,其内核非常简洁和优雅,同时具有非常高的可扩展性。Nginx最初仅仅主要被用于做反向代理,后来随着HTTP核心的成熟和各种HTTP扩展模块的丰富,Nginx越来越多被用来取代Apache而单独承担HTTP Server的责任,例如目前淘宝内各个部门正越来越多使用Nginx取代Apache,据笔者了解,在腾讯和新浪等公司也存在类似情况。
    同时,大量的第三方扩展模块也令Nginx越来越强大。例如,由淘宝的工程师清无(王晓哲)和春来(章亦春)所开发的nginx_lua_module可以将Lua语言嵌入到Nginx配置中,从而利用Lua极大增强了Nginx本身的编程能力,甚至可以不用配合其它脚本语言(如PHP或Python等),只靠Nginx本身就可以实现复杂业务的处理。而春来所开发的ngx_openresty更是通过集成LuaJIT等组件,将Nginx本身变成了一个完全的应用开发平台。目前淘宝数据平台与产品部量子统计的产品都是基于ngx_openresty所开发。

Nginx在Linux下的安装与运行:
    使用Nginx的第一步是下载Nginx源码包,例如1.0.0的下载地址为http://www.nginx.org/download/nginx-1.0.0.tar.gz下载完后用tar命令解压缩,进入目录后安装过程与Linux下通常步骤无异,例如我想讲Nginx安装到/usr/local/nginx下,则执行如下命令:


[root@localhost nginx-1.0.0]#./configure --prefix=/usr/local/nginx
[root@localhost nginx-1.0.0]#make && make install
[root@localhost nginx-1.0.0]#cd ../
安装完成后可以直接使用下面命令启动Nginx:
[root@localhost nginx-1.0.0]#/usr/local/nginx/sbin/nginx               
Nginx默认以Deamon进程启动,输入下列命令:
[root@localhost nginx-1.0.0]# curl -I localhost                                 

启动后如果想停止Nginx可以使用:
[root@localhost nginx-1.0.0]#/usr/local/nginx/sbin/nginx -s stop      

Nginx配置文件的结构:
    配置文件可以看做是Nginx的灵魂,Nginx服务在启动时会读入配置文件,而后续几乎一切动作行为都是按照配置文件中的指令进行的,因此如果将Nginx本身看做一个计算机,那么Nginx的配置文件可以看成是全部的程序指令。
下面是一个Nginx配置文件的实例:

  1. #user nobody;                                                                                      
  2. worker_processes 8;
  3. error_log logs/error.log
  4. pid          logs/nginx.pid
  5. events {
  6.     worker_connecrions 1024;
  7. }

  8. http {
  9.     include  mime.types;
  10.     default_type   application/octet-stream;
  11.     sendfile  on;
  12.     #tcp_nopush on;
  13.     keepalive_timeout  65;
  14.     #gzip on;

  15. server {
  16.         listen 80
  17.         server_name localhost;
  18.         location / {
  19.             root /home/wwwroot;
  20.             index index.html index.htm;
  21.            }

  22. #error_page 404    /404.html;
  23. error_page  500 502 503 504 /50x.html;
  24. location = 50x.html {
  25.     root   html;
  26.        }
  27.     }
  28. }
复制代码
Nginx配置文件是纯文本文件,你可以用任何文本编辑器如vim或emacs打开它,通常它会在nginx安装目录的conf下,如我的nginx安装在/usr/local/nginx,主配置文件默认放在/usr/local/nginx/conf/nginx.conf。
    其中“#”表示此行是注释,由于笔者为了学习扩展开发安装了一个纯净的Nginx,因此配置文件没有经过太多改动。
Nginx的配置文件是以block的形式组织的,一个block通常使用大括号“{}”表示。block分为几个层级,整个配置文件为main层级,这是最大的层级;在main层级下可以有event、http等层级,而http中又会有server block,server block中可以包含location block。
    每个层级可以有自己的指令(Directive),例如worker_processes是一个main层级指令,它指定Nginx服务的Worker进程数量。有的指令只能在一个层级中配置,如worker_processes只能存在于main中,而有的指令可以存在于多个层级,在这种情况下,子block会继承父block的配置,同时如果子block配置了与父block不同的指令,则会覆盖掉父block的配置。指令的格式是“指令名 参数1  参数2 … 参数N;”,注意参数间可用任意数量空格分隔,最后要加分号。
在开发Nginx HTTP扩展模块过程中,需要特别注意的是main、server和location三个层级,因为扩展模块通常允许指定新的配置指令在这三个层级中。
    最后要提到的是配置文件是可以包含的,如上面配置文件中“include mime.types”就包含了mine.types这个配置文件,此文件指定了各种HTTP Content-type。
    一般来说,一个server block表示一个Host,而里面的一个location则代表一个路由映射规则,这两个block可以说是HTTP配置的核心。
    下图是Nginx配置文件通常结构图示。




Nginx工作模块原理概述:

    (Nginx本身支持多种模块,如HTTP模块、EVENT模块和MAIL模块,本文只讨论HTTP模块)
      Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做Nginx真正的劳动工作者。通常一个location中的指令会涉及一个handler模块和多个filter模块(当然,多个location可以复用同一个模块)。handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。因此Nginx模块开发分为handler开发和filter开发(本文不考虑load-balancer模块)。下图展示了一次常规请求和响应的过程。




Nginx模块开发实战:

    下面本文展示一个简单的Nginx模块开发全过程,我们开发一个叫echo的handler模块,这个模块功能非常简单,它接收“echo”指令,指令可指定一个字符串参数,模块会输出这个字符串作为HTTP响应。例如,做如下配置:
  1. location /echo {                                                
  2.         echo "hello nginx";
  3. }
复制代码
则访问http://localhost/echo时候会出现hello nginx
    直观来看,要实现这个功能需要三步:
    1、读入配置文件中echo指令及其参数;
    2、进行HTTP包装(添加HTTP头等工作);
    3、将结果返回给客户端。

定义模块配置结构:
     首先我们需要一个结构用于存储从配置文件中读进来的相关指令参数,即模块配置信息结构。根据Nginx模块开发规则,这个结构的命名规则为ngx_http_[module-name]_[main|srv|loc]_conf_t。其中main、srv和loc分别用于表示同一模块在三层block中的配置信息。这里我们的echo模块只需要运行在loc层级下,需要存储一个字符串参数,因此我们可以定义如下的模块配置:

  1. typedef struct {
  2.      ngx_str_t  ed;
  3. } ngx_http_echo_loc_conf_t;
复制代码
复制代码
     其中字段ed用于存储echo指令指定的需要输出的字符串。注意这里ed的类型,在Nginx模块开发中使用ngx_str_t类型表示字符串,这个类型定义在core/ngx_string中:

  1. typedef struct {
  2.      size_t      len;
  3.      u_char     *data;
  4. } ngx_str_t;
复制代码
复制代码
     其中两个字段分别表示字符串的长度和数据起始地址。注意在Nginx源代码中对数据类型进行了别称定义,如ngx_int_t为intptr_t的别称,为了保持一致,在开发Nginx模块时也应该使用这些Nginx源码定义的类型而不要使用C原生类型。除了ngx_str_t外,其它三个常用的nginx type分别为:
  1. typedef intptr_t        ngx_int_t;
  2. typedef uintptr_t       ngx_uint_t;
  3. typedef intptr_t        ngx_flag_t;
复制代码
复制代码
定义指令:
    一个Nginx模块往往接收一至多个指令,echo模块接收一个指令“echo”。Nginx模块使用一个ngx_command_t数组表示模块所能接收的所有模块,其中每一个元素表示一个条指令。ngx_command_t是ngx_command_s的一个别称(Nginx习惯于使用“_s”后缀命名结构体,然后typedef一个同名“_t”后缀名称作为此结构体的类型名),ngx_command_s定义在core/ngx_config_file.h中:
  1. struct ngx_command_s {
  2.      ngx_str_t             name;
  3.      ngx_uint_t            type;
  4.      char                 *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  5.      ngx_uint_t            conf;
  6.      ngx_uint_t            offset;
  7.      void                  *post;
  8. };
复制代码
复制代码
     其中name是词条指令的名称,type使用掩码标志位方式配置指令参数,相关可用type定义在core/ngx_config_file.h中:

  1. #define NGX_CONF_NOARGS      0x00000001
  2. #define NGX_CONF_TAKE1       0x00000002
  3. #define NGX_CONF_TAKE2       0x00000004
  4. #define NGX_CONF_TAKE3       0x00000008
  5. #define NGX_CONF_TAKE4       0x00000010
  6. #define NGX_CONF_TAKE5       0x00000020
  7. #define NGX_CONF_TAKE6       0x00000040
  8. #define NGX_CONF_TAKE7       0x00000080
  9. #define NGX_CONF_MAX_ARGS    8
  10. #define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
  11. #define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
  12. #define NGX_CONF_TAKE23      (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
  13. #define NGX_CONF_TAKE123     (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
  14. #define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3   \
  15.                                |NGX_CONF_TAKE4)
  16. #define NGX_CONF_ARGS_NUMBER 0x000000ff
  17. #define NGX_CONF_BLOCK       0x00000100
  18. #define NGX_CONF_FLAG        0x00000200
  19. #define NGX_CONF_ANY         0x00000400
  20. #define NGX_CONF_1MORE       0x00000800
  21. #define NGX_CONF_2MORE       0x00001000
  22. #define NGX_CONF_MULTI       0x00002000
复制代码
复制代码
     其中NGX_CONF_NOARGS表示此指令不接受参数,NGX_CON F_TAKE1-7表示精确接收1-7个,NGX_CONF_TAKE12表示接受1或2个参数,NGX_CONF_1MORE表示至少一个参数,NGX_CONF_FLAG表示接受“on|off”……
     set是一个函数指针,用于指定一个参数转化函数,这个函数一般是将配置文件中相关指令的参数转化成需要的格式并存入配置结构体。Nginx预定义了一些转换函数,可以方便我们调用,这些函数定义在core/ngx_conf_file.h中,一般以“_slot”结尾,例如ngx_conf_set_flag_slot将“on或off”转换为“1或0”,再如ngx_conf_set_str_slot将裸字符串转化为ngx_str_t。
     conf用于指定Nginx相应配置文件内存其实地址,一般可以通过内置常量指定,如NGX_HTTP_LOC_CONF_OFFSET,offset指定此条指令的参数的偏移量。
下面是echo模块的指令定义:
  1. static ngx_command_t  ngx_http_echo_commands[] = {
  2.      { ngx_string("echo"),
  3.        NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  4.        ngx_http_echo,
  5.        NGX_HTTP_LOC_CONF_OFFSET,
  6.        offsetof(ngx_http_echo_loc_conf_t, ed),
  7.        NULL },
  8.        ngx_null_command
  9. };
复制代码
复制代码

参数转化函数的代码为:
  1. static char *
  2. ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  3. {
  4.      ngx_http_core_loc_conf_t  *clcf;
  5.      clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  6.      clcf->handler = ngx_http_echo_handler;
  7.      ngx_conf_set_str_slot(cf,cmd,conf);
  8.      return NGX_CONF_OK;
  9. }
复制代码
复制代码
    这个函数除了调用ngx_conf_set_str_slot转化echo指令的参数外,还将修改了核心模块配置(也就是这个location的配置),将其handler替换为我们编写的handler:ngx_http_echo_handler。这样就屏蔽了此location的默认handler,使用ngx_http_echo_handler产生HTTP响应。


创建合并配置信息:
下一步是定义模块Context。
这里首先需要定义一个ngx_http_module_t类型的结构体变量,命名规则为ngx_http_[module-name]_module_ctx,这个结构主要用于定义各个Hook函数。下面是echo模块的context结构:
  1. static ngx_http_module_t  ngx_http_echo_module_ctx = {
  2.      NULL,                                  /* preconfiguration */
  3.      NULL,                                  /* postconfiguration */
  4.      NULL,                                  /* create main configuration */
  5.      NULL,                                  /* init main configuration */
  6.      NULL,                                  /* create server configuration */
  7.      NULL,                                  /* merge server configuration */
  8.      ngx_http_echo_create_loc_conf,         /* create location configration */
  9.      ngx_http_echo_merge_loc_conf           /* merge location configration */
  10. };
复制代码
复制代码
    可以看到一共有8个Hook注入点,分别会在不同时刻被Nginx调用,由于我们的模块仅仅用于location域,这里将不需要的注入点设为NULL即可。其中create_loc_conf用于初始化一个配置结构体,如为配置结构体分配内存等工作;merge_loc_conf用于将其父block的配置信息合并到此结构体中,也就是实现配置的继承。这两个函数会被Nginx自动调用。注意这里的命名规则:ngx_http_[module-name]_[create|merge]_[main|srv|loc]_conf。

下面是echo模块这个两个函数的代码:
  1. static void *
  2. ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
  3. {
  4.      ngx_http_echo_loc_conf_t  *conf;
  5.      conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
  6.      if (conf == NULL) {
  7.          return NGX_CONF_ERROR;
  8.      }
  9.      conf->ed.len = 0;
  10.      conf->ed.data = NULL;
  11.      return conf;
  12. }
  13. static char *
  14. ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  15. {
  16.      ngx_http_echo_loc_conf_t *prev = parent;
  17.      ngx_http_echo_loc_conf_t *conf = child;
  18.      ngx_conf_merge_str_value(conf->ed, prev->ed, "");
  19.      return NGX_CONF_OK;
  20. }
复制代码
复制代码
     其中ngx_pcalloc用于在Nginx内存池中分配一块空间,是pcalloc的一个包装。使用ngx_pcalloc分配的内存空间不必手工free,Nginx会自行管理,在适当是否释放。     create_loc_conf新建一个ngx_http_echo_loc_conf_t,分配内存,并初始化其中的数据,然后返回这个结构的指针;merge_loc_conf将父block域的配置信息合并到create_loc_conf新建的配置结构体中。
     其中ngx_conf_merge_str_value不是一个函数,而是一个宏,其定义在core/ngx_conf_file.h中:
  1. #define ngx_conf_merge_str_value(conf, prev, default)                        \
  2.      if (conf.data == NULL) {                                                 \
  3.          if (prev.data) {                                                     \
  4.              conf.len = prev.len;                                             \
  5.              conf.data = prev.data;                                           \
  6.          } else {                                                             \
  7.              conf.len = sizeof(default) - 1;                                  \
  8.                conf.data = (u_char *) default;                                \
  9.          }                                                                  
  10.      }
复制代码
复制代码
     同时可以看到,core/ngx_conf_file.h还定义了很多merge value的宏用于merge各种数据。它们的行为比较相似:使用prev填充conf,如果prev的数据为空则使用default填充。

编写Handler:
读入模块配置。
处理功能业务。
产生HTTP header。
产生HTTP body。
下面先贴出echo模块的代码,然后通过分析代码的方式介绍如何实现这四步。这一块的代码比较复杂:

  1. static ngx_int_t
  2. ngx_http_echo_handler(ngx_http_request_t *r)
  3. {
  4.      ngx_int_t rc;
  5.      ngx_buf_t *b;
  6.      ngx_chain_t out;
  7.      ngx_http_echo_loc_conf_t *elcf;
  8.      elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
  9.      if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
  10.      {
  11.          return NGX_HTTP_NOT_ALLOWED;
  12.      }
  13.      r->headers_out.content_type.len = sizeof("text/html") - 1;
  14.      r->headers_out.content_type.data = (u_char *) "text/html";
  15.      r->headers_out.status = NGX_HTTP_OK;
  16.      r->headers_out.content_length_n = elcf->ed.len;
  17.      if(r->method == NGX_HTTP_HEAD)
  18.      {
  19.          rc = ngx_http_send_header(r);
  20.          if(rc != NGX_OK)
  21.          {
  22.              return rc;
  23.          }
  24.      }
  25.      b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
  26.      if(b == NULL)
  27.      {
  28.          ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
  29.          return NGX_HTTP_INTERNAL_SERVER_ERROR;
  30.      }
  31.      out.buf = b;
  32.      out.next = NULL;
  33.      b->pos = elcf->ed.data;
  34.      b->last = elcf->ed.data + (elcf->ed.len);
  35.      b->memory = 1;
  36.      b->last_buf = 1;
  37.      rc = ngx_http_send_header(r);
  38.      if(rc != NGX_OK)
  39.      {
  40.          return rc;
  41.      }
  42.      return ngx_http_output_filter(r, &out);
  43. }
复制代码
复制代码
handler会接收一个ngx_http_request_t指针类型的参数,这个参数指向一个ngx_http_request_t结构体,此结构体存储了这次HTTP请求的一些信息,这个结构定义在http/ngx_http_request.h中:
  1. struct ngx_http_request_s {
  2.      uint32_t                          signature;         /* "HTTP" */
  3.      ngx_connection_t                 *connection;
  4.      void                            **ctx;
  5.      void                            **main_conf;
  6.      void                            **srv_conf;
  7.      void                            **loc_conf;
  8.      ngx_http_event_handler_pt         read_event_handler;
  9.      ngx_http_event_handler_pt         write_event_handler;
  10. #if (NGX_HTTP_CACHE)
  11.      ngx_http_cache_t                 *cache;
  12. #endif
  13.      ngx_http_upstream_t              *upstream;
  14.      ngx_array_t                      *upstream_states;
  15. /* of ngx_http_upstream_state_t */
  16.      ngx_pool_t                       *pool;
  17.      ngx_buf_t                        *header_in;
  18.      ngx_http_headers_in_t             headers_in;
  19.      ngx_http_headers_out_t            headers_out;
  20.      ngx_http_request_body_t          *request_body;
  21.      time_t                            lingering_time;
  22.      time_t                            start_sec;
  23.      ngx_msec_t                        start_msec;
  24.      ngx_uint_t                        method;
  25.      ngx_uint_t                        http_version;
  26.      ngx_str_t                         request_line;
  27.      ngx_str_t                         uri;
  28.      ngx_str_t                         args;
  29.      ngx_str_t                         exten;
  30.      ngx_str_t                         unparsed_uri;
  31.      ngx_str_t                         method_name;
  32.      ngx_str_t                        http_protocol;
  33.      ngx_chain_t                      *out;
  34.      ngx_http_request_t               *main;
  35.      ngx_http_request_t               *parent;
  36.      ngx_http_postponed_request_t     *postponed;
  37.      ngx_http_post_subrequest_t       *post_subrequest;
  38.      ngx_http_posted_request_t        *posted_requests;
  39.      ngx_http_virtual_names_t         *virtual_names;
  40.      ngx_int_t                         phase_handler;
  41.      ngx_http_handler_pt               content_handler;
  42.      ngx_uint_t                        access_code;
  43.      ngx_http_variable_value_t        *variables;
  44.      /* ... */
  45. }
复制代码
复制代码

论坛徽章:
0
2 [报告]
发表于 2011-05-15 20:16 |只看该作者
由于ngx_http_request_s定义比较长,这里我只截取了一部分。可以看到里面有诸如uri,args和request_body等HTTP常用信息。这里需要特别注意的几个字段是headers_in、headers_out和chain,它们分别表示request header、response header和输出数据缓冲区链表(缓冲区链表是Nginx I/O中的重要内容,后面会单独介绍)

    第一步是获取模块配置信息,这一块只要简单使用ngx_http_get_module_loc_conf就可以了。
    第二步是功能逻辑,因为echo模块非常简单,只是简单输出一个字符串,所以这里没有功能逻辑代码。
    第三步是设置response header。Header内容可以通过填充headers_out实现,我们这里只设置了Content-type和Contentlength等基本内容,ngx_http_headers_out_t定义了所有可以设置的HTTP Response Header信息:
  1. typedef struct {
  2.      ngx_list_t                        headers;
  3.      ngx_uint_t                        status;
  4.      ngx_str_t                         status_line;
  5.      ngx_table_elt_t                  *server;
  6.      ngx_table_elt_t                  *date;
  7.      ngx_table_elt_t                  *content_length;
  8.      ngx_table_elt_t                  *content_encoding;
  9.      ngx_table_elt_t                  *location;
  10.      ngx_table_elt_t                  *refresh;
  11.      ngx_table_elt_t                  *last_modified;
  12.      ngx_table_elt_t                  *content_range;
  13.      ngx_table_elt_t                  *accept_ranges;
  14.      ngx_table_elt_t                  *www_authenticate;
  15.      ngx_table_elt_t                  *expires;
  16.      ngx_table_elt_t                  *etag;
  17.      ngx_str_t                        *override_charset;
  18.      size_t                            content_type_len;
  19.      ngx_str_t                         content_type;
  20.      ngx_str_t                         charset;
  21.      u_char                           *content_type_lowcase;
  22.      ngx_uint_t                        content_type_hash;
  23.      ngx_array_t                       cache_control;
  24.      off_t                             content_length_n;
  25.      time_t                            date_time;
  26.      time_t                            last_modified_time;
  27. } ngx_http_headers_out_t;
复制代码
复制代码   设置好头信息后使用ngx_http_send_header就可以将头信息输出,ngx_http_send_header接受一个ngx_http_request_t类型的参数。
    第四步也是最重要的一步是输出Response body。这里首先要了解Nginx的I/O机制,Nginx允许handler一次产生一组输出,可以产生多次,Nginx将输出组织成一个单链表结构,链表中的每个节点是一个chain_t,定义在core/ngx_buf.h:
  1. struct ngx_chain_s {
  2.      ngx_buf_t    *buf;
  3.      ngx_chain_t  *next;
  4. };
复制代码
复制代码
    其中ngx_chain_t是ngx_chain_s的别名,buf为某个数据缓冲区的指针,next指向下一个链表节点,可以看到这是一个非常简单的链表。ngx_buf_t的定义比较长而且很复杂,这里就不贴出来了,请自行参考core/ngx_buf.h。ngx_but_t中比较重要的是pos和last,分别表示要缓冲区数据在内存中的起始地址和结尾地址,这里我们将配置中字符串传进去,last_buf是一个位域,设为1表示此缓冲区是链表中最后一个元素,为0表示后面还有元素。因为我们只有一组数据,所以缓冲区链表中只有一个节点,如果需要输入多组数据可将各组数据放入不同缓冲区后插入到链表。下图展示了Nginx缓冲链表的结构:



  缓冲数据准备好后,用ngx_http_output_filter就可以输出了(会送到filter进行各种过滤处理)。ngx_http_output_filter的第一个参数为ngx_http_request_t结构,第二个为输出链表的起始地址&out。ngx_http_out_put_filter会遍历链表,输出所有数据。

组合Nginx Module:
    上面完成了Nginx模块各种组件的开发下面就是将这些组合起来了。一个Nginx模块被定义为一个ngx_module_t结构,这个结构的字段很多,不过开头和结尾若干字段一般可以通过Nginx内置的宏去填充,下面是我们echo模块的模块主体定义:
以上就是handler的所有工作,请对照描述理解上面贴出的handler代码。
  1. ngx_module_t  ngx_http_echo_module = {
  2.      NGX_MODULE_V1,
  3.      &ngx_http_echo_module_ctx,             /* module context */
  4.      ngx_http_echo_commands,                /* module directives */
  5.      NGX_HTTP_MODULE,                       /* module type */
  6.      NULL,                                  /* init master */
  7.      NULL,                                  /* init module */
  8.      NULL,                                  /* init process */
  9.      NULL,                                  /* init thread */
  10.      NULL,                                  /* exit thread */
  11.      NULL,                                  /* exit process */
  12.      NULL,                                  /* exit master */
  13.      NGX_MODULE_V1_PADDING
  14. };
复制代码
复制代码
    开头和结尾分别用NGX_MODULE_V1和NGX_MODULE_V1_PADDING 填充了若干字段,就不去深究了。这里主要需要填入的信息从上到下以依次为context、指令数组、模块类型以及若干特定事件的回调处理函数(不需要可以置为NULL),其中内容还是比较好理解的,注意我们的echo是一个HTTP模块,所以这里类型是NGX_HTTP_MODULE,其它可用类型还有NGX_EVENT_MODULE(事件处理模块)和NGX_MAIL_MODULE(邮件模块)。
这样,整个echo模块就写好了,下面给出echo模块的完整代码:
  1. #include <ngx_config.h>
  2. #include <ngx_core.h>
  3. #include <ngx_http.h>
  4. /* Module config */
  5. typedef struct {
  6.      ngx_str_t  ed;
  7. } ngx_http_echo_loc_conf_t;
  8. static char *ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
  9. static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf);
  10. static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
  11. /* Directives */
  12. static ngx_command_t  ngx_http_echo_commands[] = {
  13.      { ngx_string("echo"),
  14.        NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
  15.        ngx_http_echo,
  16.        NGX_HTTP_LOC_CONF_OFFSET,
  17.        offsetof(ngx_http_echo_loc_conf_t, ed),
  18.        NULL },
  19.        ngx_null_command
  20. };
  21. /* Http context of the module */
  22. static ngx_http_module_t  ngx_http_echo_module_ctx = {
  23.      NULL,                                  /* preconfiguration */
  24.      NULL,                                  /* postconfiguration */
  25.      NULL,                                  /* create main configuration */
  26.      NULL,                                  /* init main configuration */
  27.      NULL,                                  /* create server configuration */
  28.      NULL,                                  /* merge server configuration */
  29.      ngx_http_echo_create_loc_conf,         /* create location configration */
  30.      ngx_http_echo_merge_loc_conf           /* merge location configration */
  31. };
  32. /* Module */
  33. ngx_module_t  ngx_http_echo_module = {
  34.      NGX_MODULE_V1,
  35.      &ngx_http_echo_module_ctx,             /* module context */
  36.      ngx_http_echo_commands,                /* module directives */
  37.      NGX_HTTP_MODULE,                       /* module type */
  38.      NULL,                                  /* init master */
  39.      NULL,                                  /* init module */
  40.      NULL,                                  /* init process */
  41.      NULL,                                  /* init thread */
  42.      NULL,                                  /* exit thread */
  43.      NULL,                                  /* exit process */
  44.      NULL,                                  /* exit master */
  45.      NGX_MODULE_V1_PADDING
  46. };
  47. /* Handler function */
  48. static ngx_int_t
  49. ngx_http_echo_handler(ngx_http_request_t *r)
  50. {
  51.      ngx_int_t rc;
  52.      ngx_buf_t *b;
  53.      ngx_chain_t out;
  54.      ngx_http_echo_loc_conf_t *elcf;
  55.      elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
  56.      if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
  57.      {
  58.          return NGX_HTTP_NOT_ALLOWED;
  59.      }
  60.      r->headers_out.content_type.len = sizeof("text/html") - 1;
  61.      r->headers_out.content_type.data = (u_char *) "text/html";
  62.      r->headers_out.status = NGX_HTTP_OK;
  63.      r->headers_out.content_length_n = elcf->ed.len;
  64.      if(r->method == NGX_HTTP_HEAD)
  65.      {
  66.          rc = ngx_http_send_header(r);
  67.          if(rc != NGX_OK)
  68.          {
  69.              return rc;
  70.          }
  71.      }
  72.      b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
  73.      if(b == NULL)
  74.      {
  75.          ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
  76.          return NGX_HTTP_INTERNAL_SERVER_ERROR;
  77.      }
  78.      out.buf = b;
  79.      out.next = NULL;
  80.      b->pos = elcf->ed.data;
  81.      b->last = elcf->ed.data + (elcf->ed.len);
  82.      b->memory = 1;
  83.      b->last_buf = 1;
  84.      rc = ngx_http_send_header(r);
  85.      if(rc != NGX_OK)
  86.      {
  87.          return rc;
  88.      }
  89.      return ngx_http_output_filter(r, &out);
  90. }
  91. static char *
  92. ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf
  93. {
  94.      ngx_http_core_loc_conf_t  *clcf;
  95.      clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  96.      clcf->handler = ngx_http_echo_handler;
  97.      ngx_conf_set_str_slot(cf,cmd,conf);
  98.      return NGX_CONF_OK;
  99. }
  100. static void *
  101. ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
  102. {
  103.      ngx_http_echo_loc_conf_t  *conf;
  104.      conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
  105.      if (conf == NULL) {
  106.          return NGX_CONF_ERROR;
  107.      }
  108.      conf->ed.len = 0;
  109.      conf->ed.data = NULL;
  110. }
  111. static char *
  112. ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  113. {
  114.      ngx_http_echo_loc_conf_t *prev = parent;
  115.      ngx_http_echo_loc_conf_t *conf = child;
  116.      ngx_conf_merge_str_value(conf->ed, prev->ed, "");
  117.      return NGX_CONF_OK;
  118. }
复制代码
复制代码
Nginx模块的安装:

Nginx不支持动态链接模块,所以安装模块需要将模块代码与Nginx源代码进行重新编译。安装模块的步骤如下:
1、编写模块config文件,这个文件需要放在和模块源代码文件放在同一目录下。文件内容如下:
  1. ngx_addon_name=模块完整名称
  2. HTTP_MODULES="$HTTP_MODULES 模块完整名称"
  3. NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/源代码文件名"
复制代码
复制代码

2、进入Nginx源代码,使用下面命令编译安装(--add-module=)

     本文只是简要介绍了Nginx模块的开发过程,由于篇幅的原因,不能面面俱到。因为目前Nginx的学习资料很少,如果读者希望更深入学习Nginx的原理及模块开发,那么阅读源代码是最好的办法。在Nginx源代码的core/下放有Nginx的核心代码,对理解Nginx的内部工作机制非常有帮助,http/目录下有Nginx HTTP相关的实现,http/module下放有大量内置http模块,可供读者学习模块的开发,另外在http://wiki.nginx.org/3rdPartyModules上有大量优秀的第三方模块,也是非常好的学习资料。

论坛徽章:
0
3 [报告]
发表于 2011-05-16 11:31 |只看该作者
好东西,学习了

论坛徽章:
0
4 [报告]
发表于 2011-05-17 14:05 |只看该作者
nginx好东西啊      居然用lua  有创意

论坛徽章:
0
5 [报告]
发表于 2011-05-20 17:24 |只看该作者
{:3_187:}{:3_187:}{:3_187:}
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP