🎬 第十章 Nginx搭建直播平台
📄 一句话概括

每条指令都注册了「允许的上下文」,写错层直接启动失败。 报 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 秒实验

  1. 把 worker_processes 的允许层改成 [0, 2],看校验结果怎么变——能过了,但真 Nginx 会拒绝,这说明简化模型有边界。
  2. 加一条 hls_path,想想它该在几层——application 里,也就是第 4 层。
  3. 把 nginx -t 换成 nginx -T,想想差别——-T 会把展开 include 之后的完整配置打出来。

常见误区

  1. 「unknown directive 就是模块没装」——也可能位置不对。
  2. 「只要指令名对就行」——位置错一样起不来。
  3. 「nginx -t 会启动服务」——它只检查语法,不占端口。
自测三问(点开对答案) 1. `not allowed here` 与 `unknown directive` 有什么不同? —— **前者是指令存在但位置错,后者是当前块里没有这条指令**,后者可能是模块没装也可能是位置不对。 2. `rtmp` 块写错位置会报哪种错? —— **`unknown directive`**,因为 http 块里没有它。 3. 排错的第一步应该是什么? —— **`nginx -t`**,只查语法,三秒出结果。

相关笔记