每条指令都注册了「允许的上下文」,写错层直接启动失败。 报 unknown directive 时,先查位置,再查模块。
10.4.1 讲了配置是一棵树,这一节讲「每条指令能长在树的哪个位置」。 这是排错时最有用的一节。
一、指令与上下文
Nginx 的每条指令在源码里都声明了自己允许出现在哪些块。
写在不允许的地方,启动时直接报 directive is not allowed here。
几个典型:
worker_processes 只能写 main;
worker_connections 只能写 events;
listen 只能写 server;
location 只能写 server。
rtmp 是个特例,要单独记:
它是 main 级的块,和 http 平级。
写进 http 里,报的错是 unknown directive,不是「不允许」。
这个差异会把人引向错误方向——你会以为模块没装,其实是位置不对。
二、为什么报「未知」而不是「不允许」
原因是 Nginx 按块找指令表:
进到 http 块里,它只查 http 模块注册的指令表,
rtmp 不在那张表里,于是报「未知」。
所以两种报错的区别是:
not allowed here 表示指令存在但位置错;
unknown directive 表示当前这个块里根本没有这条指令。
后者既可能是「模块没装」,也可能是「位置不对」。
区分办法只有一个: 先在 main 层写一个最小配置试试。 main 层能用,说明模块装了,问题在位置; main 层也报未知,才是模块真没装。
三、排错的三步顺序
顺序不能反,反了就白花时间:
① nginx -t:只检查语法,不启动、不占端口。
这一步能抓九成的配置错误。
② 看报错的行号:Nginx 会给文件名与行号,
跳过去看那一行的上下文,多半是写错块了。
③ 查模块:只有前两步都排除了,才怀疑模块。
nginx -V 看 --add-module 里有没有。
很多人一上来就重编,
编完发现还是同样的错,因为问题从来不在模块上。
nginx -t 三秒钟就能给答案,为什么要跳过它。
四、动手 Demo:指令解析——写错层,启动就失败
给一份故意写错的配置,真跑一遍校验器, 把「允许的上下文」和「实际位置」对一遍。
ALLOW = {
"worker_processes": [0],
"error_log": [0],
"worker_connections": [1],
"listen": [3],
"server_name": [3],
"root": [2, 3, 4],
"location": [3],
"flv_live": [4],
"rtmp": [0],
}
LEVEL = {0: "main", 1: "events/rtmp", 2: "http",
3: "server", 4: "application/location"}
CASES = [
("worker_connections 10240;", 2),
("rtmp {", 3),
("listen 1935;", 3),
("flv_live on;", 4),
("worker_processes auto;", 0),
]
for txt, ctx in CASES:
name = txt.split()[0]
ok = ctx in ALLOW.get(name, [])
print("%-28s 写在 %-20s %s"
% (txt, LEVEL[ctx], "可以" if ok else "不允许"))
实跑输出:
--------------------------------------------------------------------
指令解析——写错层,启动就失败
--------------------------------------------------------------------
**① 每条指令都有它「能待的地方」**
**Nginx 的每条指令都注册了允许的上下文。**
**写在不允许的块里,启动时直接报 `directive is not allowed here`。**
指令 允许出现在 写错的典型症状
-------------------------------------------------------------------------------------
worker_processes main 写进 http 就报错
worker_connections events 写进 http 不生效或报错
listen server 写进 http 报错
rtmp main 写进 http 报 unknown directive
flv_live application 或 location 写在外面不生效
**`rtmp` 那条最值得记:**
**它是 main 级的块,和 http 平级。**
**很多人凭直觉把它写进 http,然后看到 unknown directive,**
**以为是模块没编进去——其实是位置不对。**
**② 真跑一个校验器,把写错的地方挑出来**
**下面这份配置有两处真错。**
worker_processes auto;
http {
worker_connections 10240;
server {
listen 80;
rtmp {
server {
listen 1935;
}
}
}
}
行号 指令 问题
----------------------------------------------------------------------
2 worker_connections 只能写在 events 块里
5 rtmp 它是 main 级块,不能嵌在 server 里
**注意第 7 行那个 `listen 1935`:它本身是合法的,**
**因为在 rtmp 的 server 里 listen 就长这样。**
**但它上面那个 rtmp 块位置已经错了,所以整段都失效。**
**这就说明一件事:同一个指令名在不同模块里可能是两回事,**
**判断对错必须结合「它在哪个块里」,光看名字没用。**
**③ 出错了先看哪里**
**顺序就三步,别乱:**
**① 跑 `nginx -t`**:**它只检查语法,不启动、不占端口。**
**这一步能抓九成的配置错误。**
**② 看报错的行号**:**Nginx 会给出文件名与行号,**
**直接跳到那一行看是不是写错块了。**
**③ 看模块在不在**:**报 `unknown directive "rtmp"` 时,**
**先 `nginx -V` 确认模块编进去了,再怀疑位置。**
**这三步的顺序不能反:**
**先查语法,再查位置,最后才查模块。**
**很多人一上来就重编,白花一小时。**
同一条 listen 在 http 与 rtmp 的 server 里都是合法的,层号都是 3。
rtmp 只能在 0 层,写进 server 里就是「未知指令」。
flv_live 只能在 application 或 location 里,写外面不生效。
校验器只判断层号,不管父块是谁,真实情况还要看模块归属。
30 秒实验
- 把
worker_processes的允许层改成[0, 2],看校验结果怎么变——能过了,但真 Nginx 会拒绝,这说明简化模型有边界。 - 加一条
hls_path,想想它该在几层——application 里,也就是第 4 层。 - 把
nginx -t换成nginx -T,想想差别——-T会把展开 include 之后的完整配置打出来。
常见误区
- 「
unknown directive就是模块没装」——也可能位置不对。 - 「只要指令名对就行」——位置错一样起不来。
- 「
nginx -t会启动服务」——它只检查语法,不占端口。
自测三问(点开对答案)
1. `not allowed here` 与 `unknown directive` 有什么不同? —— **前者是指令存在但位置错,后者是当前块里没有这条指令**,后者可能是模块没装也可能是位置不对。 2. `rtmp` 块写错位置会报哪种错? —— **`unknown directive`**,因为 http 块里没有它。 3. 排错的第一步应该是什么? —— **`nginx -t`**,只查语法,三秒出结果。- ☐ (本节完,下一节 10.4.3 Nginx 配置文件关于 nginx-rtmp-module 配置指令详细讲解)