Day 03 / 共 20 天 · 第 1 周 全景与请求生命周期
启动流程:从命令到就绪
今天看 APISIX 怎么"开机":从 apisix start,到根据 config.yaml 生成 nginx.conf,再到 init/init_worker 初始化各子系统。理解启动,才知道处理请求时那些对象是何时准备好的。
📍 你在整条链的位置
Nginx 阶段 D2→
Day3 启动(开机)→
请求生命周期 D4→
api_ctx D5
L01
从命令到就绪:一张时序图
启动时序:CLI 读配置→生成 nginx.conf→拉起 OpenResty;OpenResty master 跑 http_init,每 worker 跑 http_init_worker,就绪。
L02
CLI 和"网关本体"是两回事
🤔
apisix start 背后是什么?
很多人以为 apisix 命令就是网关。其实不是。💡 CLI 是"点火开关",OpenResty 是"发动机"
apisix/cli/(apisix.lua:26-31 设 Lua 包路径)是管理命令行——只在你敲命令时跑一下:生成配置、启停 OpenResty,然后退出。真正处理请求的是 OpenResty 加载的 apisix/init.lua(长期运行)。别混淆
cli/ 的活是"准备好 nginx.conf、把 OpenResty 拉起来"就下班了;init.lua 才是长期在岗处理请求的那个。好比"点火开关"(CLI)和"发动机"(OpenResty + init.lua)。L03
生成 nginx.conf:配置即代码
关键一步:cli/ngx_tpl.lua 是一个 Nginx 配置模板,启动时用 config.yaml 的值填充,生成真正的 conf/nginx.conf。模板里就包含昨天说的阶段绑定:
-- ngx_tpl.lua 里(简化)
init_by_lua_block { require("apisix").http_init() }
init_worker_by_lua_block { require("apisix").http_init_worker() }
access_by_lua_block { require("apisix").http_access_phase() }
log_by_lua_block { require("apisix").http_log_phase() }
💡 你不手写 nginx.conf,APISIX 帮你生成
你只关心 config.yaml(简单 YAML:用哪个 etcd、监听哪个端口、开哪些功能),复杂的 nginx.conf 由模板生成——并把每个 Nginx 阶段绑到
apisix 模块的对应函数(昨天说的"Lua 挂到阶段"的落地)。📝 看生成结果
apisix test 只生成配置不启动 → 去 conf/nginx.conf 就能看到生成的完整配置和阶段绑定。L04
http_init(master 一次性)
init.lua:85-104:
function _M.http_init(args)
core.resolver.init_resolver(args) -- DNS 解析器
core.id.init() -- 实例 id
core.env.init() -- 环境变量
process.enable_privileged_agent() -- 特权 agent 进程(干需要 root/只需一个进程的活)
if core.config.init then core.config.init() end -- 初始化配置中心(etcd)
xrpc.init() -- stream 的 RPC 框架
end
读法:master 阶段只做"全局一次性"准备:DNS、id、环境、配置中心连接、特权 agent。特权 agent 是 OpenResty 的特殊进程,干"只需一个进程做"的活(如证书自动续期)。
L05
http_init_worker:APISIX 的"零件清单"
init.lua:107-140 逐个初始化子系统(每个 worker 都跑):
function _M.http_init_worker()
discovery.init_worker() -- 服务发现(Day 16)
require("apisix.balancer").init_worker() -- 负载均衡(Day 10)
require("apisix.admin.init").init_worker() -- Admin API(Day 17)
require("apisix.timers").init_worker() -- 定时器框架
core.config.init_worker() -- 配置中心 worker(etcd watch,Day 7)
plugin.init_worker() -- 插件系统(Day 11)
router.http_init_worker() -- 路由(Day 6)
require("apisix.consumer").init_worker() -- 消费者(Day 14)
-- global_rules / upstream / control_api ... 逐个 init_worker
plugin.init_prometheus() -- 放最后确保所有 worker 就绪
end
💡 这个函数几乎列全了 APISIX 的所有子系统
router、plugin、consumer、discovery、balancer、admin、timers…每个都在 worker 启动时 init_worker 一下。本课后面几乎每天都在讲这里初始化的某个子系统。读懂这个函数 = 拿到整个 APISIX 的"目录"(我在每行标了对应教学日)。
L06
子系统 init_worker 通常干两件事
- 建立 etcd watch:订阅自己关心的配置(router 订阅
/routes、consumer 订阅/consumers),配置变了内存更新。 - 起后台定时任务:健康检查、指标上报、配置轮询兜底(用
timers框架)。
读法:这就是"全动态"的实现——每个子系统在 worker 里 watch 自己那部分 etcd 数据,配置一变立刻在内存生效。Day 07 深入 config_etcd 看 watch 机制。
L07
全动态从这里起步
启动 = 铺好"读走内存、写走 etcd"的双轨
启动后:各子系统在内存里持有配置(读请求走内存 = 快),同时 watch etcd(配置变更走 etcd → watch → 内存更新 = 动态)。这条"读写分离"的双轨在 init_worker 时铺好,之后每个请求(Day 4)都跑在这条轨上。回想 Day 01 的两大支柱——OpenResty 的性能 + etcd 的动态,在启动这一步就体现了。
L08
今日小结 + 动手
🧠 今天你应该能回答
- CLI(cli/)和网关本体(init.lua)的区别?
- nginx.conf 是怎么来的?阶段绑定在哪定义?
http_initvshttp_init_worker各做什么?- 子系统的 init_worker 通常干哪两件事?和"全动态"什么关系?
✋ 动手
cd /Users/bitmart/work/codes/github/apisix
ls apisix/cli/
sed -n '85,140p' apisix/init.lua
grep -n "_by_lua" apisix/cli/ngx_tpl.lua | head
明天预告 · Day 04:全课最核心的一天——精读
http_access_phase,逐段看一个请求怎么创建 ctx、匹配路由、合并配置、选并跑插件、交给上游。