nginx 学习6-源码-源码结构/启动流程/http模块初始/if指令

242 阅读2分钟

1.第三方模块源码的快速阅读方法

  1. 分析模块提供的config文件
    • 理解configure中添加第三方模块的顺序
    • 确认第三方模块的编译方式
  2. 分析ngx_module_t模块 实现了哪些在进程启动、退出时的回调方法?
  3. 分析ngx_command_t数组看看支持哪些配置指令 了解通用的指令值解析方法
  4. 对于http模块,分析ngx_http_module_t中在http#解析前后实现了哪些回调方法
  5. 根据第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 启动流程

  1. 根据命行得到配置文件路径
  2. 如果处于升级中则听环量里迷的听
  3. 调用所有核模块的create_conf方法生成存效配置项的结构体
  4. 针对所有核模块解析nginx.conf配置文件
  5. 调用所有核心模的ini_conf方法
  6. 创建目录、打开文件、初始化共享内存等进程间通信方式
  7. 打开nginx模从配置文中读取监听端口
  8. 用所有模块的module_t方法
  9. 进入single模式
  10. 调用所有模块的init_process方法
  11. 进入master选程
  12. 启动wrker进程
  13. 调用所有模执的init.process方法
  14. 启动cache manager进程
  15. 启动cache loader子选程
  16. 关闭又进程启动监听的端口

Master进程循环

  1. 监控子进程,假如所有子程已返回live=1
  2. 删除存储master进程号的pid文件
  3. 调用所有模块的exit_mster方法
  4. 关闭所有监听端口
  5. 销毁内存池
  6. 向所有子进程发送TERM信号
  7. 向所有子进发送QUIT信号
  8. 关闭所有监听端口
  9. 重新读配置件
  10. 启动worker子程
  11. 启动cache manage子进程
  12. 间所有子进程发送QUIT信号量
  13. 启动worker子进程
  14. 启动cache manage子进程
  15. 重新打开所有文件
  16. 向所有子进程发送USR1信新打开文件
  17. 运新的nginx二进制文件
  18. 向所有子程序发送QUIT信号 要求关闭服务

Worker进程循环

  1. 对当前所有活动连接调用读事件方法处理关闭连接事件
  2. 检并分发、处理事件
  3. 调用所有模块的exit_process方法 -> 销毁内存池
  4. 添加shutdow_timer定时器
  5. 关闭所有监听句柄并设置ngx_exiting标志
  6. 重新打开所有文件

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;

参考

time.geekbang.org/course/intr…