QA_ARCHIVE.md 8.5 KB

问答归档 (Q&A Archive)

Nginx Stream + SSL Preread 自动分流方案 — 技术问答归档

持续更新:每次技术问答后自动归档
最后更新:2026-10-06


目录


架构相关

Q1: 为什么使用 stream + ssl_preread 而不是传统的 http 模块 redirect?

A: 传统方案需要在 Nginx 的 HTTP 模块中直接监听外网端口并处理 SSL,所有 HTTP 和 HTTPS 流量必须使用同一端口。而 stream + ssl_preread 方案的优势:

  1. 端口灵活性:可以在任意非标准端口同时处理 HTTP 和 HTTPS 流量
  2. TCP 层分流:不解密 TLS,性能开销更低
  3. 协议检测:通过 ssl_preread 在 TCP 层检测 ClientHello,无需证书即可完成分流
  4. 后端隔离:后端服务只需提供 HTTP,SSL 终结完全由 Nginx 处理

Q2: 为什么不直接用 Nginx 的 if ($scheme = http) 来做跳转?

A: if 指令在 Nginx 中是 "if is evil" 的,有以下问题:

  1. 在 stream 上下文中无法使用 $scheme 变量(stream 不解析 HTTP)
  2. if 在 location 上下文中有已知的副作用和限制
  3. 通过 stream 层的物理分流更干净,每个路径独立互不干扰

Q3: 每个端口都需要独立的 map 变量吗?

A: 是的。Nginx 的 map 指令在同一上下文中,如果多个 stream server 共享同一个 map,它们会路由到相同的后端。每个端口需要独立的服务组(不同的 SSL 终结端口和不同的后端),因此需要独立的 map 变量。

Q4: 为什么把子域名配置拆成独立文件而不是全部写在 nginx.conf 里?

A: 模块化配置的优势:

  1. 独立维护:新增子域名只需编辑对应的 include 文件,不影响主配置
  2. 减少冲突:不同团队/项目的配置不会互相干扰
  3. 易于审计:配置分散在小文件中,便于审查
  4. 热重载安全:nginx -t 可验证所有 include 文件
  5. 结构清晰:nginx.conf 保持简洁,只负责全局配置和 include 指令

配置相关

Q5: backlog=4096 的含义是什么?需要调整吗?

A: backlog 是 TCP 监听队列的最大长度。超过 backlog 的连接会被丢弃。

  • 默认值通常是 511(Linux)
  • 4096 适合中等并发场景
  • 高并发场景可增加到 65535(需同时调整 net.core.somaxconn)

Q6: proxy_timeout 3600s 为什么要设这么大?

A: 需要与 HTTP 模块的 proxy_read_timeout 保持一致:

  1. stream 层的超时控制的是 TCP 连接空闲超时
  2. 如果后端有 WebSocket 长连接,可能需要保持数小时
  3. 设为 3600s 可覆盖绝大多数长连接场景
  4. 如果确认没有长连接需求,可以缩短到 60-300s 以释放资源

Q7: proxy_next_upstream off 为什么禁用故障转移?

A: 在 stream 上下文中:

  1. TCP 层无法确定请求是否已部分传输
  2. 非幂等请求(如 POST)重试可能导致数据重复
  3. 只有一个 upstream 时此选项实际无影响,但显式禁用更安全

Q8: include 指令的路径有什么要求?

A:

  • include 路径可以是绝对路径或相对路径(相对于 nginx.conf 所在目录)
  • 推荐使用绝对路径,避免歧义
  • 支持通配符:include /etc/nginx/conf.d/*.conf;
  • include 的文件中不能再嵌套 include stream 块(stream 块不支持嵌套 include stream)

域名与证书

Q9: 多个子域名可以共用同一个 SSL 证书吗?

A: 可以,使用通配符证书 *.zhonjin.com 即可覆盖所有子域名:

  • chanking.zhonjin.com ✓
  • mqtt.zhonjin.com ✓
  • baolin.zhonjin.com ✓
  • 其他 *.zhonjin.com ✓

申请命令:

sudo certbot certonly --manual --preferred-challenges dns \
    -d zhonjin.com -d "*.zhonjin.com"

Q10: 不同子域名使用同一个 IP,Nginx 如何区分?

A: 本方案中,不同子域名通过不同端口区分,而非 SNI 区分:

  • chanking.zhonjin.com:40130 → stream 端口 40130
  • mqtt.zhonjin.com:40715 → stream 端口 40715
  • baolin.zhonjin.com:40716 → stream 端口 40716

每个 stream server 独立监听自己的端口,转发到对应的 HTTPS server,HTTPS server 通过 server_name 匹配域名。

Q11: ssl_preread 能否根据 SNI(域名)分流到不同后端?

A: 可以。ssl_preread 不仅能检测是否为 TLS,还能读取 SNI 信息:

map $ssl_preread_server_name $sni_backend {
    "mqtt.zhonjin.com"    127.0.0.1:8085;
    "baolin.zhonjin.com"  127.0.0.1:5000;
    default               127.0.0.1:5004;
}

但本方案使用端口区分,因此不需要 SNI 分流。

Q12: 通配符证书 *.zhonjin.com 是否覆盖 zhonjin.com(不带子域名前缀)?

A: 不覆盖。通配符证书只匹配 *.zhonjin.com,不匹配裸域 zhonjin.com。申请时需要同时指定:

-d zhonjin.com -d "*.zhonjin.com"

性能相关

Q13: 这个方案的性能瓶颈在哪里?

A: 主要瓶颈点:

  1. SSL 握手:RSA/ECDHE 密钥交换是 CPU 密集操作,可使用 ssl_session_cache 复用
  2. stream 转发:几乎无额外开销(TCP 层直通)
  3. 文件描述符:高并发时需要足够的 fd 配额
  4. 树莓派 CPU:ARM 处理器 SSL 性能较弱,可通过 session cache 和硬件加速缓解

Q14: SSL Session Cache 大小怎么计算?

A:

  • shared:SSL_mqtt:5m 表示 5MB 共享内存
  • 每个 SSL session 约 500 bytes
  • 5MB ≈ 10,000 个并发 SSL session
  • 4 个服务组总计约 25MB,对 8GB 树莓派完全可接受

故障排查

Q15: 如何判断 HTTP 流量是否正确到达跳转 server?

A: 查看 stream 访问日志:

tail -f /var/log/nginx/stream_access_40715.log
# 正常 HTTP 访问应该看到 upstream 指向跳转端口

Q16: 301 跳转后浏览器显示 "重定向循环" 怎么办?

A: 常见原因:

  1. HTTP 跳转 server 的 return 301 URL 中端口写错了
  2. HTTPS server 意外把请求又转回了 HTTP
  3. 后端应用的 siteurl 配置为 HTTP

排查:

curl -v http://mqtt.zhonjin.com:40715 2>&1 | grep Location
# 确认 Location 是 https:// 开头且端口正确

Q17: nginx -t 报错 "include file not found" 怎么办?

A:

  1. 检查文件是否存在:ls -la /etc/nginx/conf.d/zhonjin_http.conf
  2. 检查目录是否存在:ls -la /etc/nginx/stream.d/
  3. 检查文件权限:确保 nginx 用户(www-data)有读权限
  4. 检查路径拼写

Q18: stream include 不生效?

A:

  • 确认 nginx 版本 ≥ 1.26(支持 stream 块内 include)
  • 确认 include 路径正确
  • 确认 nginx -t 通过
  • 检查 stream 块的 include 是否在正确位置(stream 块内部)

端口扩展

Q19: 新增端口映射时,如何确保不影响已有服务?

A: 遵循检查清单:

  1. ✅ 新增的 map 变量名不与已有的冲突
  2. ✅ 新增的 HTTPS/HTTP 内部端口不与已有的冲突
  3. ✅ SSL session cache 名称不重复
  4. ✅ stream 日志文件路径不重复
  5. ✅ 防火墙放行新端口
  6. ✅ nginx -t 通过后再 reload

Q20: 端口范围有没有限制?

A:

  • 有效端口范围:1-65535
  • 1024 以下需要 root 权限监听
  • 建议使用 10000-65535 范围的非特权端口
  • 避免与常见服务端口冲突

树莓派部署

Q21: 树莓派跑这个方案会不会性能不足?

A: 树莓派 5 8G 完全够用:

  1. stream 模块是 TCP 层直通,CPU 开销极低
  2. 树莓派 5 的 Cortex-A76 支持 ARMv8 crypto 扩展
  3. 个人/小型团队场景,并发量通常在 100 以内
  4. 实际测试:树莓派 4 就能处理 500+ SSL req/s

Q22: SD 卡会被日志写坏吗?

A: 长期高频写入确实会影响 SD 卡寿命。建议:

  1. 将日志目录挂载为 tmpfs(内存文件系统)
  2. 或使用外接 SSD/USB 驱动器存储日志
  3. 配置 logrotate 限制日志大小
  4. 对于非关键场景,可以关闭 stream access_log

Q23: 树莓派上编译 Nginx 需要多长时间?

A:

  • 树莓派 5(Cortex-A76):约 5-10 分钟(make -j4)
  • 树莓派 4(Cortex-A72):约 10-20 分钟
  • 树莓派 3:约 30-60 分钟
  • 建议直接使用 APT 安装,避免编译

新问答将按类别追加到对应章节中。