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
};