1.第三方模块源码的快速阅读方法
- 分析模块提供的config文件
- 理解configure中添加第三方模块的顺序
- 确认第三方模块的编译方式
- 分析ngx_module_t模块 实现了哪些在进程启动、退出时的回调方法?
- 分析ngx_command_t数组看看支持哪些配置指令 了解通用的指令值解析方法
- 对于http模块,分析ngx_http_module_t中在http#解析前后实现了哪些回调方法
- 根据第3、4步,找出该模块生效方式
- 在11个阶段中的哪一个阶段处理请求
- 在过滤响应中生效吗?
- 在负载均衡中生效吗?
- 是否提供了新的变量?
config结构
# 定义模块名称,用于显示执行结果
# 设定ngx_addon_name变量,例如
adding module in /home/web/ngx_cache_purge/
ngx_http_cache_purge_module was configured
# 将HITTP模块添加至数组中
# 扩展HTTP_MODULES变量,例如
HTTP_MODULES="$HTTP_MODULES ngx_http_cache_purge_module"
# 将模块源代码文件添加至Nginx 扩展NGX ADDON SRCS变量,例如:
NGX_ADDON_SRCS="$NGX_ADDON_SRCS
$ngx_addon_dir/ngx_cache_purge_module.c"
2.configure脚本分析
- 解析configure参数,生成编译参数: auto/options 第三方模块通过--add-odule参数会添加模块至NGX ADDONS变量
- 针对不同操作系统、体系架构、编译器,选择特性 (例如linux中的epoll或者windows中的iocp)及生成相应编译参数
- 根据所有模块生成ngx modules.c及makefle
- 在屏幕上显示configure执行结果: auto/summary
3.Nginx的启动流程
Nginx启动、退出时回调方法
init_module #在master进程中调用
init_process #在worker进程中调用
exit_process #在worker进程退出时调用
exit_master #master进程退出时调用
进程核心描述:ngx_cycle_t
Nginx 启动流程
- 根据命行得到配置文件路径
- 如果处于升级中则听环量里迷的听
- 调用所有核模块的create_conf方法生成存效配置项的结构体
- 针对所有核模块解析nginx.conf配置文件
- 调用所有核心模的ini_conf方法
- 创建目录、打开文件、初始化共享内存等进程间通信方式
- 打开nginx模从配置文中读取监听端口
- 用所有模块的module_t方法
- 进入single模式
- 调用所有模块的init_process方法
- 进入master选程
- 启动wrker进程
- 调用所有模执的init.process方法
- 启动cache manager进程
- 启动cache loader子选程
- 关闭又进程启动监听的端口
Master进程循环
- 监控子进程,假如所有子程已返回live=1
- 删除存储master进程号的pid文件
- 调用所有模块的exit_mster方法
- 关闭所有监听端口
- 销毁内存池
- 向所有子进程发送TERM信号
- 向所有子进发送QUIT信号
- 关闭所有监听端口
- 重新读配置件
- 启动worker子程
- 启动cache manage子进程
- 间所有子进程发送QUIT信号量
- 启动worker子进程
- 启动cache manage子进程
- 重新打开所有文件
- 向所有子进程发送USR1信新打开文件
- 运新的nginx二进制文件
- 向所有子程序发送QUIT信号 要求关闭服务
Worker进程循环
- 对当前所有活动连接调用读事件方法处理关闭连接事件
- 检并分发、处理事件
- 调用所有模块的exit_process方法 -> 销毁内存池
- 添加shutdow_timer定时器
- 关闭所有监听句柄并设置ngx_exiting标志
- 重新打开所有文件
4.HTTP第三方模块的初始化
http模块初始化
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;
HTTP模块的11个阶段: 初始化
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0
NGX_HTTP_SERVER_REWRITE_PHASE
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE
NGX_HTTP_POST_REWRITE_PHASE
NGX_HTTP_PREACCESS_PHASE
NGX_HTTP_ACCESS_PHASE
NGX_HTTP_POST_ACCESS_PHASE.
NGX_HTTP_PRECONTENT_PHASE
NGX_HTTP_CONTENT_PHASE
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);
static ngx_int_t ngx_http_rewrite_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
void ngx_http_cache_purge_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_log_handler(ngx_http_request_t *r);
添加模块至11个阶段的两种常用方式
1. 在HTTP模块解析完配置文件后
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf-
>phases[NGX_HTTP_ACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_auth_basic_handler;
2. 在HTTP模块解析配置文件,检测到某个开关被打开后
- 进入content价段处理请求
- 所有反向代理模块使用该方式,因其对其他content价段模块具有排他性
过滤模块的单链表
# 处理响应的方法:
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt) (ngx_http_request_t *r, ngx_chain_t *chain);
# 链表头:
extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
# 链表指针:
static ngx http output header filter pt ngx http next header filter:static ngx http output body filter pt ngx http next body filter;
添加过滤模块的方式
# 定义本地指针
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
# 在哪里插入链表
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_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_ssi_header_filter;
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_ssi_body_filter;
5.if指令的正确用法
rewrite的脚本指今
- set
- if
- break
- rewrite
- return
协程与rewrite脚本
上下文信息
- 指令集
- 数据栈
- 下一条指令
调度系统:切换任务
- 载入新任务
- 载入数据栈
- 重置下一条指令
rewrite脚本:设置变量
ngx_http_rewrite_loc_conf_t->codes # 所有脚本指令
ngx_http_script_engine_t # 请求的执行上下文
# 当前指令 ip
# 数据栈 sp
脚本式编程: 指令的实现
typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);
typedef struct {
ngx_http_script_code_pt code;
… …
} ngx_http_script_..._code_t;
当if指令块连续出现时
location /only-one-if
{
set $true 1;
if ($true) {
add_header X-First 1;
}
if ($true) {
add_header X-Second 2;
}
return 204;
}
## if为真时,其升内的值指令会被赋予请求
当if块内出现反向代理时
location /crash {
set $true 1;
if ($true) {
proxy_pass http://127.0.0.1:9000;
}
if ($true) {
# no handler here
}
}
if指令出现问题的原因
- if指令在rcwritc阶段执行
- if块中的配置,会在if条件为真时,替换当前请求的配置,if同样向上继承父配置
- 当在rewrite阶段顺序执行时,每次if为真都会替换当前请求的配置
- if{} 的配置,会影响rewrite阶段之后的阶段执行
使用if指令的正确姿势
理解if的执行流程
- 基本脚本编程的rewrite模块中的5个指令,在rewrite阶段执行,而此阶段后还有7个阶段中的许多模块可能会执行
- if指令一旦为真后,会替换当前请求的配置块,请务必确保if{}中的指令可以正确处理请求,不依赖if{}块外的指令
- break会阻断后续rewrite阶段指令的执行
6.解读Nginx的核心转储文件
coredump核心转储文件
Syntax: worker_rlimit_core size;
Syntax: working_directory directory;
例子
events {
worker connections 1024
}
worker_rlimit_core 90m;
working_directory /tmp/nginxcore/;
thread_pool default_threads=3 max queue=65536;
# 测试
cd /tmp/nginxcore
# 查看文件
ll
# 无文件
# 找worker id
ps -ef grep nginx
kill -SIGSEGV 21566
ll
# core.21566 多了这个文件
gdb /usr/local/nginx/sbin/nginx
# 查看 ngx_http_init_connection信息
gdb调试基本命令
bt/backstrace # 显示函数栈调用情况
f/frame # 显示某一栈顿的详细情况
p/print # 显示某一变量的值
l/list # 显示当前行附近代码
x # 显示内存中具体地址指向的值
i/info # 显示信息,如ir为显示寄存器信息
启用多进程模式
daemon on | off;