Nginx 模块开发之模块开发本质

https://juejin.cn/post/7127194402631450660

https://juejin.cn/post/7127219328151191589

整个Nginx的生命周期大概分为:

配置文件读取并解析阶段

具体请求处理阶段

我们所有的工作都在读取配置文件并解析阶段完成,在具体的请求处理阶段,一般会回调提前注册好的一系列回调函数。

总体来说,Nginx模块开发就是在配置文件读取并解析阶段,设置一系列变量以及回调函数。

每个Nginx模块都是 ngx_module_t 结构体变量,其中存储了模块的信息,以及一系列的回调函数。

我们开发Nginx模块,实际上就是编写一个 ngx_module_t 结构体变量。

typedef struct ngx_module_s ngx_module_t;

struct ngx_module_s {

复制代码
/* private part is omitted */

void                 *ctx;
ngx_command_t        *commands;
ngx_uint_t            type;

ngx_int_t           (*init_master)(ngx_log_t *log);

ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

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);
void                (*exit_process)(ngx_cycle_t *cycle);

void                (*exit_master)(ngx_cycle_t *cycle);

/* stubs for future extensions are omitted */

};

可以看到其中大部分类型都是简单类型,要么是int类型,要么是函数指针类型。 重点需要关注其中的两个部分:ctx,commands。

首先看一下 commands ,可以看到其类型为 ngx_command_t*,因此应该传入一个 ngx_command_t 数组。

typedef struct ngx_command_s ngx_command_t;

struct ngx_command_s {

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;

};

ngx_command_t 类型中定义了每个命令的相关信息,包括名称,类型,set 回调函数等等

接下来看一下 ctx ,可以看到其类型为 void*,因此这个变量的类型是不确定的。也就是说对于不同类型的模块拥有不同类型的 ctx。从 官方文档 中可以看到,模块类型分为以下几种。

NGX_CORE_MODULE

NGX_EVENT_MODULE

NGX_HTTP_MODULE

NGX_MAIL_MODULE

NGX_STREAM_MODULE

这里列出了常用的两种模块类型 NGX_CORE_MODULE,NGX_HTTP_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;

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_core_module_t 类型,还是 ngx_http_module_t 类型,本质上只是定义了一系列模块创建相关回调函数。

由此模块定义部分分析完毕。总体来看,编写模块就是编写一个 ngx_module_t 结构体,只需要按照其各部分的定义,将结构体填写完整即可。重点需要填写其中的ctx,commands两部分。

Nginx 作为一个代理服务器,本质上就是处理网络请求。

以 HTTP 请求为例,本质就是一个 Nginx(request* req, response* r) 处理函数,输入请求,输出响应。

因此对于 Nginx 模块来说,其功能就是处理 request 以及 response。 根据 request 发送到后端程序,将后端程序返回的 response 发送到客户端。

在整个处理流程中,Nginx 模块有几个地方可以做一些处理。

从客户端接收到 request,并且发送到后端之前。

从后端接收到 response,并且发送到客户端之前。

在第一个阶段,Nginx 模块可以修改 request 中的内容,可以决定将 request 发送给哪一个后端程序,或者不发送给后端程序,自己直接创建一个 response 。

在第二个阶段,Nginx 模块可以修改 response 中的内容。

而 Nginx 本身处理了大量网络连接的细节问题,例如数据包的收发,请求路由,连接的重用,大量连接并发问题等等。

因此在开发 Nginx 模块之前,首先需要明白该模块需要在上述哪几个部分进行处理。

Nginx 模块中可以定义一系列回调函数,在这些阶段中进行回调。

下面简单介绍 Nginx 模块中的一些回调接口。

之前提到,编写 Nginx 模块,本质就是填写 ngx_module_t 结构体变量。 ngx_module_t 结构体中就定义了几个回调函数。通过名字大概能猜出其回调时机。 其中参数为 ngx_cycle_t, ngx_log_t

typedef struct ngx_module_s ngx_module_t;

struct ngx_module_s {

复制代码
/* private part is omitted */

void                 *ctx;
ngx_command_t        *commands;
ngx_uint_t            type;

ngx_int_t           (*init_master)(ngx_log_t *log);

ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

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);
void                (*exit_process)(ngx_cycle_t *cycle);

void                (*exit_master)(ngx_cycle_t *cycle);

/* stubs for future extensions are omitted */

};

ngx_command_t 结构体中定义了 set 回调函数

typedef struct ngx_command_s ngx_command_t;

struct ngx_command_s {

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;

};

ngx_core_module_t 结构体中定义了 create_conf init_conf 回调函数

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;

ngx_http_module_t 结构体中定义了如下几个回调函数。

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_top_header_filter 以及 ngx_http_top_body_filter 来添加自定义的 filter。

对于http请求来说,可以通过以下宏来获取 Nginx 模块的相应配置信息, 其中提供了一些回调函数接口。

ngx_http_conf_get_module_main_conf(cf, module)

ngx_http_conf_get_module_srv_conf(cf, module)

ngx_http_conf_get_module_loc_conf(cf, module)

ngx_http_conf_get_module_main_conf 宏返回的是 ngx_http_core_main_conf_t 类型,其中 phases 字段定义了一系列回调函数。

回调函数添加示例:

ngx_http_handler_pt *h;

ngx_http_core_main_conf_t *cmcf;

cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);

if (h == NULL) {

return NGX_ERROR;

}

*h = ngx_http_foo_handler;

return NGX_OK;

typedef struct {

ngx_array_t servers; /* ngx_http_core_srv_conf_t */

复制代码
ngx_http_phase_engine_t    phase_engine;

ngx_hash_t                 headers_in_hash;

ngx_hash_t                 variables_hash;

ngx_array_t                variables;         /* ngx_http_variable_t */
ngx_array_t                prefix_variables;  /* ngx_http_variable_t */
ngx_uint_t                 ncaptures;

ngx_uint_t                 server_names_hash_max_size;
ngx_uint_t                 server_names_hash_bucket_size;

ngx_uint_t                 variables_hash_max_size;
ngx_uint_t                 variables_hash_bucket_size;

ngx_hash_keys_arrays_t    *variables_keys;

ngx_array_t               *ports;

ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

} ngx_http_core_main_conf_t;

typedef struct {

ngx_array_t handlers;

} ngx_http_phase_t;

ngx_http_conf_get_module_srv_conf 宏返回的是 ngx_http_core_srv_conf_t 类型。

typedef struct {

/* array of the ngx_http_server_name_t, "server_name" directive */

ngx_array_t server_names;

复制代码
/* server ctx */
ngx_http_conf_ctx_t        *ctx;

u_char                     *file_name;
ngx_uint_t                  line;

ngx_str_t                   server_name;

size_t                      connection_pool_size;
size_t                      request_pool_size;
size_t                      client_header_buffer_size;

ngx_bufs_t                  large_client_header_buffers;

ngx_msec_t                  client_header_timeout;

ngx_flag_t                  ignore_invalid_headers;
ngx_flag_t                  merge_slashes;
ngx_flag_t                  underscores_in_headers;

unsigned                    listen:1;

#if (NGX_PCRE)

unsigned captures:1;

#endif

复制代码
ngx_http_core_loc_conf_t  **named_locations;

} ngx_http_core_srv_conf_t;

ngx_http_conf_get_module_loc_conf 宏返回的是 ngx_http_core_loc_conf_t 类型,其中 handler 字段定义了一系列回调函数。

typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t;

struct ngx_http_core_loc_conf_s {

ngx_str_t name; /* location name */

#if (NGX_PCRE)

ngx_http_regex_t *regex;

#endif

复制代码
unsigned      noname:1;   /* "if () {}" block or limit_except */
unsigned      lmt_excpt:1;
unsigned      named:1;

unsigned      exact_match:1;
unsigned      noregex:1;

unsigned      auto_redirect:1;

#if (NGX_HTTP_GZIP)

unsigned gzip_disable_msie6:2;

unsigned gzip_disable_degradation:2;

#endif

复制代码
ngx_http_location_tree_node_t   *static_locations;

#if (NGX_PCRE)

ngx_http_core_loc_conf_t **regex_locations;

#endif

复制代码
/* pointer to the modules' loc_conf */
void        **loc_conf;

uint32_t      limit_except;
void        **limit_except_loc_conf;

ngx_http_handler_pt  handler;

/* location name length for inclusive location with inherited alias */
size_t        alias;
ngx_str_t     root;                    /* root, alias */
ngx_str_t     post_action;

ngx_array_t  *root_lengths;
ngx_array_t  *root_values;

ngx_array_t  *types;
ngx_hash_t    types_hash;
ngx_str_t     default_type;

off_t         client_max_body_size;    /* client_max_body_size */
off_t         directio;                /* directio */
off_t         directio_alignment;      /* directio_alignment */

size_t        client_body_buffer_size; /* client_body_buffer_size */
size_t        send_lowat;              /* send_lowat */
size_t        postpone_output;         /* postpone_output */
size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
size_t        read_ahead;              /* read_ahead */
size_t        subrequest_output_buffer_size;
                                       /* subrequest_output_buffer_size */

ngx_http_complex_value_t  *limit_rate; /* limit_rate */
ngx_http_complex_value_t  *limit_rate_after; /* limit_rate_after */

ngx_msec_t    client_body_timeout;     /* client_body_timeout */
ngx_msec_t    send_timeout;            /* send_timeout */
ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
ngx_msec_t    lingering_time;          /* lingering_time */
ngx_msec_t    lingering_timeout;       /* lingering_timeout */
ngx_msec_t    resolver_timeout;        /* resolver_timeout */
ngx_msec_t    auth_delay;              /* auth_delay */

ngx_resolver_t  *resolver;             /* resolver */

time_t        keepalive_header;        /* keepalive_timeout */

ngx_uint_t    keepalive_requests;      /* keepalive_requests */
ngx_uint_t    keepalive_disable;       /* keepalive_disable */
ngx_uint_t    satisfy;                 /* satisfy */
ngx_uint_t    lingering_close;         /* lingering_close */
ngx_uint_t    if_modified_since;       /* if_modified_since */
ngx_uint_t    max_ranges;              /* max_ranges */
ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */

ngx_flag_t    client_body_in_single_buffer;
                                       /* client_body_in_singe_buffer */
ngx_flag_t    internal;                /* internal */
ngx_flag_t    sendfile;                /* sendfile */
ngx_flag_t    aio;                     /* aio */
ngx_flag_t    aio_write;               /* aio_write */
ngx_flag_t    tcp_nopush;              /* tcp_nopush */
ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
ngx_flag_t    absolute_redirect;       /* absolute_redirect */
ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
ngx_flag_t    port_in_redirect;        /* port_in_redirect */
ngx_flag_t    msie_padding;            /* msie_padding */
ngx_flag_t    msie_refresh;            /* msie_refresh */
ngx_flag_t    log_not_found;           /* log_not_found */
ngx_flag_t    log_subrequest;          /* log_subrequest */
ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
ngx_uint_t    server_tokens;           /* server_tokens */
ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
ngx_flag_t    etag;                    /* etag */

#if (NGX_HTTP_GZIP)

ngx_flag_t gzip_vary; /* gzip_vary */

复制代码
ngx_uint_t    gzip_http_version;       /* gzip_http_version */
ngx_uint_t    gzip_proxied;            /* gzip_proxied */

#if (NGX_PCRE)

ngx_array_t gzip_disable; / gzip_disable */

#endif

#endif

#if (NGX_THREADS || NGX_COMPAT)

ngx_thread_pool_t *thread_pool;

ngx_http_complex_value_t *thread_pool_value;

#endif

#if (NGX_HAVE_OPENAT)

ngx_uint_t disable_symlinks; /* disable_symlinks */

ngx_http_complex_value_t *disable_symlinks_from;

#endif

复制代码
ngx_array_t  *error_pages;             /* error_page */

ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */

ngx_open_file_cache_t  *open_file_cache;
time_t        open_file_cache_valid;
ngx_uint_t    open_file_cache_min_uses;
ngx_flag_t    open_file_cache_errors;
ngx_flag_t    open_file_cache_events;

ngx_log_t    *error_log;

ngx_uint_t    types_hash_max_size;
ngx_uint_t    types_hash_bucket_size;

ngx_queue_t  *locations;

#if 0

ngx_http_core_loc_conf_t *prev_location;

#endif

};

相关推荐
不知更鸟4 小时前
Django 项目是什么
数据库·sqlite
我是小超人-雨石花5 小时前
Jenkins&Robot Framework持续集成
运维·jenkins·ci
有一个好名字7 小时前
MyBatis-Plus 三种数据库操作方式详解 + 常用方法大全
数据库·mybatis
-Xie-7 小时前
Redis(八)——多线程与单线程
java·数据库·redis
wanhengidc7 小时前
云手机的软件核心是什么
运维·服务器·web安全·游戏·智能手机
G探险者7 小时前
为什么 VARCHAR(1000) 存不了 1000 个汉字? —— 详解主流数据库“字段长度”的底层差异
数据库·后端·mysql
芬加达8 小时前
jvm八股
运维·服务器·jvm
小兔薯了8 小时前
11. Linux firewall 防火墙管理
linux·运维·服务器
Albert Tan8 小时前
Oracle EBS R12.2.14 清理FND_LOBS并释放磁盘空间
数据库·oracle
L.EscaRC9 小时前
图数据库Neo4j原理与运用
数据库·oracle·neo4j