Nginx 模块开发

本文详细介绍了Nginx模块的开发,包括Handler模块和Filter模块的角色与结构,讲解了如何创建配置结构、处理请求、发送响应以及挂载模块。同时涵盖了配置指令解析、上下文管理等内容,为开发Nginx新模块提供了全面指导。

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

关注校招、实习信息


Nginx 模块概述

Nginx 模块有三种角色:

  • 处理请求并产生输出的 Handler 模块
  • 处理由 Handler 产生的输出的 Filter(滤波器)模块;
  • 当出现多个后台服务器时,Load-balancer (负载均衡器)模块负责选择其中一个后台服务器发送请求;

        通常,服务器启动时,任何 Handler 模块都有可能去处理配置文件中的  location 定义。若出现多个 Handler 模块被配置成需要处理某一特定的  location 时,最终只有其中一个 Handler 模块是成功的。 Handler 模块有三种返回方式:
  1. 接收请求,并成功返回;
  2. 接收请求,但是出错返回;
  3. 拒绝请求,使默认的 Handler 模块处理该请求;
        若 Handler 模块的作用是把一个请求反向代理到后台服务器,则会出现另一种类型的空间模块——  Load-balancer。  Load-balancer 负责决定将请求发送给哪个后端服务器。 Nginx 目前支持两种  Load-balancer 模块: round-robin (轮询,处理请求就像打扑克时发牌那样)和 "IP hash" method(众多请求时,保证来自同一 IP 的请求被分发的同一个后端服务器)。
        若 Handler 模块没有产生错误返回时,则会调用  Filter 模块。每个 location 配置里都可以添加多个 Filter 模块 ,因此响应可以被压缩和分块。 Filter 模块之间的处理顺序是在编译时就已经确定的。 Filter 模块采用“ CHAIN OF RESPONSIBILITY”链式的设计模式。当有请求到达时,请求依次经过这条链上的全部  Filter 模块,一个 Filter 被调用并处理,接下来调用下一个 Filter,直到最后一个 Filter 被调用完成, Nginx 才真正完成响应流程。

        总结如下,典型的处理形式如下:
Client sends HTTP request → Nginx chooses the appropriate handler based on the location config →  
 (if applicable) load-balancer picks a backend server →   
Handler does its thing and passes each output buffer to the first filter →   
First filter passes the output to the second filter → second to third → third to fourth → etc.   
→ Final response sent to client  

 

Nginx 模块的结构

模块的配置结构

        模块最多可以定义三个配置结构: mainserverlocation。绝大多数模块仅需要一个 location 配置。名称约定如下以 ngx_http_<module name>_(main|srv|loc)_conf_t为例的 dav module
typedef struct {
      ngx_uint_t methods;
      ngx_flag_t create_full_put_path;
      ngx_uint_t access;
} ngx_http_dav_loc_conf_t;
       Nginx 模块的数据结构如下定义:
/* Nginx 模块的数据结构 */
#define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

struct ngx_module_s {
    /* 模块类别由type成员决定,ctx_index表示当前模块在type类模块中的序号 */
    ngx_uint_t            ctx_index;
    /* index 区别与ctx_index,index表示当前模块在所有模块中的序号 */
    ngx_uint_t            index;

    /* spare 序列保留变量,暂时不被使用 */
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;

    /* 当前模块的版本 */
    ngx_uint_t            version;

    /* ctx指向特定类型模块的公共接口,例如在HTTP模块中,ctx指向ngx_http_module_t结构体 */
    void                 *ctx;
    /* 处理nginx.conf中的配置项 */
    ngx_command_t        *commands;
    /* type表示当前模块的类型 */
    ngx_uint_t            type;

    /* 下面的7个函数指针是在Nginx启动或停止时,分别调用的7中方法 */
    /* 在master进程中回调init_master */
    ngx_int_t           (*init_master)(ngx_log_t *log);

    /* 初始化所有模块时回调init_module */
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    /* 在worker进程提供正常服务之前回调init_process初始化进程 */
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    /* 初始化多线程 */
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    /* 退出多线程 */
    void                (*exit_thread)(ngx_cycle_t *cycle);
    /* 在worker进程停止服务之前回调exit_process */
    void                (*exit_process)(ngx_cycle_t *cycle);

    /* 在master进程退出之前回调exit_master */
    void                (*exit_master)(ngx_cycle_t *cycle);

    /* 保留字段,未被使用 */
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};
        在该数据结构中,其中最重要的是两个成员 ctxcommands,这里两个成员会在分别在下面的模块配置指令和模块上下文中讲解;若是 HTTP 模块时, type 字段必须定义为 NGX_HTTP_MODULE

模块配置指令

        模块指令存储在一个 ngx_command_t 类型的静态数组结构中,例如:
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
};
ngx_command_t 类型定义在  core/ngx_conf_file.h
struct ngx_command_s {
    /* 配置项名称 */
    ngx_str_t             name;
    /* 配置项类型,type将指定配置项可以出现的位置以及携带参数的个数 */
    ngx_uint_t            type;
    /* 处理配置项的参数 */
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    /* 在配置文件中的偏移量,conf与offset配合使用 */
    ngx_uint_t            conf;
    ngx_uint_t            offset;
    /* 配置项读取后的处理方法,必须指向ngx_conf_post_t 结构 */
    void                 *post;
};
name :配置指令的名称;
type    :该配置的类型,指定配置项的出现位置以及可携带参数的个数,下面规定只是其中一部分,更多信息可查看文件 core/ngx_conf_file.h
NGX_HTTP_MAIN_CONF:  directive is valid in the main config
NGX_HTTP_SRV_CONF:   directive is valid in the server (host) config
NGX_HTTP_LOC_CONF:   directive is valid in a location config
NGX_HTTP_UPS_CONF:   directive is valid in an upstream config
NGX_CONF_NOARGS:     directive can take 0 arguments
NGX_CONF_TAKE1:      directive can take exactly 1 argument
NGX_CONF_TAKE2:      directive can take exactly 2 arguments
…
NGX_CONF_TAKE7:      directive can take exactly 7 arguments
NGX_CONF_FLAG:       directive takes a boolean ("on" or "off")
NGX_CONF_1MORE:      directive must be passed at least one argument
NGX_CONF_2MORE:      directive must be passed at least two arguments
set     :这是一个函数指针,当 Nginx 在解析配置时,若遇到该配置指令,将会把读取到的值传递给这个函数进行分解处理。因为具体每个配置指令的值如何处理,只有定义这个配置指令的人是最清楚的。来看一下这个函数指针要求的函数原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
          该函数处理成功时,返回 NGX_OK,否则返回 NGX_CONF_ERROR 或者是一个自定义的错误信息的字符串。该函数传入三个类型的参数:
  1. cf    :指向ngx_conf_t  结构的指针,该结构包括从配置指令传递的参数;
  2. cmd:指向当前ngx_command_t 结构;
  3. conf:指向模块配置结构;
        为了方便实现对配置指令参数的读取, Nginx 已经默认提供了对一些标准类型的参数进行读取的函数,可以直接赋值给 set 字段使用。下面是一部分已经实现的 set 类型函数,更多可参考文件 core/ngx_conf_file.h
  • ngx_conf_set_flag_slot : 把 "
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值