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

};

相关推荐
青木沐6 分钟前
Jenkins介绍
运维·jenkins
WTT001141 分钟前
2024楚慧杯WP
大数据·运维·网络·安全·web安全·ctf
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
了一li1 小时前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
日记跟新中1 小时前
Ubuntu20.04 修改root密码
linux·运维·服务器
唐小旭2 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python
码农君莫笑2 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
BUG 4042 小时前
Linux——Shell
linux·运维·服务器
别致的影分身2 小时前
使用C语言连接MySQL
数据库·mysql