不管是 C/C++ 技术栈, 还是 PHP,Java 技术栈, 从事后端开发的朋友对 nginx 一定不会陌生.
想要深入学习 nginx, 阅读源码一定是非常重要的一环, 但 nginx 源码量毕竟还是不算少, 一不小心就容易陷入某个细节, 迷失在茫茫码海之中.
如果有一张地图, 让我们开启上帝视角, 总览全局, 帮助我们快速学习整体框架结构, 又能不至于迷失其中那就再好不过了!
看到这篇文章的你有福了, 笔者花了不少时间, 把这件事给做了, 先来看个全貌(限于平台图片尺寸设定, 这里只能看个大概, 想获取高清大图请看文末):
下面选取一些关键部分来一窥神秘的 nginx.
主进程启动
nginx 主进程启动后, 进行一系列的初始化, 包括但不限于:
命令行参数解析
时间初始化
日志初始化
ssl 初始化
操作系统相关初始化
一致性 hash 表初始化
模块编号处理
核心初始化
另外一个最重要的初始化由 ngx_init_cycle()函数完成, 该函数围绕 nginx 中非常核心的一个全局数据结构 ngx_cycle_t 展开.
该函数完成了几个核心初始化:
配置文件解析
创建并监听 socket
初始化 nginx 各模块
nginx 核心模块群
nginx 是一个模块化设计的软件, 优秀的架构设计使得 nginx 可以扩展非常多的模块.
要一一描绘出这些模块显得有些杂乱和工作量巨大, 仅选取一些关键核心模块进行了展示:
每个模块有一个支持的命令解析列表, 在初始化过程中, 主进程将会遍历所有模块的命令列表, 进行配置文件中的命令解析, 如经常用的 ngx_http_proxy_module:
ngx_http_core_module 模块:
main 函数的最后, 根据是否启用多进程模型, 分别进入多进程版本的 ngx_master_process_cycle 和单进程版本的 ngx_single_process_cycle().
以常见的多进程版本为例, 进入该函数后, 首先设置进程名称为:"master process", 随后启动各工作子进程.
启动子进程
经过几层封装, 最终通过 fork 启动多个子进程:
除了工作子进程, 还启动了缓存管理进程.
之后主进程进入工作循环, 周期性更新时间并检查各全局标记, 根据不同情况给子进程发送不同信号.
子进程工作循环
子进程启动后, 进入 ngx_worker_process_cycle, 进行一些工作进程的初始化, 随后修改进程名称为:"worker process".
接着进入工作循环函数 ngx_process_events_and_timers, 在该函数中主要负责:
竞争互斥锁, 拿到锁的进程才能执行 accept 接受新的连接, 以此在多进程之间解决惊群效应
通过 epoll 异步 IO 模型处理网络 IO 事件, 包括新的连接事件和已建立连接发生的读写事件
处理定时器队列中到期的定时器事件, 定时器通过红黑树的方式存储
HTTP 请求预处理
当连接有数据产生时, 工作线程读取 socket 中到来的数据, 并根据 HTTP 协议格式进行解析, 最终封装成 ngx_request_t 请求对象, 提交处理.
HTTP 请求处理的 11 个阶段
在 nginx 中各 HTTP 模块是以挂载的形式串接而成, 以流水线工作模式进行 HTTP 请求的处理, nginx 将一个 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;
每阶段 (部分阶段保留, 不允许挂载) 允许多个模块挂载, 一个模块也可以挂载到多个阶段. 因此, 初次完成挂载的存储结构是一个二维数组的形式.
不过在初始化过程中, ngx_http_init_phase_handlers 函数将该二维数组转换成了一维数组. 下图是 nginx 中各模块挂载情况:
全景图
最后, 再来看一看全貌:
总结
nginx 不仅是一款优秀的高性能 web 服务器, 对于 C/C++ 技术栈的同学来说, 还是一个很好的学习对象, 其良好的架构设计, 优美的代码风格和经典的编程技法无一不值得细细品来.
不过限于笔者水平和时间有限, 虽然号称全景图, 但依然无法覆盖到 nginx 的方方面面, 欢迎读者朋友留言交流, 让此图日渐完善, 谢谢大家.
获取完整高清大图, 可在公众号里回复 "nginx" 自动获取.
往期热门回顾
一个 Java 对象的回忆录: 那些被锁住的日子 https://mp.weixin.qq.com/s/kcd7co6hS7j-RFSOiL-qYA
一个整数 + 1 引发的灾难 https://mp.weixin.qq.com/s/gZPxqZzY2rnngxvvzexWTw
一网打尽! 每个程序猿都该了解的黑客技术大汇总 https://mp.weixin.qq.com/s/V7wBdl-5W4ehTAnACQFjGQ
看过无数 Java GC 文章, 这 5 个问题你也未必知道! https://mp.weixin.qq.com/s/Bb2ugXYPR6r11QaGKbNBSw
Python 一键转 Jar 包, Java 调用 Python 新姿势! https://mp.weixin.qq.com/s/BEYQF305cWAOIwK2DtvyfQ
一个 Java 对象的回忆录: 垃圾回收 https://mp.weixin.qq.com/s/xp2S4_3UQTZ0TOIlVqM8uw
内核地址空间大冒险 3: 权限管理 https://mp.weixin.qq.com/s/WkQ5mVZrF7V2GrU-rsPOdQ
谁动了你的 HTTPS 流量? https://mp.weixin.qq.com/s/lxpHhHVIh6DktoHzrRLaKA
路由器里的广告秘密 https://mp.weixin.qq.com/s/7gM31s4-hTJTprJnxsHgEA
内核地址空间大冒险 2: 中断与异常 https://mp.weixin.qq.com/s/0b5e1_vwyvw8WOOHbVcQyQ
DDoS 攻击: 无限战争 https://mp.weixin.qq.com/s/JTr1-5nPtseAYXfvJdamVg
一条 SQL 注入引出的惊天大案 https://mp.weixin.qq.com/s/lerhjpAEdp4RiwsmetyqPg
内核地址空间大冒险: 系统调用 https://mp.weixin.qq.com/s/esc9gWg42vyPkT58HCKNgg
一个 HTTP 数据包的奇幻之旅 https://mp.weixin.qq.com/s/suzicCzb2g5b8NN71S5Ngw
一个 DNS 数据包的惊险之旅 https://mp.weixin.qq.com/s/_TOFIPGIeMHhVxIVToxmiQ
我是一个流氓软件线程 https://mp.weixin.qq.com/s/-ggUa3aWkjjHjr9VwQL9TQ
来源: https://www.cnblogs.com/xuanyuan/p/12710715.html