docs(nginx): 新增健康检查详解与7个高级模块文档
- 04-proxy-loadbalancing: 新增第18节主动健康检查详解 - 被动检查vs主动检查对比、NGINX Plus health_check/match指令 - Stream健康检查、gRPC健康检查、开源替代方案 - 新增 MQTT 模块文档 (33): broker负载均衡、Client ID路由 - 新增 OIDC 模块文档 (34): OpenID Connect认证、JWT验证 - 新增 Keyval 模块文档 (35): 动态键值存储、API管理接口 - 新增 流媒体模块文档 (36): HLS/FLV/MP4伪流媒体配置 - 新增 WebDAV 模块文档 (37): 文件共享服务器配置 - 新增 Zone Sync 模块文档 (38): 多节点状态同步 - 新增 HTTP Tunnel 模块文档 (39): HTTP CONNECT代理隧道 - 更新 README.md 目录索引 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
238fa20e41
commit
4d108267c3
@ -931,3 +931,556 @@ http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 主动健康检查详解
|
||||||
|
|
||||||
|
### 18.1 被动检查 vs 主动检查
|
||||||
|
|
||||||
|
| 特性 | 被动健康检查 (Passive) | 主动健康检查 (Active) |
|
||||||
|
|------|----------------------|---------------------|
|
||||||
|
| **实现方式** | 基于真实客户端请求响应判断 | 独立的探测请求周期性检测 |
|
||||||
|
| **触发时机** | 实际请求失败时 | 按配置间隔主动发起 |
|
||||||
|
| **资源占用** | 无额外开销 | 需要额外的连接和请求 |
|
||||||
|
| **发现速度** | 慢(依赖真实流量) | 快(独立探测) |
|
||||||
|
| **可用性** | 开源 NGINX 内置 | NGINX Plus 商业版 / 第三方模块 |
|
||||||
|
| **配置位置** | `server` 指令参数 | `upstream` 块或 `location` 指令 |
|
||||||
|
| **典型参数** | `max_fails`, `fail_timeout` | `interval`, `fails`, `passes`, `match` |
|
||||||
|
|
||||||
|
**被动检查机制**:
|
||||||
|
```nginx
|
||||||
|
upstream backend {
|
||||||
|
# 在 fail_timeout(30s) 内连续失败 max_fails(3) 次,标记为不可用
|
||||||
|
server srv1.example.com max_fails=3 fail_timeout=30s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**主动检查优势**:
|
||||||
|
- 不依赖真实客户端流量即可检测后端状态
|
||||||
|
- 可以检测特定的健康检查端点(如 `/health`)
|
||||||
|
- 支持自定义匹配规则验证响应内容
|
||||||
|
- 支持 gRPC、TCP、UDP 等多种协议
|
||||||
|
|
||||||
|
### 18.2 HTTP 健康检查指令详解 (NGINX Plus)
|
||||||
|
|
||||||
|
**注意**:HTTP 主动健康检查模块 (`ngx_http_upstream_hc_module`) 是 NGINX Plus 商业订阅的一部分。
|
||||||
|
|
||||||
|
#### health_check 指令
|
||||||
|
|
||||||
|
**语法**:`health_check [parameters];`
|
||||||
|
**上下文**:`location`
|
||||||
|
**功能**:启用 upstream 服务器组的定期健康检查
|
||||||
|
|
||||||
|
**参数说明**:
|
||||||
|
|
||||||
|
| 参数 | 语法 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `interval` | `interval=time` | `5s` | 检查间隔时间 |
|
||||||
|
| `jitter` | `jitter=time` | — | 随机延迟时间,避免多个服务器同时检查 |
|
||||||
|
| `fails` | `fails=number` | `1` | 连续失败次数判定为不健康 |
|
||||||
|
| `passes` | `passes=number` | `1` | 连续成功次数判定为健康 |
|
||||||
|
| `uri` | `uri=uri` | `/` | 健康检查请求的 URI |
|
||||||
|
| `port` | `port=number` | 服务器端口 | 健康检查使用的端口 |
|
||||||
|
| `match` | `match=name` | — | 引用 `match` 块进行响应验证 |
|
||||||
|
| `mandatory` | `mandatory [persistent]` | — | 初始状态为 "checking";`persistent` 在 reload 后保持状态 |
|
||||||
|
| `keepalive_time` | `keepalive_time=time` | — | 启用健康检查连接的 keepalive |
|
||||||
|
| `type=grpc` | `type=grpc [grpc_service=name] [grpc_status=code]` | — | 启用 gRPC 健康检查 |
|
||||||
|
|
||||||
|
#### match 指令
|
||||||
|
|
||||||
|
**语法**:`match name { ... }`
|
||||||
|
**上下文**:`http`
|
||||||
|
**功能**:定义响应验证测试集
|
||||||
|
|
||||||
|
**测试项**:
|
||||||
|
|
||||||
|
| 测试项 | 语法 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `status` | `status [!] code [code...]` | 状态码匹配,支持范围如 `200-399` |
|
||||||
|
| `header` | `header header [operator] value` | 响应头匹配,`=` 精确匹配,`~` 正则匹配 |
|
||||||
|
| `body` | `body ~ "regex"` | 响应体正则匹配(只检查前 256KB) |
|
||||||
|
| `require` | `require $variable` | 变量非空且不为 "0" |
|
||||||
|
|
||||||
|
**header 操作符**:
|
||||||
|
- `=` 或 `==`:精确相等
|
||||||
|
- `!=`:不相等
|
||||||
|
- `~`:正则匹配(区分大小写)
|
||||||
|
- `~*`:正则匹配(不区分大小写)
|
||||||
|
|
||||||
|
#### 配置示例
|
||||||
|
|
||||||
|
**基础健康检查**:
|
||||||
|
```nginx
|
||||||
|
upstream dynamic {
|
||||||
|
zone upstream_dynamic 64k; # 共享内存区必须
|
||||||
|
|
||||||
|
server backend1.example.com weight=5;
|
||||||
|
server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_pass http://dynamic;
|
||||||
|
health_check; # 使用默认配置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**高级配置**:
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
health_check interval=10s jitter=2s fails=3 passes=2
|
||||||
|
uri=/health port=8080 match=server_ok
|
||||||
|
keepalive_time=60s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match server_ok {
|
||||||
|
status 200; # 状态码必须是 200
|
||||||
|
header Content-Type = application/json; # Content-Type 精确匹配
|
||||||
|
header X-Health-Status ~ ^ok$; # 正则匹配头值
|
||||||
|
body ~ "\"status\":\\s*\"healthy\""; # 响应体包含状态标记
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**gRPC 健康检查**(不兼容 `uri` 和 `match`):
|
||||||
|
```nginx
|
||||||
|
upstream grpc_backend {
|
||||||
|
zone grpc_zone 64k;
|
||||||
|
server grpc1.example.com:50051;
|
||||||
|
server grpc2.example.com:50051;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
grpc_pass grpc://grpc_backend;
|
||||||
|
health_check mandatory type=grpc grpc_service=myapp.HealthCheck grpc_status=12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.3 Stream 健康检查指令详解 (NGINX Plus)
|
||||||
|
|
||||||
|
**注意**:Stream 主动健康检查模块 (`ngx_stream_upstream_hc_module`) 是 NGINX Plus 商业订阅的一部分。
|
||||||
|
|
||||||
|
#### 指令概览
|
||||||
|
|
||||||
|
| 指令 | 上下文 | 默认值 | 说明 |
|
||||||
|
|------|--------|--------|------|
|
||||||
|
| `health_check` | `server` | — | 启用健康检查 |
|
||||||
|
| `health_check_timeout` | `stream`, `server` | `5s` | 健康检查超时 |
|
||||||
|
| `match` | `stream` | — | 定义响应验证规则 |
|
||||||
|
|
||||||
|
#### health_check 参数(Stream)
|
||||||
|
|
||||||
|
| 参数 | 默认值 | 说明 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `interval` | `5s` | 检查间隔 |
|
||||||
|
| `jitter` | — | 随机延迟 |
|
||||||
|
| `fails` | `1` | 失败次数阈值 |
|
||||||
|
| `passes` | `1` | 成功次数阈值 |
|
||||||
|
| `match` | — | 引用 match 块 |
|
||||||
|
| `port` | 服务器端口 | 检查端口 |
|
||||||
|
| `udp` | — | 使用 UDP 协议 |
|
||||||
|
| `mandatory` | — | 初始状态为 "checking" |
|
||||||
|
| `persistent` | — | reload 后保持状态 |
|
||||||
|
|
||||||
|
#### match 块(Stream)
|
||||||
|
|
||||||
|
| 测试项 | 语法 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `send` | `send "string"` | 发送给服务器的字符串(支持 `\x` 十六进制) |
|
||||||
|
| `expect` | `expect "string"` / `expect ~ "regex"` | 期望的响应 |
|
||||||
|
|
||||||
|
**注意**:只检查服务器返回数据的前 `proxy_buffer_size` 字节。
|
||||||
|
|
||||||
|
#### 配置示例
|
||||||
|
|
||||||
|
**TCP 基础检查**:
|
||||||
|
```nginx
|
||||||
|
upstream tcp_backend {
|
||||||
|
zone tcp_zone 64k;
|
||||||
|
server backend1.example.com:12345 weight=5;
|
||||||
|
server backend2.example.com:12345;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 12346;
|
||||||
|
proxy_pass tcp_backend;
|
||||||
|
health_check interval=5s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**UDP 健康检查**:
|
||||||
|
```nginx
|
||||||
|
upstream dns_upstream {
|
||||||
|
zone dns_zone 64k;
|
||||||
|
server dns1.example.com:53;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 53 udp;
|
||||||
|
proxy_pass dns_upstream;
|
||||||
|
health_check udp interval=3s; # 发送探测并期望无 ICMP 不可达回复
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**自定义匹配规则(MySQL 检查)**:
|
||||||
|
```nginx
|
||||||
|
upstream mysql_backend {
|
||||||
|
zone mysql_zone 10m;
|
||||||
|
server db1.example.com:3306;
|
||||||
|
server db2.example.com:3306;
|
||||||
|
}
|
||||||
|
|
||||||
|
match mysql_handshake {
|
||||||
|
# 发送 MySQL 握手包(十六进制)
|
||||||
|
send "\x3a\x00\x00\x01\x0a\x35\x2e\x35\x2e\x32\x2d\x6d\x32\x00\x01...";
|
||||||
|
# 期望收到包含版本信息的响应
|
||||||
|
expect ~ "\x4a\x00\x00\x00\x0a";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 3307;
|
||||||
|
proxy_pass mysql_backend;
|
||||||
|
health_check match=mysql_handshake interval=5s;
|
||||||
|
health_check_timeout 10s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**HTTP 风格的 TCP 检查**:
|
||||||
|
```nginx
|
||||||
|
match http_check {
|
||||||
|
send "GET /health HTTP/1.0\r\nHost: localhost\r\n\r\n";
|
||||||
|
expect ~ "200 OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
proxy_pass backend;
|
||||||
|
health_check match=http_check interval=5s fails=3 passes=2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.4 自定义健康检查配置示例
|
||||||
|
|
||||||
|
#### 场景一:API 网关健康检查
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
upstream api_backend {
|
||||||
|
zone api_zone 64k;
|
||||||
|
|
||||||
|
server api1.example.com:8080;
|
||||||
|
server api2.example.com:8080;
|
||||||
|
server api3.example.com:8080;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查匹配规则
|
||||||
|
match api_healthy {
|
||||||
|
status 200;
|
||||||
|
header Content-Type = application/json;
|
||||||
|
body ~ "\"status\":\\s*\"up\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name api.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://api_backend;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
|
||||||
|
# 健康检查配置
|
||||||
|
health_check interval=5s jitter=1s fails=3 passes=2
|
||||||
|
uri=/api/health
|
||||||
|
match=api_healthy;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查状态页(NGINX Plus)
|
||||||
|
location /upstream_status {
|
||||||
|
upstream_status;
|
||||||
|
access_log off;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 场景二:多协议混合检查
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# TCP 服务健康检查
|
||||||
|
stream {
|
||||||
|
upstream redis_backend {
|
||||||
|
zone redis_zone 64k;
|
||||||
|
server redis1.example.com:6379;
|
||||||
|
server redis2.example.com:6379;
|
||||||
|
}
|
||||||
|
|
||||||
|
match redis_ping {
|
||||||
|
send "PING\r\n";
|
||||||
|
expect ~ "\+PONG";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 6379;
|
||||||
|
proxy_pass redis_backend;
|
||||||
|
health_check match=redis_ping interval=10s fails=2 passes=2;
|
||||||
|
health_check_timeout 3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTP 服务健康检查
|
||||||
|
http {
|
||||||
|
upstream web_backend {
|
||||||
|
zone web_zone 64k;
|
||||||
|
server web1.example.com:80;
|
||||||
|
server web2.example.com:80;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_pass http://web_backend;
|
||||||
|
health_check interval=5s uri=/nginx_health;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 场景三:微服务 gRPC 健康检查
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream grpc_services {
|
||||||
|
zone grpc_zone 64k;
|
||||||
|
server service1.example.com:50051;
|
||||||
|
server service2.example.com:50051;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 50051 http2;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
grpc_pass grpc://grpc_services;
|
||||||
|
# gRPC 健康检查:使用标准 gRPC Health Checking Protocol
|
||||||
|
# grpc_status=12 (UNIMPLEMENTED) 表示服务未实现健康检查接口
|
||||||
|
# grpc_status=0 (OK) 表示服务健康
|
||||||
|
health_check mandatory type=grpc grpc_service=grpc.health.v1.Health
|
||||||
|
interval=5s fails=3 passes=2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.5 健康检查与负载均衡配合
|
||||||
|
|
||||||
|
#### 状态流转机制
|
||||||
|
|
||||||
|
```
|
||||||
|
初始状态
|
||||||
|
|
|
||||||
|
v
|
||||||
|
┌───────────┐ ┌──────────────┐
|
||||||
|
│ checking │────▶│ unhealthy │
|
||||||
|
└─────┬─────┘ └──────────────┘
|
||||||
|
│ │
|
||||||
|
│ passes 次成功 │ fails 次失败
|
||||||
|
v v
|
||||||
|
┌───────────┐ ┌──────────────┐
|
||||||
|
│ healthy │◀────│ │
|
||||||
|
└───────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键行为**:
|
||||||
|
- `checking` 状态:初始或 reload 后,不接收客户端请求
|
||||||
|
- `mandatory` 参数:强制等待首次健康检查完成才标记为健康
|
||||||
|
- `persistent` 参数:reload 后如之前是健康状态则保持 healthy
|
||||||
|
|
||||||
|
#### 与负载均衡算法结合
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
upstream backend {
|
||||||
|
zone backend 64k;
|
||||||
|
least_conn; # 最少连接算法
|
||||||
|
|
||||||
|
server srv1.example.com weight=5;
|
||||||
|
server srv2.example.com;
|
||||||
|
server srv3.example.com;
|
||||||
|
|
||||||
|
# 被动检查参数与主动检查并存
|
||||||
|
# 被动检查作为兜底,主动检查提供快速发现
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
health_check interval=5s fails=3 passes=2;
|
||||||
|
|
||||||
|
# 故障转移配置
|
||||||
|
proxy_next_upstream error timeout http_502 http_503 http_504;
|
||||||
|
proxy_next_upstream_timeout 5s;
|
||||||
|
proxy_next_upstream_tries 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**监控指标集成**:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
log_format health_log '$remote_addr - $remote_user [$time_local] '
|
||||||
|
'"$request" $status '
|
||||||
|
'upstream=$upstream_addr '
|
||||||
|
'upstream_status=$upstream_status '
|
||||||
|
'health_check=$upstream_health_check_status';
|
||||||
|
|
||||||
|
server {
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
health_check interval=5s;
|
||||||
|
access_log /var/log/nginx/health.log health_log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 18.6 开源 NGINX 的替代方案
|
||||||
|
|
||||||
|
由于主动健康检查是 NGINX Plus 商业特性,开源版本需要使用第三方模块。
|
||||||
|
|
||||||
|
#### nginx_upstream_check_module (Tengine)
|
||||||
|
|
||||||
|
由阿里巴巴 Tengine 团队开发的第三方模块,支持主动健康检查。
|
||||||
|
|
||||||
|
**源码地址**:https://github.com/yaoweibin/nginx_upstream_check_module
|
||||||
|
|
||||||
|
**安装方法**:
|
||||||
|
```bash
|
||||||
|
# 下载模块源码
|
||||||
|
git clone https://github.com/yaoweibin/nginx_upstream_check_module.git
|
||||||
|
|
||||||
|
# 下载 NGINX 源码并解压
|
||||||
|
cd /usr/local/src
|
||||||
|
tar -xzvf nginx-1.24.0.tar.gz
|
||||||
|
cd nginx-1.24.0
|
||||||
|
|
||||||
|
# 应用补丁(根据版本选择)
|
||||||
|
patch -p1 < /path/to/nginx_upstream_check_module/check.patch
|
||||||
|
# 或 patch -p1 < /path/to/nginx_upstream_check_module/check_1.20.1+.patch
|
||||||
|
|
||||||
|
# 编译安装
|
||||||
|
./configure \
|
||||||
|
--prefix=/etc/nginx \
|
||||||
|
--add-module=/path/to/nginx_upstream_check_module \
|
||||||
|
--with-http_ssl_module \
|
||||||
|
--with-http_v2_module
|
||||||
|
|
||||||
|
make && make install
|
||||||
|
```
|
||||||
|
|
||||||
|
**指令说明**:
|
||||||
|
|
||||||
|
| 指令 | 语法 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `check` | `check interval=ms [fall=N] [rise=N] [timeout=ms] [default_down=true\|false] [type=tcp\|http\|ssl_hello\|mysql\|ajp\|fastcgi]` | 见右侧 | 启用健康检查<br>`interval`: 检查间隔(ms)<br>`fall`: 失败次数<br>`rise`: 成功次数<br>`timeout`: 超时(ms)<br>`default_down`: 默认下线状态<br>`type`: 检查协议 |
|
||||||
|
| `check_keepalive_requests` | `check_keepalive_requests num` | `1` | 长连接检查次数 |
|
||||||
|
| `check_http_send` | `check_http_send "packet"` | `GET / HTTP/1.0\r\n\r\n` | HTTP 检查请求包 |
|
||||||
|
| `check_http_expect_alive` | `check_http_expect_alive [http_2xx] [http_3xx] [http_4xx] [http_5xx]` | `http_2xx` `http_3xx` | 视为健康的 HTTP 状态码 |
|
||||||
|
| `check_fastcgi_param` | `check_fastcgi_param parameter value` | — | FastCGI 检查参数 |
|
||||||
|
| `check_status` | `check_status [html\|csv\|json]` | `html` | 状态查看页面格式 |
|
||||||
|
|
||||||
|
**配置示例**:
|
||||||
|
```nginx
|
||||||
|
upstream backend {
|
||||||
|
server 192.168.0.1:80;
|
||||||
|
server 192.168.0.2:80;
|
||||||
|
|
||||||
|
# 每 5 秒检查一次,失败 3 次下线,成功 2 次上线,超时 4 秒
|
||||||
|
check interval=5000 rise=2 fall=3 timeout=4000 type=http;
|
||||||
|
|
||||||
|
# HTTP 健康检查配置
|
||||||
|
check_http_send "GET /health HTTP/1.0\r\n\r\n";
|
||||||
|
check_http_expect_alive http_2xx http_3xx;
|
||||||
|
|
||||||
|
# 启用长连接检查(可选)
|
||||||
|
check_keepalive_requests 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查状态页
|
||||||
|
location /status {
|
||||||
|
check_status json; # 可选 html, csv, json
|
||||||
|
access_log off;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**支持的健康检查类型**:
|
||||||
|
- `tcp`:仅建立 TCP 连接
|
||||||
|
- `http`:发送 HTTP 请求并验证响应
|
||||||
|
- `ssl_hello`:发送 SSL Client Hello
|
||||||
|
- `mysql`:发送 MySQL ping 包
|
||||||
|
- `ajp`:发送 AJP ping 包
|
||||||
|
- `fastcgi`:发送 FastCGI 请求
|
||||||
|
|
||||||
|
#### 其他替代方案对比
|
||||||
|
|
||||||
|
| 方案 | 类型 | 活跃度 | 特点 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| **nginx_upstream_check_module** | 第三方模块 | 中等 | 功能完整,Tengine 使用 |
|
||||||
|
| **nginx-upsync-module** | 第三方模块 | 低 | 结合 Consul/etcd 动态发现 |
|
||||||
|
| **OpenResty + lua-resty-healthcheck** | Lua 扩展 | 高 | 灵活可编程 |
|
||||||
|
| **Traefik** | 替代代理 | 高 | 原生支持主动健康检查 |
|
||||||
|
| **Envoy** | 替代代理 | 高 | 云原生,功能强大 |
|
||||||
|
|
||||||
|
#### OpenResty + Lua 实现示例
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
lua_shared_dict healthcheck 1m;
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server 127.0.0.1:8081;
|
||||||
|
server 127.0.0.1:8082;
|
||||||
|
server 127.0.0.1:8083;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_worker_by_lua_block {
|
||||||
|
local hc = require "resty.healthcheck"
|
||||||
|
local checker = hc.new({
|
||||||
|
name = "my-checker",
|
||||||
|
shm_name = "healthcheck",
|
||||||
|
checks = {
|
||||||
|
active = {
|
||||||
|
healthy = {
|
||||||
|
interval = 5,
|
||||||
|
successes = 2,
|
||||||
|
},
|
||||||
|
unhealthy = {
|
||||||
|
interval = 5,
|
||||||
|
http_failures = 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
checker:add_target("127.0.0.1", 8081)
|
||||||
|
checker:add_target("127.0.0.1", 8082)
|
||||||
|
checker:add_target("127.0.0.1", 8083)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**选型建议**:
|
||||||
|
- 商业环境且有预算:**NGINX Plus**(完整支持,商业支持)
|
||||||
|
- 开源替代且功能优先:**nginx_upstream_check_module**
|
||||||
|
- 需要动态配置:**OpenResty + lua-resty-upstream-healthcheck**
|
||||||
|
- 新架构选型:**Traefik** 或 **Envoy**(原生支持服务发现)
|
||||||
1087
docs/33-nginx-mqtt-module.md
Normal file
1087
docs/33-nginx-mqtt-module.md
Normal file
File diff suppressed because it is too large
Load Diff
1501
docs/34-nginx-oidc-module.md
Normal file
1501
docs/34-nginx-oidc-module.md
Normal file
File diff suppressed because it is too large
Load Diff
1080
docs/35-nginx-keyval-module.md
Normal file
1080
docs/35-nginx-keyval-module.md
Normal file
File diff suppressed because it is too large
Load Diff
904
docs/36-nginx-streaming-media.md
Normal file
904
docs/36-nginx-streaming-media.md
Normal file
@ -0,0 +1,904 @@
|
|||||||
|
# NGINX 流媒体模块指南
|
||||||
|
|
||||||
|
## 1. 模块概述
|
||||||
|
|
||||||
|
NGINX 提供多个模块支持 HTTP 流媒体服务,涵盖直播、点播和伪流媒体场景。
|
||||||
|
|
||||||
|
### 1.1 模块对比
|
||||||
|
|
||||||
|
| 模块 | 协议/格式 | 用途 | 可用性 |
|
||||||
|
|------|-----------|------|--------|
|
||||||
|
| `ngx_http_hls_module` | HLS (HTTP Live Streaming) | MP4/MOV 文件的 HLS 直播流 | NGINX Plus 商业版 |
|
||||||
|
| `ngx_http_flv_module` | FLV (Flash Video) | FLV 文件伪流媒体 | 开源版 |
|
||||||
|
| `ngx_http_mp4_module` | MP4 (H.264/AAC) | MP4 文件伪流媒体 | 开源版 |
|
||||||
|
| `ngx_http_f4f_module` | F4F/F4M (Adobe HDS) | Adobe HTTP Dynamic Streaming | NGINX Plus 商业版 |
|
||||||
|
|
||||||
|
### 1.2 伪流媒体 vs 直播流
|
||||||
|
|
||||||
|
**伪流媒体 (Pseudo-Streaming)**:
|
||||||
|
- 客户端通过 `start` 参数请求特定时间点
|
||||||
|
- 服务器从该时间点开始发送视频数据
|
||||||
|
- 适用于点播场景,支持随机 seek
|
||||||
|
|
||||||
|
**直播流 (Live Streaming)**:
|
||||||
|
- 实时生成媒体片段 (TS/F4F)
|
||||||
|
- 动态更新播放列表 (M3U8/F4M)
|
||||||
|
- 支持 HLS、HDS 等自适应码率协议
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. HLS 模块 (ngx_http_hls_module)
|
||||||
|
|
||||||
|
为 MP4 和 MOV 媒体文件提供 HTTP Live Streaming (HLS) 服务器端支持。
|
||||||
|
|
||||||
|
### 2.1 编译配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 商业订阅版本已包含此模块
|
||||||
|
# 无需额外编译参数
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 指令详解
|
||||||
|
|
||||||
|
#### hls
|
||||||
|
|
||||||
|
**语法**:`hls;`
|
||||||
|
|
||||||
|
**默认值**:无
|
||||||
|
|
||||||
|
**上下文**:`location`
|
||||||
|
|
||||||
|
**说明**:在 surrounding location 中开启 HLS 流媒体服务。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
hls;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hls_fragment
|
||||||
|
|
||||||
|
**语法**:`hls_fragment time;`
|
||||||
|
|
||||||
|
**默认值**:`hls_fragment 5s;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:为未带 `len` 参数请求的播放列表 URI 定义默认片段长度。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
hls_fragment 10s; # 每个 TS 片段 10 秒
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hls_buffers
|
||||||
|
|
||||||
|
**语法**:`hls_buffers number size;`
|
||||||
|
|
||||||
|
**默认值**:`hls_buffers 8 2m;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:设置用于读写数据帧的最大缓冲区数量和大小。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
hls_buffers 10 10m; # 10 个缓冲区,每个 10MB
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hls_forward_args
|
||||||
|
|
||||||
|
**语法**:`hls_forward_args on | off;`
|
||||||
|
|
||||||
|
**默认值**:`hls_forward_args off;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:将播放列表请求中的参数添加到片段 (fragment) 的 URI 中。
|
||||||
|
|
||||||
|
**用途**:
|
||||||
|
- 客户端授权
|
||||||
|
- 配合 `ngx_http_secure_link_module` 保护 HLS 流
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
hls_forward_args on;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hls_mp4_buffer_size
|
||||||
|
|
||||||
|
**语法**:`hls_mp4_buffer_size size;`
|
||||||
|
|
||||||
|
**默认值**:`hls_mp4_buffer_size 512k;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:设置用于处理 MP4 和 MOV 文件的初始缓冲区大小。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
hls_mp4_buffer_size 1m;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### hls_mp4_max_buffer_size
|
||||||
|
|
||||||
|
**语法**:`hls_mp4_max_buffer_size size;`
|
||||||
|
|
||||||
|
**默认值**:`hls_mp4_max_buffer_size 10m;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:在元数据处理期间,缓冲区最大不能超过此值,否则返回 500 错误。
|
||||||
|
|
||||||
|
**错误日志**:`"mp4 moov atom is too large"`
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
hls_mp4_max_buffer_size 5m;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 请求参数
|
||||||
|
|
||||||
|
HLS 播放列表支持以下 URI 参数:
|
||||||
|
|
||||||
|
| 参数 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `start` | 起始时间(秒) | `?start=1.000` |
|
||||||
|
| `end` | 结束时间(秒) | `?end=2.200` |
|
||||||
|
| `offset` | 偏移时间(秒) | `?offset=1.000` |
|
||||||
|
| `len` | 片段长度(秒) | `?len=10` |
|
||||||
|
|
||||||
|
### 2.4 配置示例
|
||||||
|
|
||||||
|
#### 基本 HLS 配置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name hls.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
hls;
|
||||||
|
hls_fragment 5s;
|
||||||
|
hls_buffers 10 10m;
|
||||||
|
hls_mp4_buffer_size 1m;
|
||||||
|
hls_mp4_max_buffer_size 5m;
|
||||||
|
root /var/video/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 安全链接配置
|
||||||
|
|
||||||
|
配合 `hls_forward_args on` 使用 secure_link:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
# 提取基础 URI(去掉 .m3u8 和 .ts 后缀)
|
||||||
|
map $uri $hls_uri {
|
||||||
|
~^(?<base_uri>.*)\.m3u8$ $base_uri;
|
||||||
|
~^(?<base_uri>.*)\.ts$ $base_uri;
|
||||||
|
default $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name secure-hls.example.com;
|
||||||
|
|
||||||
|
location /hls/ {
|
||||||
|
hls;
|
||||||
|
hls_forward_args on;
|
||||||
|
alias /var/videos/;
|
||||||
|
|
||||||
|
# 安全链接验证
|
||||||
|
secure_link $arg_md5,$arg_expires;
|
||||||
|
secure_link_md5 "$secure_link_expires$hls_uri$remote_addr secret";
|
||||||
|
|
||||||
|
if ($secure_link = "") { return 403; }
|
||||||
|
if ($secure_link = "0") { return 410; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 请求 URI 示例
|
||||||
|
|
||||||
|
对于文件 `/var/video/test.mp4`:
|
||||||
|
|
||||||
|
| 类型 | URI 示例 |
|
||||||
|
|------|----------|
|
||||||
|
| 播放列表 | `http://hls.example.com/test.mp4.m3u8?offset=1.000&start=1.000&end=2.200` |
|
||||||
|
| 片段 | `http://hls.example.com/test.mp4.ts?start=1.000&end=2.200` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. FLV 模块 (ngx_http_flv_module)
|
||||||
|
|
||||||
|
为 Flash Video (FLV) 文件提供伪流媒体服务器端支持。
|
||||||
|
|
||||||
|
### 3.1 编译配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 此模块默认不构建,需要显式启用
|
||||||
|
./configure --with-http_flv_module ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 指令详解
|
||||||
|
|
||||||
|
#### flv
|
||||||
|
|
||||||
|
**语法**:`flv;`
|
||||||
|
|
||||||
|
**默认值**:无
|
||||||
|
|
||||||
|
**上下文**:`location`
|
||||||
|
|
||||||
|
**说明**:在 surrounding location 中开启 FLV 模块处理。
|
||||||
|
|
||||||
|
**行为**:
|
||||||
|
- 特殊处理包含 `start` 参数的请求
|
||||||
|
- 从请求的字节偏移量发送文件内容
|
||||||
|
- 自动前置 FLV 头
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location ~ \.flv$ {
|
||||||
|
flv;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 请求参数
|
||||||
|
|
||||||
|
| 参数 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `start` | 起始字节偏移量 | `?start=1000` |
|
||||||
|
|
||||||
|
### 3.4 配置示例
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name video.example.com;
|
||||||
|
|
||||||
|
location /videos/ {
|
||||||
|
root /var/www/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.flv$ {
|
||||||
|
flv;
|
||||||
|
root /var/www/videos/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. MP4 模块 (ngx_http_mp4_module)
|
||||||
|
|
||||||
|
为 MP4 文件提供服务器端伪流媒体支持,允许通过 `start` 和 `end` 参数进行随机 seek。
|
||||||
|
|
||||||
|
### 4.1 编译配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 此模块默认不构建,需要显式启用
|
||||||
|
./configure --with-http_mp4_module ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 指令详解
|
||||||
|
|
||||||
|
#### mp4
|
||||||
|
|
||||||
|
**语法**:`mp4;`
|
||||||
|
|
||||||
|
**默认值**:无
|
||||||
|
|
||||||
|
**上下文**:`location`
|
||||||
|
|
||||||
|
**说明**:在 surrounding location 中开启 MP4 模块处理。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /video/ {
|
||||||
|
mp4;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mp4_buffer_size
|
||||||
|
|
||||||
|
**语法**:`mp4_buffer_size size;`
|
||||||
|
|
||||||
|
**默认值**:`mp4_buffer_size 512K;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:设置用于处理 MP4 文件的初始缓冲区大小。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
mp4_buffer_size 1m;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mp4_max_buffer_size
|
||||||
|
|
||||||
|
**语法**:`mp4_max_buffer_size size;`
|
||||||
|
|
||||||
|
**默认值**:`mp4_max_buffer_size 10M;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:元数据处理期间缓冲区的最大大小。若 moov atom 过大,返回 500 错误。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
mp4_max_buffer_size 5m;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mp4_limit_rate
|
||||||
|
|
||||||
|
**语法**:`mp4_limit_rate on | off | factor;`
|
||||||
|
|
||||||
|
**默认值**:`mp4_limit_rate off;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:基于文件平均比特率限制响应传输速率。
|
||||||
|
|
||||||
|
**参数说明**:
|
||||||
|
- `on`:限速因子为 1.1
|
||||||
|
- `factor`:自定义限速因子
|
||||||
|
|
||||||
|
**注意**:此指令仅适用于商业订阅版本。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
mp4_limit_rate on; # 因子 1.1
|
||||||
|
mp4_limit_rate 1.5; # 自定义因子 1.5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mp4_limit_rate_after
|
||||||
|
|
||||||
|
**语法**:`mp4_limit_rate_after time;`
|
||||||
|
|
||||||
|
**默认值**:`mp4_limit_rate_after 60s;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:设置开始限速前的初始媒体数据播放时长。
|
||||||
|
|
||||||
|
**注意**:此指令仅适用于商业订阅版本。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
mp4_limit_rate_after 30s;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### mp4_start_key_frame
|
||||||
|
|
||||||
|
**语法**:`mp4_start_key_frame on | off;`
|
||||||
|
|
||||||
|
**默认值**:`mp4_start_key_frame off;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:强制输出视频从关键帧开始。
|
||||||
|
|
||||||
|
**行为**:
|
||||||
|
- 若 `start` 指定的位置非关键帧,使用 edit list 隐藏初始帧
|
||||||
|
- 需要 NGINX 1.21.4+
|
||||||
|
- 主流播放器(Chrome、Safari 等)支持 edit list
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
mp4_start_key_frame on;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 请求参数
|
||||||
|
|
||||||
|
| 参数 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `start` | 起始时间(秒) | `?start=238.88` |
|
||||||
|
| `end` | 结束时间(秒) | `?end=555.55` |
|
||||||
|
|
||||||
|
**组合示例**:`?start=238.88&end=555.55`
|
||||||
|
|
||||||
|
### 4.4 配置示例
|
||||||
|
|
||||||
|
#### 基本 MP4 配置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name video.example.com;
|
||||||
|
|
||||||
|
location /video/ {
|
||||||
|
mp4;
|
||||||
|
mp4_buffer_size 1m;
|
||||||
|
mp4_max_buffer_size 5m;
|
||||||
|
root /var/videos/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 高级配置(商业版功能)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name video.example.com;
|
||||||
|
|
||||||
|
location /video/ {
|
||||||
|
mp4;
|
||||||
|
mp4_buffer_size 1m;
|
||||||
|
mp4_max_buffer_size 5m;
|
||||||
|
mp4_limit_rate on;
|
||||||
|
mp4_limit_rate_after 30s;
|
||||||
|
mp4_start_key_frame on;
|
||||||
|
root /var/videos/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 性能优化建议
|
||||||
|
|
||||||
|
**文件结构优化**:
|
||||||
|
- 若 moov atom(元数据)位于文件末尾,NGINX 需要读取整个文件
|
||||||
|
- 建议使用工具(如 `qt-faststart`)将 moov atom 移到文件开头:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 qt-faststart 优化 MP4 文件
|
||||||
|
qt-faststart input.mp4 output.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. F4F 模块 (ngx_http_f4f_module)
|
||||||
|
|
||||||
|
提供 Adobe HTTP Dynamic Streaming (HDS) 的服务器端支持。
|
||||||
|
|
||||||
|
### 5.1 模块说明
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 处理 `/videoSeg1-Frag1` 形式的请求
|
||||||
|
- 使用 `.f4x` 索引文件从 `.f4f` 文件中提取片段
|
||||||
|
|
||||||
|
**可用性**:仅作为 NGINX Plus 商业订阅的一部分提供。
|
||||||
|
|
||||||
|
### 5.2 指令详解
|
||||||
|
|
||||||
|
#### f4f
|
||||||
|
|
||||||
|
**语法**:`f4f;`
|
||||||
|
|
||||||
|
**默认值**:无
|
||||||
|
|
||||||
|
**上下文**:`location`
|
||||||
|
|
||||||
|
**说明**:在 surrounding location 中开启 F4F 模块处理。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /video/ {
|
||||||
|
f4f;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### f4f_buffer_size
|
||||||
|
|
||||||
|
**语法**:`f4f_buffer_size size;`
|
||||||
|
|
||||||
|
**默认值**:`f4f_buffer_size 512k;`
|
||||||
|
|
||||||
|
**上下文**:`http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明**:设置用于读取 `.f4x` 索引文件的缓冲区大小。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
f4f_buffer_size 1m;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 配置示例
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name hds.example.com;
|
||||||
|
|
||||||
|
location /video/ {
|
||||||
|
f4f;
|
||||||
|
f4f_buffer_size 1m;
|
||||||
|
root /var/f4f/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 完整配置示例
|
||||||
|
|
||||||
|
### 6.1 综合流媒体服务器
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 负载均衡后端(用于回源)
|
||||||
|
upstream media_backend {
|
||||||
|
server 192.168.1.10:8080 weight=5;
|
||||||
|
server 192.168.1.11:8080 weight=5;
|
||||||
|
server 192.168.1.12:8080 backup;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 限速配置
|
||||||
|
limit_rate_after 1m;
|
||||||
|
limit_rate 1m;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name media.example.com;
|
||||||
|
|
||||||
|
# 日志格式
|
||||||
|
log_format media '$remote_addr - $remote_user [$time_local] '
|
||||||
|
'"$request" $status $bytes_sent '
|
||||||
|
'"$http_referer" "$http_user_agent" '
|
||||||
|
'time=$request_time';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/media-access.log media;
|
||||||
|
|
||||||
|
# HLS 流媒体(NGINX Plus)
|
||||||
|
location /hls/ {
|
||||||
|
hls;
|
||||||
|
hls_fragment 5s;
|
||||||
|
hls_buffers 10 10m;
|
||||||
|
hls_mp4_buffer_size 1m;
|
||||||
|
hls_mp4_max_buffer_size 5m;
|
||||||
|
alias /var/videos/hls/;
|
||||||
|
|
||||||
|
# 可选:安全链接
|
||||||
|
# hls_forward_args on;
|
||||||
|
# secure_link ...
|
||||||
|
}
|
||||||
|
|
||||||
|
# FLV 伪流媒体
|
||||||
|
location /flv/ {
|
||||||
|
location ~ \.flv$ {
|
||||||
|
flv;
|
||||||
|
alias /var/videos/flv/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# MP4 伪流媒体
|
||||||
|
location /mp4/ {
|
||||||
|
location ~ \.mp4$ {
|
||||||
|
mp4;
|
||||||
|
mp4_buffer_size 1m;
|
||||||
|
mp4_max_buffer_size 5m;
|
||||||
|
alias /var/videos/mp4/;
|
||||||
|
|
||||||
|
# NGINX Plus 功能
|
||||||
|
# mp4_limit_rate on;
|
||||||
|
# mp4_limit_rate_after 30s;
|
||||||
|
# mp4_start_key_frame on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# F4F 流媒体(NGINX Plus)
|
||||||
|
location /hds/ {
|
||||||
|
f4f;
|
||||||
|
f4f_buffer_size 1m;
|
||||||
|
alias /var/videos/hds/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 视频文件通用缓存配置
|
||||||
|
location ~* \.(mp4|flv|f4f|ts|m3u8)$ {
|
||||||
|
expires 1d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
|
||||||
|
# 跨域支持
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 播放列表不缓存(实时更新)
|
||||||
|
location ~ \.m3u8$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
add_header Pragma "no-cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 状态监控
|
||||||
|
location /nginx_status {
|
||||||
|
stub_status on;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTTPS 配置
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name media.example.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/ssl/certs/media.crt;
|
||||||
|
ssl_certificate_key /etc/ssl/private/media.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
# 复用 HTTP 配置
|
||||||
|
include /etc/nginx/conf.d/media-locations.conf;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 带转码的流媒体配置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 使用 ngx_rtmp_module(第三方模块)做 RTMP 转 HLS
|
||||||
|
rtmp {
|
||||||
|
server {
|
||||||
|
listen 1935;
|
||||||
|
|
||||||
|
application live {
|
||||||
|
live on;
|
||||||
|
|
||||||
|
# 转 HLS
|
||||||
|
hls on;
|
||||||
|
hls_path /var/videos/hls/;
|
||||||
|
hls_fragment 5s;
|
||||||
|
hls_playlist_length 60s;
|
||||||
|
|
||||||
|
# 多码率
|
||||||
|
hls_variant _low BANDWIDTH=500000;
|
||||||
|
hls_variant _mid BANDWIDTH=1500000;
|
||||||
|
hls_variant _high BANDWIDTH=5000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name live.example.com;
|
||||||
|
|
||||||
|
# 服务 HLS 流
|
||||||
|
location /hls/ {
|
||||||
|
types {
|
||||||
|
application/vnd.apple.mpegurl m3u8;
|
||||||
|
video/mp2t ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias /var/videos/hls/;
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 与 Lolly 项目的关系和建议
|
||||||
|
|
||||||
|
### 7.1 当前状态对比
|
||||||
|
|
||||||
|
| 特性 | NGINX 流媒体模块 | Lolly 当前状态 |
|
||||||
|
|------|------------------|----------------|
|
||||||
|
| HLS 支持 | 完整(商业版) | 暂未实现 |
|
||||||
|
| FLV 支持 | 完整(开源版) | 暂未实现 |
|
||||||
|
| MP4 点播 | 完整(需编译) | 暂未实现 |
|
||||||
|
| F4F/HDS | 完整(商业版) | 暂未实现 |
|
||||||
|
| 静态文件 | 完整 | 支持 |
|
||||||
|
| 文件缓存 | 完整 | 支持 |
|
||||||
|
|
||||||
|
### 7.2 实现建议
|
||||||
|
|
||||||
|
对于 Lolly 项目,可以考虑以下实现策略:
|
||||||
|
|
||||||
|
#### 1. 伪流媒体实现(MP4/FLV)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// handler/streaming.go
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MP4Handler 处理 MP4 伪流媒体请求
|
||||||
|
func MP4Handler(ctx *fasthttp.RequestCtx) {
|
||||||
|
start := ctx.QueryArgs().GetFloat64("start")
|
||||||
|
end := ctx.QueryArgs().GetFloat64("end")
|
||||||
|
|
||||||
|
// 解析 MP4 moov atom,计算偏移量
|
||||||
|
// 从指定时间点开始传输
|
||||||
|
// 处理 end 参数截断
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLVHandler 处理 FLV 伪流媒体请求
|
||||||
|
func FLVHandler(ctx *fasthttp.RequestCtx) {
|
||||||
|
start := ctx.QueryArgs().GetInt("start")
|
||||||
|
|
||||||
|
// 发送 FLV 头
|
||||||
|
// 从指定字节偏移开始传输
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. HLS 服务实现
|
||||||
|
|
||||||
|
```go
|
||||||
|
// handler/hls.go
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HLSPlaylistHandler 生成 M3U8 播放列表
|
||||||
|
func HLSPlaylistHandler(ctx *fasthttp.RequestCtx) {
|
||||||
|
videoPath := getVideoPath(ctx)
|
||||||
|
|
||||||
|
// 解析 MP4,计算片段
|
||||||
|
segments := generateSegments(videoPath, fragmentDuration)
|
||||||
|
|
||||||
|
// 生成 M3U8 内容
|
||||||
|
playlist := generateM3U8(segments)
|
||||||
|
|
||||||
|
ctx.SetContentType("application/vnd.apple.mpegurl")
|
||||||
|
ctx.WriteString(playlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HLSSegmentHandler 返回 TS 片段
|
||||||
|
func HLSSegmentHandler(ctx *fasthttp.RequestCtx) {
|
||||||
|
// 从 MP4 提取指定时间范围的 TS 数据
|
||||||
|
// 或使用预生成的 TS 文件
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 配置扩展示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# lolly.yaml 流媒体配置示例
|
||||||
|
server:
|
||||||
|
# 静态文件服务(已支持)
|
||||||
|
static:
|
||||||
|
- path: "/"
|
||||||
|
root: "/var/www/html"
|
||||||
|
|
||||||
|
# 流媒体服务(建议新增)
|
||||||
|
streaming:
|
||||||
|
# HLS 配置
|
||||||
|
hls:
|
||||||
|
enabled: true
|
||||||
|
path: "/hls/"
|
||||||
|
root: "/var/videos/"
|
||||||
|
fragment: "5s"
|
||||||
|
buffers: 10
|
||||||
|
buffer_size: "10m"
|
||||||
|
mp4_buffer_size: "1m"
|
||||||
|
mp4_max_buffer_size: "5m"
|
||||||
|
|
||||||
|
# MP4 伪流媒体
|
||||||
|
mp4:
|
||||||
|
enabled: true
|
||||||
|
path: "/mp4/"
|
||||||
|
root: "/var/videos/"
|
||||||
|
buffer_size: "1m"
|
||||||
|
max_buffer_size: "5m"
|
||||||
|
|
||||||
|
# FLV 伪流媒体
|
||||||
|
flv:
|
||||||
|
enabled: true
|
||||||
|
path: "/flv/"
|
||||||
|
root: "/var/videos/"
|
||||||
|
|
||||||
|
# 跨域配置
|
||||||
|
cors:
|
||||||
|
enabled: true
|
||||||
|
origins: ["*"]
|
||||||
|
methods: ["GET", "HEAD", "OPTIONS"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 技术实现要点
|
||||||
|
|
||||||
|
#### MP4 文件处理
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 关键:解析 moov atom,计算时间到字节的映射
|
||||||
|
type MP4Parser struct {
|
||||||
|
Moov *MoovBox
|
||||||
|
}
|
||||||
|
|
||||||
|
type MoovBox struct {
|
||||||
|
Tracks []*Track
|
||||||
|
}
|
||||||
|
|
||||||
|
type Track struct {
|
||||||
|
Timescale uint32
|
||||||
|
Samples []*Sample
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeekToTime 返回指定时间对应的文件偏移和样本索引
|
||||||
|
func (p *MP4Parser) SeekToTime(seconds float64) (offset int64, sampleIdx int) {
|
||||||
|
// 遍历样本表,找到对应时间点的样本
|
||||||
|
// 计算文件偏移
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### HLS 切片生成
|
||||||
|
|
||||||
|
```go
|
||||||
|
// SegmentInfo 表示一个 TS 片段
|
||||||
|
type SegmentInfo struct {
|
||||||
|
Sequence int
|
||||||
|
Duration float64
|
||||||
|
StartTime float64
|
||||||
|
EndTime float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateSegments 将 MP4 切分为片段信息
|
||||||
|
func GenerateSegments(videoPath string, fragmentDuration float64) ([]SegmentInfo, error) {
|
||||||
|
// 解析 MP4 结构
|
||||||
|
// 按关键帧边界分割片段
|
||||||
|
// 返回片段信息列表
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 依赖库建议
|
||||||
|
|
||||||
|
| 功能 | 推荐库 |
|
||||||
|
|------|--------|
|
||||||
|
| MP4 解析 | `github.com/abema/go-mp4` 或 `github.com/deepch/mp4` |
|
||||||
|
| HLS 生成 | 自行实现或使用 `github.com/grafov/m3u8` |
|
||||||
|
| 视频转码 | `github.com/xfrr/goffmpeg` (FFmpeg 绑定) |
|
||||||
|
| FLV 解析 | `github.com/yapingcat/gomedia` |
|
||||||
|
|
||||||
|
### 7.5 性能优化建议
|
||||||
|
|
||||||
|
1. **文件缓存**:复用现有文件缓存系统缓存解析后的 MP4 元数据
|
||||||
|
2. **预生成切片**:对于点播内容,预先生成 TS 片段文件
|
||||||
|
3. **零拷贝传输**:大视频文件使用 sendfile
|
||||||
|
4. **Goroutine 池**:控制并发转码任务数量
|
||||||
|
5. **内存复用**:使用 sync.Pool 复用缓冲区
|
||||||
|
|
||||||
|
### 7.6 安全建议
|
||||||
|
|
||||||
|
1. **路径遍历防护**:验证请求路径,防止访问非视频目录
|
||||||
|
2. **限速控制**:对视频流进行带宽限制
|
||||||
|
3. **防盗链**:使用 secure_link 或 JWT token 验证
|
||||||
|
4. **CORS 配置**:按需配置跨域访问
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 常见问题
|
||||||
|
|
||||||
|
### Q1: HLS 播放列表不更新?
|
||||||
|
|
||||||
|
**A**: 确保播放列表响应头禁用缓存:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location ~ \.m3u8$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q2: MP4 seek 不准确?
|
||||||
|
|
||||||
|
**A**: 启用 `mp4_start_key_frame on`(NGINX Plus 1.21.4+),或使用 edit list 隐藏非关键帧。
|
||||||
|
|
||||||
|
### Q3: FLV 无法 seek?
|
||||||
|
|
||||||
|
**A**: FLV 需要播放器支持,确保播放器发送 `start` 参数。
|
||||||
|
|
||||||
|
### Q4: 大文件处理缓慢?
|
||||||
|
|
||||||
|
**A**: 使用 `qt-faststart` 将 moov atom 移到文件开头:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
qt-faststart input.mp4 output.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q5: 跨域播放失败?
|
||||||
|
|
||||||
|
**A**: 添加 CORS 响应头:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS";
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 参考资源
|
||||||
|
|
||||||
|
- [NGINX HLS Module](https://nginx.org/en/docs/http/ngx_http_hls_module.html)
|
||||||
|
- [NGINX FLV Module](https://nginx.org/en/docs/http/ngx_http_flv_module.html)
|
||||||
|
- [NGINX MP4 Module](https://nginx.org/en/docs/http/ngx_http_mp4_module.html)
|
||||||
|
- [NGINX F4F Module](https://nginx.org/en/docs/http/ngx_http_f4f_module.html)
|
||||||
|
- [Apple HLS Specification](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis)
|
||||||
|
- [Adobe HDS Specification](https://www.adobe.com/devnet/hds.html)
|
||||||
687
docs/37-nginx-dav-module.md
Normal file
687
docs/37-nginx-dav-module.md
Normal file
@ -0,0 +1,687 @@
|
|||||||
|
# Nginx WebDAV 模块完整指南
|
||||||
|
|
||||||
|
## 1. WebDAV 模块概述
|
||||||
|
|
||||||
|
### 1.1 模块简介
|
||||||
|
|
||||||
|
`ngx_http_dav_module` 是 Nginx 的文件管理自动化模块,通过 WebDAV (Web Distributed Authoring and Versioning) 协议支持远程文件操作。
|
||||||
|
|
||||||
|
### 1.2 主要用途
|
||||||
|
|
||||||
|
- 文件上传与管理
|
||||||
|
- 远程文件编辑
|
||||||
|
- 目录结构创建
|
||||||
|
- 文件复制与移动
|
||||||
|
- 简单的文件服务器
|
||||||
|
|
||||||
|
### 1.3 编译要求
|
||||||
|
|
||||||
|
该模块**不会默认构建**,需要在编译时显式启用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./configure --with-http_dav_module
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 支持的 HTTP 方法
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `PUT` | 上传/创建文件 |
|
||||||
|
| `DELETE` | 删除文件或目录 |
|
||||||
|
| `MKCOL` | 创建目录 (Make Collection) |
|
||||||
|
| `COPY` | 复制文件或目录 |
|
||||||
|
| `MOVE` | 移动文件或目录 |
|
||||||
|
|
||||||
|
> **注意**:Nginx WebDAV 模块仅支持上述 5 种方法。需要其他 WebDAV 方法(如 PROPFIND、PROPPATCH、OPTIONS、LOCK、UNLOCK)的客户端将无法与此模块配合工作。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 指令详解
|
||||||
|
|
||||||
|
### 2.1 dav_methods
|
||||||
|
|
||||||
|
启用指定的 HTTP 方法。
|
||||||
|
|
||||||
|
**语法:**
|
||||||
|
```nginx
|
||||||
|
dav_methods off | PUT | DELETE | MKCOL | COPY | MOVE ...;
|
||||||
|
```
|
||||||
|
|
||||||
|
**默认值:** `off`
|
||||||
|
|
||||||
|
**上下文:** `http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明:**
|
||||||
|
- `off` - 禁止此模块的所有方法
|
||||||
|
- 可以指定一个或多个方法
|
||||||
|
- 未列出的方法将被禁止
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```nginx
|
||||||
|
# 仅启用 PUT 和 DELETE
|
||||||
|
dav_methods PUT DELETE;
|
||||||
|
|
||||||
|
# 启用全部支持的方法
|
||||||
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||||
|
|
||||||
|
# 禁用所有 WebDAV 方法
|
||||||
|
dav_methods off;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 create_full_put_path
|
||||||
|
|
||||||
|
允许创建所有必需的中间目录。
|
||||||
|
|
||||||
|
**语法:**
|
||||||
|
```nginx
|
||||||
|
create_full_put_path on | off;
|
||||||
|
```
|
||||||
|
|
||||||
|
**默认值:** `off`
|
||||||
|
|
||||||
|
**上下文:** `http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明:**
|
||||||
|
- WebDAV 规范通常要求目标目录必须已存在
|
||||||
|
- 设置为 `on` 时,PUT 请求可以自动创建路径中的所有中间目录
|
||||||
|
- 对于深度嵌套的文件上传非常有用
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```nginx
|
||||||
|
# 允许 PUT /files/a/b/c/file.txt 自动创建 a/b/c/ 目录
|
||||||
|
create_full_put_path on;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.3 dav_access
|
||||||
|
|
||||||
|
设置新创建的文件和目录的访问权限。
|
||||||
|
|
||||||
|
**语法:**
|
||||||
|
```nginx
|
||||||
|
dav_access users:permissions ...;
|
||||||
|
```
|
||||||
|
|
||||||
|
**默认值:** `user:rw`
|
||||||
|
|
||||||
|
**上下文:** `http`, `server`, `location`
|
||||||
|
|
||||||
|
**权限格式:**
|
||||||
|
```
|
||||||
|
user:permissions # 文件所有者权限
|
||||||
|
group:permissions # 组权限
|
||||||
|
all:permissions # 所有用户权限
|
||||||
|
```
|
||||||
|
|
||||||
|
**权限值:**
|
||||||
|
- `r` - 读
|
||||||
|
- `w` - 写
|
||||||
|
- `x` - 执行(目录为进入)
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```nginx
|
||||||
|
# 默认:用户可读写
|
||||||
|
dav_access user:rw;
|
||||||
|
|
||||||
|
# 用户读写,组读,其他用户只读
|
||||||
|
dav_access user:rw group:r all:r;
|
||||||
|
|
||||||
|
# 用户完全权限,组读执行,其他无权限
|
||||||
|
dav_access user:rwx group:rx all:;
|
||||||
|
|
||||||
|
# 多组权限
|
||||||
|
dav_access group:rw all:r;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.4 min_delete_depth
|
||||||
|
|
||||||
|
设置 DELETE 操作允许删除的最小路径深度。
|
||||||
|
|
||||||
|
**语法:**
|
||||||
|
```nginx
|
||||||
|
min_delete_depth number;
|
||||||
|
```
|
||||||
|
|
||||||
|
**默认值:** `0`
|
||||||
|
|
||||||
|
**上下文:** `http`, `server`, `location`
|
||||||
|
|
||||||
|
**说明:**
|
||||||
|
- 用于防止意外删除重要目录
|
||||||
|
- 路径深度计算以 `/` 分隔的元素数量
|
||||||
|
- 请求 URI 的元素数量必须 >= 设定值才能执行 DELETE
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```nginx
|
||||||
|
# 至少需要 4 层深度才能删除
|
||||||
|
min_delete_depth 4;
|
||||||
|
|
||||||
|
# 允许删除:DELETE /users/00/00/name (4 层)
|
||||||
|
# 禁止删除:DELETE /users/00/00 (3 层)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 配置示例
|
||||||
|
|
||||||
|
### 3.1 基础文件共享服务器
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name dav.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# 根目录
|
||||||
|
root /data/www;
|
||||||
|
|
||||||
|
# 临时文件目录(与根目录同一文件系统以获得最佳性能)
|
||||||
|
client_body_temp_path /data/client_temp;
|
||||||
|
|
||||||
|
# 启用 WebDAV 方法
|
||||||
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||||
|
|
||||||
|
# 允许创建中间目录
|
||||||
|
create_full_put_path on;
|
||||||
|
|
||||||
|
# 设置文件权限
|
||||||
|
dav_access group:rw all:r;
|
||||||
|
|
||||||
|
# 限制写操作的访问
|
||||||
|
limit_except GET {
|
||||||
|
allow 192.168.1.0/24;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 只读文件服务器(带选择性上传)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name files.example.com;
|
||||||
|
|
||||||
|
# 公共只读区域
|
||||||
|
location /public/ {
|
||||||
|
root /data/files;
|
||||||
|
dav_methods off; # 禁止写操作
|
||||||
|
autoindex on; # 启用目录列表
|
||||||
|
}
|
||||||
|
|
||||||
|
# 受限上传区域
|
||||||
|
location /uploads/ {
|
||||||
|
root /data/files;
|
||||||
|
dav_methods PUT DELETE;
|
||||||
|
dav_access user:rw;
|
||||||
|
|
||||||
|
# 仅允许特定 IP 上传
|
||||||
|
limit_except GET {
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 支持深度目录的上传服务
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name upload.example.com;
|
||||||
|
|
||||||
|
location /storage/ {
|
||||||
|
root /data/storage;
|
||||||
|
|
||||||
|
dav_methods PUT DELETE MKCOL;
|
||||||
|
create_full_put_path on; # 自动创建嵌套目录
|
||||||
|
dav_access user:rw group:r;
|
||||||
|
|
||||||
|
# 防止删除顶层目录
|
||||||
|
min_delete_depth 3;
|
||||||
|
|
||||||
|
# 限制请求体大小
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
|
# 限制写操作
|
||||||
|
limit_except GET PUT {
|
||||||
|
allow 192.168.0.0/16;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 带认证的 WebDAV 服务
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name secure-dav.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /data/secure;
|
||||||
|
|
||||||
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||||
|
create_full_put_path on;
|
||||||
|
dav_access user:rw;
|
||||||
|
|
||||||
|
# HTTP 基本认证
|
||||||
|
auth_basic "WebDAV Access";
|
||||||
|
auth_basic_user_file /etc/nginx/.dav_passwd;
|
||||||
|
|
||||||
|
# 限制客户端大小
|
||||||
|
client_max_body_size 500M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
生成密码文件:
|
||||||
|
```bash
|
||||||
|
htpasswd -c /etc/nginx/.dav_passwd username
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.5 带 SSL 的 WebDAV 服务器
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name dav.example.com;
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name dav.example.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/ssl/dav.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/dav.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root /data/www;
|
||||||
|
|
||||||
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||||
|
create_full_put_path on;
|
||||||
|
dav_access user:rw group:r all:r;
|
||||||
|
|
||||||
|
client_max_body_size 1G;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 客户端使用指南
|
||||||
|
|
||||||
|
### 4.1 使用 curl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 上传文件(PUT)
|
||||||
|
curl -T localfile.txt http://dav.example.com/remotefile.txt
|
||||||
|
|
||||||
|
# 创建目录(MKCOL)
|
||||||
|
curl -X MKCOL http://dav.example.com/newdir/
|
||||||
|
|
||||||
|
# 删除文件(DELETE)
|
||||||
|
curl -X DELETE http://dav.example.com/file.txt
|
||||||
|
|
||||||
|
# 复制文件(COPY)
|
||||||
|
curl -X COPY -H "Destination: http://dav.example.com/copy.txt" \
|
||||||
|
http://dav.example.com/original.txt
|
||||||
|
|
||||||
|
# 移动文件(MOVE)
|
||||||
|
curl -X MOVE -H "Destination: http://dav.example.com/moved.txt" \
|
||||||
|
http://dav.example.com/original.txt
|
||||||
|
|
||||||
|
# 带认证上传
|
||||||
|
curl -T file.txt -u username:password \
|
||||||
|
http://dav.example.com/file.txt
|
||||||
|
|
||||||
|
# 查看文件内容(GET)
|
||||||
|
curl http://dav.example.com/file.txt
|
||||||
|
|
||||||
|
# 列出目录内容(需要客户端支持 PROPFIND)
|
||||||
|
curl -X PROPFIND http://dav.example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 使用 cadaver(命令行 WebDAV 客户端)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装
|
||||||
|
# Debian/Ubuntu
|
||||||
|
apt-get install cadaver
|
||||||
|
# macOS
|
||||||
|
brew install cadaver
|
||||||
|
|
||||||
|
# 连接
|
||||||
|
cadaver http://dav.example.com/
|
||||||
|
|
||||||
|
# cadaver 常用命令
|
||||||
|
dav:> ls # 列出目录
|
||||||
|
dav:> cd directory # 进入目录
|
||||||
|
dav:> put file.txt # 上传文件
|
||||||
|
dav:> get file.txt # 下载文件
|
||||||
|
dav:> mkdir newdir # 创建目录
|
||||||
|
dav:> rm file.txt # 删除文件
|
||||||
|
dav:> mv old new # 移动/重命名
|
||||||
|
dav:> quit # 退出
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 使用 GNOME Nautilus(文件管理器)
|
||||||
|
|
||||||
|
1. 打开 Nautilus 文件管理器
|
||||||
|
2. 按 `Ctrl+L` 或选择"其他位置"
|
||||||
|
3. 输入地址:`dav://dav.example.com/`
|
||||||
|
4. 输入凭据(如需要)
|
||||||
|
5. 可像本地文件夹一样操作
|
||||||
|
|
||||||
|
### 4.4 挂载为本地文件系统(Linux)
|
||||||
|
|
||||||
|
使用 davfs2:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装
|
||||||
|
apt-get install davfs2
|
||||||
|
|
||||||
|
# 挂载
|
||||||
|
mount -t davfs http://dav.example.com/ /mnt/webdav
|
||||||
|
|
||||||
|
# 或使用 /etc/fstab 自动挂载
|
||||||
|
http://dav.example.com/ /mnt/webdav davfs user,noauto 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.5 Windows 资源管理器
|
||||||
|
|
||||||
|
1. 打开"此电脑"
|
||||||
|
2. 右键 -> "添加网络位置"
|
||||||
|
3. 输入:`http://dav.example.com/`
|
||||||
|
4. 完成向导
|
||||||
|
|
||||||
|
或使用 `net use`:
|
||||||
|
```cmd
|
||||||
|
net use * http://dav.example.com/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.6 macOS Finder
|
||||||
|
|
||||||
|
1. Finder -> "前往" -> "连接服务器"
|
||||||
|
2. 输入:`http://dav.example.com/`
|
||||||
|
3. 点击"连接"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 与 Lolly 项目的关系
|
||||||
|
|
||||||
|
### 5.1 Lolly 项目概述
|
||||||
|
|
||||||
|
Lolly 是一个用 Go 编写的高性能 HTTP 服务器/代理工具,提供:
|
||||||
|
- 灵活的路由和重写规则
|
||||||
|
- 中间件支持
|
||||||
|
- 反向代理功能
|
||||||
|
- 性能分析工具(pprof)
|
||||||
|
|
||||||
|
### 5.2 与 Nginx WebDAV 的对比
|
||||||
|
|
||||||
|
| 特性 | Nginx WebDAV | Lolly |
|
||||||
|
|------|--------------|-------|
|
||||||
|
| 协议支持 | WebDAV (有限) | 标准 HTTP |
|
||||||
|
| 配置方式 | 声明式配置 | Go 代码 |
|
||||||
|
| 扩展性 | 需重新编译 | 热插拔中间件 |
|
||||||
|
| 性能分析 | 第三方模块 | 内置 pprof |
|
||||||
|
| 学习曲线 | 低 | 中(需 Go 基础) |
|
||||||
|
|
||||||
|
### 5.3 建议与集成方案
|
||||||
|
|
||||||
|
#### 方案一:Nginx + Lolly 组合架构
|
||||||
|
|
||||||
|
```
|
||||||
|
客户端 -> Nginx (WebDAV/静态文件) -> Lolly (动态请求/代理)
|
||||||
|
```
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
# WebDAV 文件存储
|
||||||
|
location /dav/ {
|
||||||
|
root /data/storage;
|
||||||
|
dav_methods PUT DELETE MKCOL;
|
||||||
|
create_full_put_path on;
|
||||||
|
dav_access user:rw group:r;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 动态请求代理到 Lolly
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://localhost:8080;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 静态文件由 Nginx 直接服务
|
||||||
|
location /static/ {
|
||||||
|
root /data/www;
|
||||||
|
expires 30d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方案二:Lolly 实现类似 WebDAV 功能
|
||||||
|
|
||||||
|
如需在 Lolly 中实现类似 WebDAV 的文件上传功能,可参考以下思路:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 概念示例 - 实际实现请参考 Lolly 代码
|
||||||
|
router.PUT("/files/*filepath", func(c *gin.Context) {
|
||||||
|
filepath := c.Param("filepath")
|
||||||
|
fullPath := "/data/storage/" + filepath
|
||||||
|
|
||||||
|
// 创建中间目录(类似 create_full_put_path on)
|
||||||
|
os.MkdirAll(path.Dir(fullPath), 0755)
|
||||||
|
|
||||||
|
// 保存上传的文件
|
||||||
|
c.SaveUploadedFile(file, fullPath)
|
||||||
|
|
||||||
|
// 设置权限(类似 dav_access)
|
||||||
|
os.Chmod(fullPath, 0644)
|
||||||
|
|
||||||
|
c.Status(http.StatusCreated)
|
||||||
|
})
|
||||||
|
|
||||||
|
router.DELETE("/files/*filepath", func(c *gin.Context) {
|
||||||
|
filepath := c.Param("filepath")
|
||||||
|
fullPath := "/data/storage/" + filepath
|
||||||
|
|
||||||
|
// 检查深度(类似 min_delete_depth)
|
||||||
|
depth := len(strings.Split(filepath, "/"))
|
||||||
|
if depth < 4 {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "path too shallow"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(fullPath)
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方案三:Lolly 作为 WebDAV 后端代理
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /webdav/ {
|
||||||
|
# 认证和限流在 Nginx 层处理
|
||||||
|
auth_basic "WebDAV";
|
||||||
|
auth_basic_user_file /etc/nginx/.passwd;
|
||||||
|
|
||||||
|
# 限流
|
||||||
|
limit_req zone=webdav burst=10;
|
||||||
|
|
||||||
|
# 代理到 Lolly
|
||||||
|
proxy_pass http://localhost:8080;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Destination $http_destination;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 选择建议
|
||||||
|
|
||||||
|
| 场景 | 推荐方案 |
|
||||||
|
|------|----------|
|
||||||
|
| 简单文件共享 | Nginx WebDAV |
|
||||||
|
| 需要复杂业务逻辑 | Lolly |
|
||||||
|
| 高性能静态文件 | Nginx |
|
||||||
|
| 需要自定义协议扩展 | Lolly |
|
||||||
|
| 快速部署 | Nginx WebDAV |
|
||||||
|
| 深度集成应用 | Lolly |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 性能优化建议
|
||||||
|
|
||||||
|
### 6.1 文件系统建议
|
||||||
|
|
||||||
|
**强烈推荐**:临时目录和数据目录使用**同一文件系统**。
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
root /data/www; # 数据目录
|
||||||
|
client_body_temp_path /data/client_temp; # 临时目录(同文件系统)
|
||||||
|
|
||||||
|
dav_methods PUT DELETE;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因:**
|
||||||
|
- 同文件系统:文件上传使用 `rename()` 系统调用(原子操作,极快)
|
||||||
|
- 不同文件系统:文件上传需要 `copy + delete`(慢,占用双倍空间)
|
||||||
|
|
||||||
|
### 6.2 客户端大小限制
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 限制上传文件大小,防止资源耗尽
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
|
# 调整缓冲区
|
||||||
|
client_body_buffer_size 128k;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 超时设置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# WebDAV 操作可能需要较长时间
|
||||||
|
client_body_timeout 300s;
|
||||||
|
send_timeout 300s;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 故障排查
|
||||||
|
|
||||||
|
### 7.1 常见问题
|
||||||
|
|
||||||
|
| 问题 | 可能原因 | 解决方案 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 405 Method Not Allowed | 方法未启用 | 检查 `dav_methods` 配置 |
|
||||||
|
| 403 Forbidden | 权限不足或 IP 限制 | 检查 `dav_access` 和 `limit_except` |
|
||||||
|
| 409 Conflict | 目录不存在 | 设置 `create_full_put_path on` |
|
||||||
|
| 423 Locked | 文件被锁定 | Nginx 不支持锁,换客户端 |
|
||||||
|
| 500 Internal Error | 路径过深或循环 | 检查 `min_delete_depth` |
|
||||||
|
|
||||||
|
### 7.2 启用调试日志
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
error_log /var/log/nginx/error.log debug;
|
||||||
|
```
|
||||||
|
|
||||||
|
查看日志:
|
||||||
|
```bash
|
||||||
|
tail -f /var/log/nginx/error.log | grep dav
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 验证配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试配置文件语法
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
# 重新加载配置
|
||||||
|
nginx -s reload
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 完整配置模板
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# /etc/nginx/conf.d/webdav.conf
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name dav.example.com;
|
||||||
|
|
||||||
|
# 访问日志
|
||||||
|
access_log /var/log/nginx/webdav_access.log;
|
||||||
|
error_log /var/log/nginx/webdav_error.log;
|
||||||
|
|
||||||
|
# 客户端限制
|
||||||
|
client_max_body_size 500M;
|
||||||
|
client_body_buffer_size 128k;
|
||||||
|
client_body_timeout 300s;
|
||||||
|
send_timeout 300s;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# 根目录
|
||||||
|
root /data/webdav;
|
||||||
|
|
||||||
|
# 临时目录(与根目录同文件系统)
|
||||||
|
client_body_temp_path /data/webdav_tmp;
|
||||||
|
|
||||||
|
# 启用 WebDAV 方法
|
||||||
|
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||||
|
|
||||||
|
# 允许自动创建中间目录
|
||||||
|
create_full_put_path on;
|
||||||
|
|
||||||
|
# 文件权限设置
|
||||||
|
dav_access user:rw group:r all:r;
|
||||||
|
|
||||||
|
# 防止误删顶层目录
|
||||||
|
min_delete_depth 2;
|
||||||
|
|
||||||
|
# HTTP 基本认证
|
||||||
|
auth_basic "WebDAV Repository";
|
||||||
|
auth_basic_user_file /etc/nginx/.webdav_passwd;
|
||||||
|
|
||||||
|
# 访问控制
|
||||||
|
limit_except GET {
|
||||||
|
# 允许内网
|
||||||
|
allow 192.168.0.0/16;
|
||||||
|
allow 10.0.0.0/8;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
# 拒绝其他
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 目录列表(可选)
|
||||||
|
autoindex on;
|
||||||
|
autoindex_format html;
|
||||||
|
autoindex_localtime on;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 参考资源
|
||||||
|
|
||||||
|
- [Nginx 官方文档 - ngx_http_dav_module](https://nginx.org/en/docs/http/ngx_http_dav_module.html)
|
||||||
|
- [WebDAV RFC 4918](https://tools.ietf.org/html/rfc4918)
|
||||||
|
- [cadaver 客户端](http://www.webdav.org/cadaver/)
|
||||||
|
- [davfs2 - Linux WebDAV 挂载](http://savannah.nongnu.org/projects/davfs2/)
|
||||||
1502
docs/38-nginx-zone-sync-module.md
Normal file
1502
docs/38-nginx-zone-sync-module.md
Normal file
File diff suppressed because it is too large
Load Diff
603
docs/39-nginx-tunnel-module.md
Normal file
603
docs/39-nginx-tunnel-module.md
Normal file
@ -0,0 +1,603 @@
|
|||||||
|
# NGINX HTTP Tunnel 模块文档
|
||||||
|
|
||||||
|
## 1. 模块概述
|
||||||
|
|
||||||
|
### 1.1 简介
|
||||||
|
|
||||||
|
`ngx_http_tunnel_module` 是 NGINX 的商业模块,用于处理 HTTP CONNECT 请求并建立端到端的虚拟连接隧道。
|
||||||
|
|
||||||
|
**版本要求**: NGINX 1.29.3 及以上
|
||||||
|
|
||||||
|
**授权**: 仅作为 F5 NGINX 商业订阅的一部分提供,开源版本不包含此模块
|
||||||
|
|
||||||
|
### 1.2 核心用途
|
||||||
|
|
||||||
|
- **HTTP 代理隧道**: 处理 RFC 9110 定义的 CONNECT 方法,用于建立 HTTPS 代理隧道
|
||||||
|
- **TCP 穿透**: 允许客户端通过 HTTP 代理与后端 TCP 服务建立直接连接
|
||||||
|
- **动态路由**: 支持变量实现动态目标地址和绑定地址
|
||||||
|
|
||||||
|
### 1.3 配置上下文
|
||||||
|
|
||||||
|
支持以下配置块:
|
||||||
|
- `http`
|
||||||
|
- `server`
|
||||||
|
- `location`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 指令详解
|
||||||
|
|
||||||
|
### 2.1 核心指令
|
||||||
|
|
||||||
|
#### tunnel_pass
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_pass [address];
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | 无(必须显式配置) |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
| **支持变量** | 是 |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 启用 CONNECT 请求处理
|
||||||
|
- 默认目标地址为 `$host:$request_port`
|
||||||
|
- `address` 可以是:域名、IP 地址、端口、UNIX 套接字路径或上游服务器组名称
|
||||||
|
- 支持变量实现动态路由
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```nginx
|
||||||
|
tunnel_pass $host:$request_port;
|
||||||
|
tunnel_pass backend_upstream;
|
||||||
|
tunnel_pass 127.0.0.1:8443;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_allow_upstream
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_allow_upstream string ...;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | 无 |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
| **支持变量** | 是 |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 定义允许访问后端服务器的条件
|
||||||
|
- 所有参数必须非空且不等于 "0" 才允许连接
|
||||||
|
- 每次建立连接前都会评估
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```nginx
|
||||||
|
tunnel_allow_upstream $allow_port $allow_host;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_bind
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_bind address | off;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | 无 |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
| **支持变量** | 是 |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 指定出站连接到后端服务器时使用的本地 IP 地址(可选端口)
|
||||||
|
- `off` 取消从上级配置继承的效果
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
```nginx
|
||||||
|
tunnel_bind 192.168.1.100;
|
||||||
|
tunnel_bind $local_ip:$local_port;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_bind_dynamic
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_bind_dynamic on | off;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `off` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 启用后,每次连接尝试时都会执行 `tunnel_bind` 操作
|
||||||
|
- 适用于 `tunnel_bind` 中使用动态变量的场景
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_socket_keepalive
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_socket_keepalive on | off;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `off` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 配置出站连接的 TCP keepalive 行为
|
||||||
|
- `on` 开启 `SO_KEEPALIVE` 选项
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.2 超时与缓冲指令
|
||||||
|
|
||||||
|
#### tunnel_connect_timeout
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_connect_timeout time;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `60s` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 建立后端连接的超时时间
|
||||||
|
- 通常不应超过 75 秒
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_read_timeout
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_read_timeout time;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `60s` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 客户端或后端连接上两次连续读写操作之间的超时
|
||||||
|
- 无数据传输时关闭连接
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_send_timeout
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_send_timeout time;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `60s` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 向后端服务器传输请求的超时时间
|
||||||
|
- 仅针对两次连续写操作之间
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_buffer_size
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_buffer_size size;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `16k` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 设置用于从后端服务器读取数据的缓冲区大小
|
||||||
|
- 同时也设置从客户端读取数据的缓冲区大小
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_send_lowat
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_send_lowat size;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `0` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 非零值时尝试最小化发送操作(使用 `NOTE_LOWAT` 或 `SO_SNDLOWAT`)
|
||||||
|
- **注意**: 在 Linux、Solaris 和 Windows 上被忽略
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2.3 上游故障转移指令
|
||||||
|
|
||||||
|
#### tunnel_next_upstream
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_next_upstream error | timeout | denied | off ...;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `error timeout` |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 指定何时将请求传递给下一台服务器
|
||||||
|
- `denied`: 被 `tunnel_allow_upstream` 拒绝
|
||||||
|
- `off`: 禁用故障转移
|
||||||
|
|
||||||
|
**重要限制**: 若已向客户端发送数据,则无法传递到下一台服务器
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_next_upstream_timeout
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_next_upstream_timeout time;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `0`(无限制) |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 限制请求传递给下一台服务器的总时间
|
||||||
|
- `0` 表示关闭限制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### tunnel_next_upstream_tries
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
tunnel_next_upstream_tries number;
|
||||||
|
```
|
||||||
|
|
||||||
|
| 属性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **默认值** | `0`(无限制) |
|
||||||
|
| **上下文** | http, server, location |
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 限制传递给下一台服务器的尝试次数
|
||||||
|
- `0` 表示关闭限制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. TCP 隧道配置示例
|
||||||
|
|
||||||
|
### 3.1 基础 HTTP 代理
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
# 定义允许的端口
|
||||||
|
map $request_port $allow_port {
|
||||||
|
443 1;
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 定义允许的域名
|
||||||
|
map $host $allow_host {
|
||||||
|
hostnames;
|
||||||
|
example.org 1;
|
||||||
|
*.example.org 1;
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
resolver dns.example.com;
|
||||||
|
|
||||||
|
# 权限检查
|
||||||
|
if ($allow_port != 1) {
|
||||||
|
return 502;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($allow_host != 1) {
|
||||||
|
return 502;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 启用隧道穿透
|
||||||
|
tunnel_pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**客户端使用**:
|
||||||
|
```bash
|
||||||
|
curl -x http://nginx:8000 https://example.org
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 带上游服务器的 TCP 隧道
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
upstream backend_pool {
|
||||||
|
server 10.0.0.1:8443;
|
||||||
|
server 10.0.0.2:8443;
|
||||||
|
server 10.0.0.3:8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# 所有 CONNECT 请求转发到上游池
|
||||||
|
tunnel_pass backend_pool;
|
||||||
|
|
||||||
|
# 故障转移配置
|
||||||
|
tunnel_next_upstream error timeout;
|
||||||
|
tunnel_next_upstream_tries 3;
|
||||||
|
tunnel_connect_timeout 30s;
|
||||||
|
|
||||||
|
# 本地绑定地址
|
||||||
|
tunnel_bind $server_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 动态目标路由
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
map $http_x_target_host $tunnel_target {
|
||||||
|
default $host:$request_port;
|
||||||
|
api.internal 10.0.0.100:8443;
|
||||||
|
db.internal unix:/var/run/db.sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
tunnel_pass $tunnel_target;
|
||||||
|
tunnel_connect_timeout 10s;
|
||||||
|
tunnel_read_timeout 300s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. WebSocket 隧道配置
|
||||||
|
|
||||||
|
**注意**: HTTP Tunnel 模块主要用于 TCP 隧道。WebSocket 支持取决于具体实现。
|
||||||
|
|
||||||
|
### 4.1 WebSocket 代理配置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream websocket_backend {
|
||||||
|
server 127.0.0.1:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
|
||||||
|
location /ws/ {
|
||||||
|
# WebSocket 升级
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
|
||||||
|
# 隧道穿透(如后端支持 CONNECT)
|
||||||
|
tunnel_pass websocket_backend;
|
||||||
|
|
||||||
|
# 长连接超时
|
||||||
|
tunnel_read_timeout 3600s;
|
||||||
|
tunnel_send_timeout 3600s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 与 stream 模块的区别
|
||||||
|
|
||||||
|
| 特性 | HTTP Tunnel Module | Stream Module |
|
||||||
|
|------|-------------------|---------------|
|
||||||
|
| **层级** | HTTP 层 (L7) | 传输层 (L4) |
|
||||||
|
| **协议** | 处理 HTTP CONNECT 请求 | 原始 TCP/UDP |
|
||||||
|
| **授权** | 商业订阅 | 开源免费 |
|
||||||
|
| **配置块** | `http`/`server`/`location` | `stream`/`server` |
|
||||||
|
| **路由能力** | 基于 HTTP 头、Host 等 L7 信息 | 仅基于 IP/端口 |
|
||||||
|
| **变量支持** | 丰富的 HTTP 变量 | 有限的 stream 变量 |
|
||||||
|
| **访问控制** | 可基于域名、端口、自定义条件 | 基于 IP 的 allow/deny |
|
||||||
|
| **典型用途** | HTTP 代理、HTTPS 穿透 | 数据库代理、TCP 负载均衡 |
|
||||||
|
|
||||||
|
### 5.1 stream 模块示例对比
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# stream 模块 (L4 层)
|
||||||
|
stream {
|
||||||
|
server {
|
||||||
|
listen 8443;
|
||||||
|
proxy_pass backend_pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# tunnel 模块 (L7 层,处理 CONNECT)
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
location / {
|
||||||
|
tunnel_pass backend_pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 与 Lolly 项目的关系和建议
|
||||||
|
|
||||||
|
### 6.1 Lolly 项目概述
|
||||||
|
|
||||||
|
Lolly 是一个 Go 语言实现的高性能网络代理/隧道项目,专注于:
|
||||||
|
- 轻量级部署
|
||||||
|
- 简洁的配置
|
||||||
|
- 高性能转发
|
||||||
|
|
||||||
|
### 6.2 功能对比
|
||||||
|
|
||||||
|
| 特性 | NGINX Tunnel | Lolly |
|
||||||
|
|------|-------------|-------|
|
||||||
|
| **CONNECT 支持** | 原生支持 | 需确认实现 |
|
||||||
|
| **配置复杂度** | 高(多指令组合) | 低(简洁配置) |
|
||||||
|
| **动态路由** | 支持变量 | 需确认 |
|
||||||
|
| **故障转移** | 完善的上游故障转移 | 需确认 |
|
||||||
|
| **性能** | 高(C 语言优化) | 高(Go 并发模型) |
|
||||||
|
| **可观测性** | 标准日志 | 可定制 |
|
||||||
|
| **扩展性** | 模块扩展 | 代码扩展 |
|
||||||
|
|
||||||
|
### 6.3 对 Lolly 的建议
|
||||||
|
|
||||||
|
#### 6.3.1 参考设计
|
||||||
|
|
||||||
|
1. **CONNECT 方法处理**: 参考 `tunnel_pass` 的默认行为 `$host:$request_port`
|
||||||
|
2. **条件访问控制**: 实现类似 `tunnel_allow_upstream` 的灵活条件评估
|
||||||
|
3. **超时分层**: 区分连接、读取、发送超时,默认值参考 NGINX
|
||||||
|
|
||||||
|
#### 6.3.2 差异化优势
|
||||||
|
|
||||||
|
1. **简化配置**: Lolly 可提供更简洁的单行配置实现常见场景
|
||||||
|
2. **原生可观测性**: 内置 pprof、指标导出(已支持 pprof 端点)
|
||||||
|
3. **动态重载**: Go 的热重载比 NGINX 更友好
|
||||||
|
|
||||||
|
#### 6.3.3 建议新增功能
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 建议的 Lolly 配置格式
|
||||||
|
tunnel:
|
||||||
|
enable: true
|
||||||
|
default_target: "$host:$port" # 类似 tunnel_pass
|
||||||
|
allowed_ports: [443, 8443] # 类似 map $allow_port
|
||||||
|
allowed_hosts: ["*.example.com"]
|
||||||
|
timeouts:
|
||||||
|
connect: 60s
|
||||||
|
read: 300s
|
||||||
|
send: 60s
|
||||||
|
bind_address: "0.0.0.0" # 类似 tunnel_bind
|
||||||
|
keepalive: true # 类似 tunnel_socket_keepalive
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.3.4 实现优先级
|
||||||
|
|
||||||
|
| 优先级 | 功能 | 参考 NGINX 指令 |
|
||||||
|
|--------|------|---------------|
|
||||||
|
| P0 | 基础 CONNECT 处理 | `tunnel_pass` |
|
||||||
|
| P0 | 访问控制(端口/域名) | `tunnel_allow_upstream` |
|
||||||
|
| P1 | 连接超时 | `tunnel_connect_timeout` |
|
||||||
|
| P1 | 读写超时 | `tunnel_read_timeout` / `tunnel_send_timeout` |
|
||||||
|
| P2 | 本地地址绑定 | `tunnel_bind` |
|
||||||
|
| P2 | TCP Keepalive | `tunnel_socket_keepalive` |
|
||||||
|
| P3 | 上游故障转移 | `tunnel_next_upstream` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录
|
||||||
|
|
||||||
|
### A. 完整配置模板
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
http {
|
||||||
|
# 1. 访问控制映射
|
||||||
|
map $request_port $tunnel_allow_port {
|
||||||
|
443 1;
|
||||||
|
8443 1;
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
map $host $tunnel_allow_host {
|
||||||
|
hostnames;
|
||||||
|
example.com 1;
|
||||||
|
*.example.com 1;
|
||||||
|
default 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. 代理服务器
|
||||||
|
server {
|
||||||
|
listen 8000;
|
||||||
|
server_name proxy.example.com;
|
||||||
|
|
||||||
|
# DNS 解析
|
||||||
|
resolver 8.8.8.8 8.8.4.4 valid=30s;
|
||||||
|
|
||||||
|
# 访问控制
|
||||||
|
if ($tunnel_allow_port != 1) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tunnel_allow_host != 1) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 隧道配置
|
||||||
|
tunnel_pass $host:$request_port;
|
||||||
|
|
||||||
|
# 超时配置
|
||||||
|
tunnel_connect_timeout 30s;
|
||||||
|
tunnel_read_timeout 300s;
|
||||||
|
tunnel_send_timeout 60s;
|
||||||
|
|
||||||
|
# 缓冲配置
|
||||||
|
tunnel_buffer_size 32k;
|
||||||
|
|
||||||
|
# Keepalive
|
||||||
|
tunnel_socket_keepalive on;
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
access_log /var/log/nginx/tunnel_access.log;
|
||||||
|
error_log /var/log/nginx/tunnel_error.log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### B. 调试命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试 CONNECT 请求
|
||||||
|
curl -v -x http://nginx:8000 https://example.org
|
||||||
|
|
||||||
|
# 查看连接状态
|
||||||
|
nginx -T | grep tunnel
|
||||||
|
|
||||||
|
# 监控日志
|
||||||
|
tail -f /var/log/nginx/tunnel_access.log
|
||||||
|
tail -f /var/log/nginx/tunnel_error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### C. 参考资料
|
||||||
|
|
||||||
|
- [NGINX 官方文档](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html)
|
||||||
|
- [RFC 9110 - CONNECT 方法](https://datatracker.ietf.org/doc/html/rfc9110#section-9.3.6)
|
||||||
|
- [F5 NGINX 商业订阅](https://www.f5.com/products/nginx)
|
||||||
Loading…
x
Reference in New Issue
Block a user