NGINX源码阅读
目录
NGINX源码阅读指南
一、前言
- 源码版本:2018-10-02 nginx-1.15.5
- 环境说明:主要描述Darwin环境下的流程,与Linux环境下类似,Win32环境下可能会减少部分流程
- 运行模式:
- Darwin/Linux等*nix类系统使用多进程方式运行
- Win32使用多线程方式运行
- 命名规范:
ngx_开头的变量多为全局变量 - 模块命名:
ngx_model_name.c多为处理nginx配置中相应模块的配置处理,ngx_model_name_core_module.c多为该模块的核心(通用)处理逻辑
二、NGINX架构
1. 进程工作模式
1.1 多进程模式
-
master进程
- 接收外部信号发送给worker进程(如stop、restart、reload等)
- 监控worker进程运行状态,worker异常退出后重新启动新的worker进程
- 缓存管理
-
worker进程
- 处理基本网络事件,如http、mail请求等
1.2 单进程模式
- 调试情况下使用,直接使用单进程处理网络事件
1.3 核心配置参数
| 配置项 | 配置块 | 值类型 | 默认值 | 说明 |
|---|---|---|---|---|
daemon |
main | flag | 1 | 是否使用守护进程模式开启服务 |
master_process |
main | flag | 1 | 是否开启master管理进程,主要用于nginx开发调试 |
timer_resolution |
main | time | 0 | 控制gettimeofday()系统调用时机 |
pid |
main | string | logs/nginx.pid | 主进程pid的存放路径 |
lock_file |
main | string | logs/nginx.lock | 用于不支持原子操作的系统使用文件锁 |
worker_processes |
main | unit | string(auto) | 1 |
debug_points |
main | uint | 0 | 监测内部错误时中止或停止进程 |
user |
main | string | nobody | 设置worker进程运行的用户和用户组 |
worker_priority |
main | int | 0 | 设置worker进程的调度优先级 |
worker_cpu_affinity |
main | umask | string(auto) | — |
worker_rlimit_nofile |
main | uint | — | 修改worker进程的最大文件描述符限制 |
worker_rlimit_core |
main | uint | — | 修改worker进程的核心文件最大限制 |
worker_shutdown_timeout |
main | time | 0 | 设置worker进程的结束等待时间 |
working_directory |
main | string | — | 设置工作目录 |
env |
main | string | TZ | 设置需要的环境变量 |
load_module |
main | string | — | 用于加载动态模块 |
2. 启动阶段
2.1 master处理流程
-
初始化阶段
- 日志初始化(初始化日志格式,打开日志文件)
- SSL初始化
- 保存启动参数
- 解析配置文件路径
- 运行环境系统初始化(获取CPU缓存块大小、内存页大小、CPU核数)
-
配置加载阶段
- 压缩表初始化
- 模块预初始化(将所需加载的模块信息加入全局变量)
- 轮训初始化(加载配置、加载模块、创建共享内存、关闭无用socket)
-
启动管理阶段
- 检查配置文件错误
- 处理外部信号
- 注册信号处理器
- 守护进程模式下fork子进程
- 创建pid文件
- 启动worker进程循环
2.2 worker处理流程
-
初始化
- 设置进程执行优先级
- 设置进程可打开的最大文件描述符
- 根据nginx.conf设置进程运行的用户、用户组
- 设置CPU亲缘性
- 解除阻塞系统信号
- 执行各模块init_process
- 设置通道读事件回调
-
事件循环
- 从定时事件红黑树中查找最近定时器
- 检查worker是否繁忙,尝试获取事件监听锁
- 等待I/O事件唤起
- 处理accept事件
- 释放事件监听锁
- 处理时间到达的定时器
- 处理普通事件
-
信号处理
ngx_exiting:检查定时器中是否仍有未处理事件ngx_terminate:直接退出workerngx_quit:关闭定时器、socket监听、闲置连接ngx_reopen:轮转日志
2.3 缓存管理处理流程
- 设置进程类型为helper
- 关闭socket监听
- 重置最大连接数为512
- 初始化(设置优先级、文件描述符、用户组等)
- 设置定时器
- 循环处理事件及定时器
3. 事件模块
3.1 加载流程
graph TD
A[ngx_init_cycle] --> B[create_conf]
A --> C[command->set]
A --> D[init_conf]
A --> E[init_module]
F[ngx_event_process_init] --> G[init_event]
F --> H[init_process]
F --> I[add_event]
F --> J[process_events]
F --> K[event_handle]
L[ngx_worker_process_exit] --> M[exit_process]
N[ngx_master_process_exit] --> O[exit_master]
3.2 主要函数解析
事件结构
static ngx_event_module_t ngx_kqueue_module_ctx = {
&kqueue_name,
ngx_kqueue_create_conf, /* create configuration */
ngx_kqueue_init_conf, /* init configuration */
{
ngx_kqueue_add_event, /* add an event */
ngx_kqueue_del_event, /* delete an event */
ngx_kqueue_add_event, /* enable an event */
ngx_kqueue_del_event, /* disable an event */
NULL, /* add an connection */
NULL, /* delete an connection */
#ifdef EVFILT_USER
ngx_kqueue_notify, /* trigger a notify */
#else
NULL, /* trigger a notify */
#endif
ngx_kqueue_process_events, /* process the events */
ngx_kqueue_init, /* init the events */
ngx_kqueue_done /* done the events */
}
};
模块结构
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
关键函数
| 函数 | 说明 |
|---|---|
ngx_event_module_init |
检查文件描述符,分配共享内存 |
ngx_event_process_init |
初始化连接池、事件定时器等 |
ngx_event_accept |
处理accept事件,添加读事件 |
ngx_trylock_accept_mutex |
尝试获取accept锁 |
ngx_event_connect_peer |
连接上游服务 |
ngx_event_pipe |
从上游读数据写入下游 |
ngx_event_pipe_read_upstream |
读取上游数据 |
ngx_event_pipe_write_to_downstream |
写入下游数据 |
ngx_event_recvmsg |
处理UDP消息 |
3.3 事件配置
| 配置项 | 配置块 | 值类型 | 默认值 | 说明 |
|---|---|---|---|---|
worker_connections |
events | uint | 512 | 单worker可同时处理的最大连接数 |
use |
events | string | 根据系统选择 | 设置I/O事件通知机制 |
multi_accept |
events | string | off | 是否尽可能多接收等待队列中的socket |
accept_mutex |
events | string | off | 是否开启accept互斥锁 |
accept_mutex_delay |
events | uint | 500 | accept互斥锁延迟时间 |
debug_connection |
events | string | 无 | 开启debug日志的连接 |
三、HTTP服务
1. 初始化流程
create_main_conf
create_srv_conf
create_loc_conf
preconfiguration
init_main_conf
merge_srv_conf
merge_loc_conf
ngx_http_init_static_location_trees
ngx_http_init_phases
postconfiguration
ngx_http_optimize_servers
优化server信息列表:
- 添加各server所需地址的监听
- 设置监听参数(backlog、reuseport等)
- 将监听处理器设置为
ngx_http_init_connection
2. 建立连接流程
- 查找请求对应的server配置
- 设置当前server的读控制器(ssl、http2、http)
- 如果读事件未就绪,将连接放回队列并添加读事件
- 如果读事件已就绪,执行对应读控制器
2.1 HTTPS处理流程
- 尝试从socket读取数据
- 创建ssl连接并完成握手
- 如果客户端使用http2,进入http2处理流程
- 否则进入http处理流程
2.2 HTTP2处理流程
- 发送http2的headers frame
- 设置单个stream的流量控制
- 接收客户端请求数据
- 处理HEADERS frame(检查错误、处理优先级等)
- 添加读事件
- 处理输出队列
- 处理已建立的连接
2.3 HTTP处理流程
- 读取socket中已接收信息
- 从复用连接池中移除当前连接
- 处理header相关信息
- 进入请求处理流程
- 添加输出写事件
3. 请求处理流程
NGINX采用阶段式处理请求,共8个阶段:
| 阶段 | 类型 | 说明 | 主要处理函数 |
|---|---|---|---|
POST_READ_PHASE |
读取请求内容 | 获取客户端真实IP | ngx_http_realip_handler |
SERVER_REWRITE_PHASE |
server级别重写 | 重写URI | ngx_http_rewrite_handler |
FIND_CONFIG_PHASE |
寻找location | 查找匹配的location | ngx_http_core_find_config_phase |
REWRITE_PHASE |
location级别重写 | 重写URI | ngx_http_rewrite_handler |
POST_REWRITE_PHASE |
重写后处理 | 检查URI重写结果 | ngx_http_core_post_rewrite_phase |
PREACCESS_PHASE |
访问权限控制前 | 降级处理、连接限制 | ngx_http_degradation_handler, ngx_http_limit_conn_handler |
ACCESS_PHASE |
访问权限控制 | IP黑白名单、Basic认证 | ngx_http_access_handler, ngx_http_auth_basic_handler |
POST_ACCESS_PHASE |
访问权限控制后 | 处理权限结果 | ngx_http_core_post_access_phase |
PRECONTENT_PHASE |
内容生成前 | 请求镜像、文件检查 | ngx_http_mirror_handler, ngx_http_try_files_handler |
CONTENT_PHASE |
内容生成 | 生成响应 | ngx_http_core_content_phase, ngx_http_autoindex_handler |
LOG_PHASE |
日志记录 | 记录访问日志 | ngx_http_log_handler |
4. HTTP配置详解
4.1 核心配置参数
| 配置项 | 配置块 | 值类型 | 默认值 | 说明 |
|---|---|---|---|---|
variables_hash_max_size |
http | uint | 1024 | 变量哈希表最大大小 |
server_names_hash_max_size |
http | uint | 512 | 服务器名称哈希表最大大小 |
connection_pool_size |
http/server | string | 64*指针长度 | 每个连接的内存分配 |
request_pool_size |
http/server | string | 4096 | 每个请求的内存分配 |
client_header_timeout |
http/server | time | 60000 | 读取客户端请求头超时 |
client_header_buffer_size |
http/server | string | 1024 | 客户端请求头缓冲区大小 |
large_client_header_buffers |
http/server | uint size | 4 8192 | 大请求头缓冲区配置 |
keepalive_timeout |
http/server | time | 75000 | 长连接超时时间 |
keepalive_requests |
http/server | uint | 100 | 一个长连接的最大请求数 |
4.2 HTTP核心配置
| 配置项 | 配置块 | 值类型 | 默认值 | 说明 |
|---|---|---|---|---|
server |
http | 配置块 | 无 | 设置虚拟服务器配置 |
location |
server | 配置块 | 无 | 根据URI单独设置配置 |
listen |
http/server | string | *:80 | 设置监听地址 |
server_name |
http/server | string | "" | 设置虚拟服务器名称 |
root |
http/server/location | string | {0, NULL} | 设置请求根目录 |
alias |
location | string | 无 | 为特定location定义别名路径 |
limit_except |
location | 配置块 | 无 | 为特定location增加请求方法限制 |
client_max_body_size |
http/server/location | buf | 110241024 | 客户端请求体最大大小 |
sendfile |
http/server/location | flag | 0 | 是否使用sendfile发送文件数据 |
5. HTTP状态码详解
| 状态码 | 类型 | 说明 | 适用场景 |
|---|---|---|---|
100 |
Continue | 客户端应继续请求 | 未使用 |
101 |
Switching Protocols | 服务器正在切换协议 | proxy、uwsgi、scgi模块 |
200 |
OK | 请求成功 | 常规响应 |
201 |
Created | 资源已创建 | WebDAV模块 |
204 |
No Content | 请求成功但无内容 | 无内容响应 |
206 |
Partial Content | 部分内容响应 | Range请求 |
301 |
Moved Permanently | 永久重定向 | 重定向 |
302 |
Moved Temporarily | 临时重定向 | 重定向 |
304 |
Not Modified | 内容未修改 | 缓存验证 |
400 |
Bad Request | 语义有误 | 请求错误 |
401 |
Unauthorized | 需要认证 | 认证失败 |
403 |
Forbidden | 请求被拒绝 | 权限控制 |
404 |
Not Found | 资源未找到 | 资源不存在 |
408 |
Request Timeout | 请求超时 | 接收数据超时 |
409 |
Conflict | 请求与当前状态冲突 | WebDAV操作 |
413 |
Request Entity Too Large | 请求体过大 | 请求体超过限制 |
414 |
Request URI Too Large | 请求URI过长 | URI过长 |
416 |
Range Not Satisfiable | Range请求不合法 | Range请求错误 |
429 |
Too Many Requests | 请求过于频繁 | 速率限制 |
500 |
Internal Server Error | 服务器内部错误 | 程序错误 |
502 |
Bad Gateway | 网关错误 | 上游服务错误 |
503 |
Service Unavailable | 服务不可用 | 限流、上游错误 |
504 |
Gateway Timeout | 网关超时 | 上游响应超时 |
四、MAIL服务
1. 初始化流程
create_main_conf
create_srv_conf
init_main_conf
merge_srv_conf
ngx_mail_add_ports
ngx_mail_optimize_servers
优化监听列表:
- 如果相同端口有泛IP监听则忽略其他指定IP地址的监听
- 添加各server所需地址的监听
- 设置监听参数(backlog、rcvbuf等)
- 将监听处理器设置为
ngx_mail_init_connection
2. 建立连接流程
- 查找请求对应的server配置
- 设置当前server的读控制器(ssl、非ssl)
- 执行对应读控制器
2.1 SSL处理流程
- 创建ssl连接并完成握手
- 检查客户端证书
- 如果server配置中STARTTLS未关闭,执行对应协议的init_protocol
- 否则进入session初始化流程
2.2 非SSL处理流程
- 直接进入session初始化流程
3. session初始化流程
| 协议 | 处理流程 |
|---|---|
| IMAP | 添加读事件、写事件 |
| POP3 | 加盐处理、添加读事件、写事件 |
| SMTP | 添加读事件、写事件 |
4. 协议初始化流程
- 检查请求是否超时
- 创建临时缓冲区
- 读取客户端请求命令
- 解析命令并提取鉴权信息
- 发送至上游身份验证服务
- 根据返回结果处理
- 将结果发送给客户端
五、STREAM服务
1. 初始化流程
create_main_conf
create_srv_conf
preconfiguration
init_main_conf
merge_srv_conf
postconfiguration
ngx_stream_init_phase_handlers
ngx_stream_add_ports
ngx_stream_optimize_servers
优化监听列表:
- 相同端口有泛IP监听则忽略其他指定IP
- 添加各server所需地址的监听
- 设置监听参数(backlog、keepalive等)
2. 请求处理流程
| 阶段 | 类型 | 说明 | 处理函数 |
|---|---|---|---|
POST_ACCEPT_PHASE |
读取请求内容 | 获取客户端真实IP | ngx_stream_realip_handler |
PREACCESS_PHASE |
访问权限控制前 | 连接数限制 | ngx_stream_limit_conn_handler |
ACCESS_PHASE |
访问权限控制 | IP黑白名单 | ngx_stream_access_handler |
SSL_PHASE |
SSL连接 | 建立SSL连接 | ngx_stream_ssl_handler |
PREREAD_PHASE |
请求数据预读 | 读取请求数据 | ngx_stream_core_preread_phase |
CONTENT_PHASE |
内容生成 | 转发请求至上游 | ngx_stream_core_content_phase |
LOG_PHASE |
日志记录 | 记录访问日志 | ngx_stream_log_handler |
六、参考文档
- Nginx开发从入门到精通
- CPU缓存 - 维基百科
- Page (computer memory)
- OpenEvent
- Linux管道编程技术
- Nginx Caching
- 红黑树
- nginx documentation
- HTTP/2 Specification
- HTTP响应代码
- Configuring NGINX as a Mail Proxy Server
本文由佐柱撰写,转载请注明出处。欢迎访问我的小站。