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:
xfy 2026-04-08 10:27:06 +08:00
parent 238fa20e41
commit 4d108267c3
8 changed files with 7918 additions and 1 deletions

View File

@ -930,4 +930,557 @@ 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

File diff suppressed because it is too large Load Diff

1501
docs/34-nginx-oidc-module.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
View 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/)

File diff suppressed because it is too large Load Diff

View 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)