feat(http3,docs,middleware): 实现 Phase 9 HTTP/3 与性能优化扩展
- 新增 HTTP/3 (QUIC) 服务器支持,集成到 App 生命周期管理 - 新增 nginx 内置变量速查表文档 - 完善多篇 nginx 文档(代理、安全、流、限流、HTTP/2/3、核心事件) - 新增一致性哈希负载均衡、gzip_static、滑动窗口限流中间件 - 扩展配置支持 HTTP/3 和日志格式选项 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a627d58832
commit
d6367a1c38
@ -175,7 +175,7 @@ logging:
|
||||
format: "text" # 全局日志格式(有效值: text, json),控制启动/停止日志格式
|
||||
access:
|
||||
path: "" # 日志文件路径(空表示输出到 stdout)
|
||||
format: "$remote_addr - $remote_user [$time] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"" # 访问日志格式,近似 nginx combined
|
||||
format: '$remote_addr - $remote_user [$time] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"' # 访问日志格式,近似 nginx combined
|
||||
# 支持变量: $remote_addr, $remote_user, $request, $status, $body_bytes_sent, $request_time, $http_referer, $http_user_agent, $time
|
||||
# 特殊值 "json" 输出结构化 JSON
|
||||
error:
|
||||
|
||||
@ -436,7 +436,207 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 12. FastCGI 代理
|
||||
## 12. 高级代理指令
|
||||
|
||||
### proxy_bind
|
||||
|
||||
指定连接后端时使用的源地址,用于多网卡服务器选择出口IP。
|
||||
|
||||
```nginx
|
||||
语法: proxy_bind address [transparent];
|
||||
默认: —
|
||||
上下文: http, server, location
|
||||
```
|
||||
|
||||
```nginx
|
||||
proxy_bind $server_addr; # 使用服务器IP
|
||||
proxy_bind 192.168.1.1 transparent; # 透明代理(需要root权限)
|
||||
```
|
||||
|
||||
### proxy_intercept_errors
|
||||
|
||||
拦截后端错误响应,配合 error_page 自定义错误页面。
|
||||
|
||||
```nginx
|
||||
语法: proxy_intercept_errors on | off;
|
||||
默认: off
|
||||
上下文: http, server, location
|
||||
```
|
||||
|
||||
```nginx
|
||||
proxy_intercept_errors on;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
```
|
||||
|
||||
### proxy_hide_header / proxy_pass_header
|
||||
|
||||
控制后端响应头的传递行为:
|
||||
|
||||
```nginx
|
||||
# 隐藏后端返回的特定头
|
||||
proxy_hide_header X-Powered-By;
|
||||
proxy_hide_header X-Runtime;
|
||||
|
||||
# 传递被默认隐藏的头
|
||||
proxy_pass_header X-Accel-Redirect;
|
||||
proxy_pass_header X-Accel-Limit-Rate;
|
||||
```
|
||||
|
||||
### proxy_ignore_headers
|
||||
|
||||
忽略后端的特定响应头(如缓存控制),允许NGINX处理这些头:
|
||||
|
||||
```nginx
|
||||
proxy_ignore_headers Cache-Control Expires X-Accel-Redirect X-Accel-Expires;
|
||||
```
|
||||
|
||||
### proxy_cookie_* 系列
|
||||
|
||||
修改后端返回的 Set-Cookie 头:
|
||||
|
||||
```nginx
|
||||
# 修改域名
|
||||
proxy_cookie_domain localhost example.com;
|
||||
proxy_cookie_domain off; # 禁用域名修改
|
||||
|
||||
# 修改路径
|
||||
proxy_cookie_path /foo/ /bar/;
|
||||
proxy_cookie_path off; # 禁用路径修改
|
||||
|
||||
# 添加安全标志
|
||||
proxy_cookie_flags session httponly secure samesite=strict;
|
||||
proxy_cookie_flags * samesite=lax; # 应用到所有cookie
|
||||
```
|
||||
|
||||
### proxy_limit_rate
|
||||
|
||||
限制从后端读取响应的传输速率:
|
||||
|
||||
```nginx
|
||||
proxy_limit_rate 100k; # 100KB/s
|
||||
```
|
||||
|
||||
### proxy_request_buffering
|
||||
|
||||
控制请求是否先完整缓冲再发送到后端:
|
||||
|
||||
```nginx
|
||||
proxy_request_buffering on; # 默认,完整缓冲
|
||||
proxy_request_buffering off; # 流式传输,支持上传进度
|
||||
```
|
||||
|
||||
### proxy_redirect
|
||||
|
||||
修改后端返回的重定向头 Location 和 Refresh:
|
||||
|
||||
```nginx
|
||||
proxy_redirect default; # 使用默认替换
|
||||
proxy_redirect off; # 禁用替换
|
||||
proxy_redirect http://localhost:8080/ http://$host/; # 自定义替换
|
||||
proxy_redirect ~^http://([^/]+)/(.+)$ http://$host/$2; # 使用正则
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. SSL 客户端证书认证 (proxy_ssl_*)
|
||||
|
||||
用于 mTLS 双向认证场景,NGINX 作为客户端向后端提供证书:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass https://backend.example.com;
|
||||
|
||||
# mTLS 双向认证
|
||||
proxy_ssl_certificate /path/to/client.crt;
|
||||
proxy_ssl_certificate_key /path/to/client.key;
|
||||
|
||||
# 验证后端证书
|
||||
proxy_ssl_verify on;
|
||||
proxy_ssl_trusted_certificate /path/to/ca.crt;
|
||||
proxy_ssl_verify_depth 2;
|
||||
|
||||
# SSL协议和加密套件
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_ssl_ciphers HIGH:!aNULL;
|
||||
|
||||
# 会话复用
|
||||
proxy_ssl_session_reuse on;
|
||||
|
||||
# SNI支持
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_name backend.example.com;
|
||||
}
|
||||
```
|
||||
|
||||
| 指令 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `proxy_ssl_certificate` | 客户端证书路径 | — |
|
||||
| `proxy_ssl_certificate_key` | 客户端私钥路径 | — |
|
||||
| `proxy_ssl_verify` | 验证后端证书 | off |
|
||||
| `proxy_ssl_trusted_certificate` | 受信任CA证书 | — |
|
||||
| `proxy_ssl_verify_depth` | 验证深度 | 1 |
|
||||
| `proxy_ssl_protocols` | 启用的协议 | TLSv1.2 TLSv1.3 |
|
||||
| `proxy_ssl_ciphers` | 加密套件 | DEFAULT |
|
||||
| `proxy_ssl_session_reuse` | 会话复用 | on |
|
||||
| `proxy_ssl_name` | SNI名称 | — |
|
||||
|
||||
---
|
||||
|
||||
## 14. 高级缓存指令
|
||||
|
||||
### proxy_cache_methods
|
||||
|
||||
指定可缓存的请求方法:
|
||||
|
||||
```nginx
|
||||
proxy_cache_methods GET HEAD POST; # 可缓存 POST 请求
|
||||
```
|
||||
|
||||
### proxy_cache_min_uses
|
||||
|
||||
设置最小访问次数才开始缓存,避免缓存低频请求:
|
||||
|
||||
```nginx
|
||||
proxy_cache_min_uses 3; # 第3次访问才开始缓存
|
||||
```
|
||||
|
||||
### proxy_cache_background_update
|
||||
|
||||
后台异步更新过期缓存(类似 stale-while-revalidate):
|
||||
|
||||
```nginx
|
||||
proxy_cache_background_update on;
|
||||
```
|
||||
|
||||
### proxy_cache_revalidate
|
||||
|
||||
使用 If-Modified-Since 和 If-None-Match 重新验证缓存:
|
||||
|
||||
```nginx
|
||||
proxy_cache_revalidate on; # 减少数据传输
|
||||
```
|
||||
|
||||
### proxy_cache_convert_head
|
||||
|
||||
自动将 HEAD 请求转为 GET 以获取响应体:
|
||||
|
||||
```nginx
|
||||
proxy_cache_convert_head on; # 默认启用
|
||||
```
|
||||
|
||||
### proxy_cache_purge
|
||||
|
||||
支持 PURGE 方法清除缓存(需编译时启用):
|
||||
|
||||
```nginx
|
||||
location ~ /purge(/.*) {
|
||||
proxy_cache_purge cache_zone $1;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. FastCGI 代理
|
||||
|
||||
### 基础配置
|
||||
|
||||
@ -449,22 +649,113 @@ location ~ \.php$ {
|
||||
}
|
||||
```
|
||||
|
||||
### 常用 FastCGI 指令
|
||||
### FastCGI 指令完整列表
|
||||
|
||||
| 指令 | 说明 |
|
||||
|------|------|
|
||||
| `fastcgi_pass` | FastCGI 服务器地址 |
|
||||
| `fastcgi_index` | URI 以斜杠结尾时追加的文件名 |
|
||||
| `fastcgi_param` | 传递参数给 FastCGI 服务器 |
|
||||
| `fastcgi_split_path_info` | 分离 SCRIPT_NAME 和 PATH_INFO |
|
||||
| `fastcgi_connect_timeout` | 连接超时 |
|
||||
| `fastcgi_read_timeout` | 读取超时 |
|
||||
| `fastcgi_buffer_size` | 响应头缓冲区大小 |
|
||||
| `fastcgi_buffers` | 响应体缓冲区 |
|
||||
| 指令 | 语法 | 默认值 | 上下文 |
|
||||
|------|------|--------|--------|
|
||||
| `fastcgi_pass` | fastcgi_pass address; | — | location |
|
||||
| `fastcgi_index` | fastcgi_index name; | — | http, server, location |
|
||||
| `fastcgi_param` | fastcgi_param parameter value [if_not_empty]; | — | http, server, location |
|
||||
| `fastcgi_split_path_info` | fastcgi_split_path_info regex; | — | location |
|
||||
| `fastcgi_buffer_size` | fastcgi_buffer_size size; | 4k/8k | http, server, location |
|
||||
| `fastcgi_buffers` | fastcgi_buffers number size; | 8 4k/8k | http, server, location |
|
||||
| `fastcgi_busy_buffers_size` | fastcgi_busy_buffers_size size; | 8k/16k | http, server, location |
|
||||
| `fastcgi_temp_file_write_size` | fastcgi_temp_file_write_size size; | 8k/16k | http, server, location |
|
||||
| `fastcgi_temp_path` | fastcgi_temp_path path [level1 [level2 [level3]]]; | — | http, server, location |
|
||||
| `fastcgi_cache` | fastcgi_cache zone; | — | http, server, location |
|
||||
| `fastcgi_cache_key` | fastcgi_cache_key string; | — | http, server, location |
|
||||
| `fastcgi_cache_valid` | fastcgi_cache_valid [code...] time; | — | http, server, location |
|
||||
| `fastcgi_cache_methods` | fastcgi_cache_methods method...; | GET HEAD | http, server, location |
|
||||
| `fastcgi_cache_min_uses` | fastcgi_cache_min_uses number; | 1 | http, server, location |
|
||||
| `fastcgi_cache_bypass` | fastcgi_cache_bypass string...; | — | http, server, location |
|
||||
| `fastcgi_no_cache` | fastcgi_no_cache string...; | — | http, server, location |
|
||||
| `fastcgi_cache_use_stale` | fastcgi_cache_use_stale condition...; | — | http, server, location |
|
||||
| `fastcgi_cache_background_update` | fastcgi_cache_background_update on/off; | off | http, server, location |
|
||||
| `fastcgi_cache_revalidate` | fastcgi_cache_revalidate on/off; | off | http, server, location |
|
||||
| `fastcgi_cache_lock` | fastcgi_cache_lock on/off; | off | http, server, location |
|
||||
| `fastcgi_cache_lock_timeout` | fastcgi_cache_lock_timeout time; | 5s | http, server, location |
|
||||
| `fastcgi_cache_convert_head` | fastcgi_cache_convert_head on/off; | on | http, server, location |
|
||||
| `fastcgi_connect_timeout` | fastcgi_connect_timeout time; | 60s | http, server, location |
|
||||
| `fastcgi_send_timeout` | fastcgi_send_timeout time; | 60s | http, server, location |
|
||||
| `fastcgi_read_timeout` | fastcgi_read_timeout time; | 60s | http, server, location |
|
||||
| `fastcgi_send_lowat` | fastcgi_send_lowat size; | 0 | http, server, location |
|
||||
| `fastcgi_request_buffering` | fastcgi_request_buffering on/off; | on | http, server, location |
|
||||
| `fastcgi_intercept_errors` | fastcgi_intercept_errors on/off; | off | http, server, location |
|
||||
| `fastcgi_hide_header` | fastcgi_hide_header field; | — | http, server, location |
|
||||
| `fastcgi_pass_header` | fastcgi_pass_header field; | — | http, server, location |
|
||||
| `fastcgi_ignore_headers` | fastcgi_ignore_headers field...; | — | http, server, location |
|
||||
| `fastcgi_limit_rate` | fastcgi_limit_rate rate; | 0 | http, server, location |
|
||||
|
||||
### FastCGI 缓存完整配置示例
|
||||
|
||||
```nginx
|
||||
http {
|
||||
# 缓存路径定义
|
||||
fastcgi_cache_path /var/cache/nginx/php
|
||||
levels=1:2
|
||||
keys_zone=php:10m
|
||||
max_size=100m
|
||||
inactive=60m
|
||||
use_temp_path=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com;
|
||||
|
||||
location ~ \.php$ {
|
||||
# 启用缓存
|
||||
fastcgi_cache php;
|
||||
fastcgi_cache_key "$scheme$request_method$host$request_uri";
|
||||
|
||||
# 缓存有效期
|
||||
fastcgi_cache_valid 200 302 1h;
|
||||
fastcgi_cache_valid 404 1m;
|
||||
fastcgi_cache_valid any 5m;
|
||||
|
||||
# 使用过期缓存
|
||||
fastcgi_cache_use_stale error timeout updating http_500 http_503;
|
||||
|
||||
# 后台更新
|
||||
fastcgi_cache_background_update on;
|
||||
|
||||
# 重新验证
|
||||
fastcgi_cache_revalidate on;
|
||||
|
||||
# 缓存锁
|
||||
fastcgi_cache_lock on;
|
||||
fastcgi_cache_lock_timeout 5s;
|
||||
|
||||
# 绕过缓存条件
|
||||
fastcgi_cache_bypass $cookie_nocache $arg_nocache;
|
||||
fastcgi_no_cache $http_pragma $http_authorization;
|
||||
|
||||
# FastCGI 后端
|
||||
fastcgi_pass unix:/run/php/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
|
||||
# 超时设置
|
||||
fastcgi_connect_timeout 5s;
|
||||
fastcgi_send_timeout 60s;
|
||||
fastcgi_read_timeout 60s;
|
||||
|
||||
# 缓冲配置
|
||||
fastcgi_buffer_size 16k;
|
||||
fastcgi_buffers 8 16k;
|
||||
fastcgi_busy_buffers_size 32k;
|
||||
|
||||
# 错误处理
|
||||
fastcgi_intercept_errors on;
|
||||
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 内置变量
|
||||
## 16. 内置变量
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
@ -474,7 +765,7 @@ location ~ \.php$ {
|
||||
|
||||
---
|
||||
|
||||
## 14. 综合配置示例
|
||||
## 17. 综合配置示例
|
||||
|
||||
```nginx
|
||||
http {
|
||||
|
||||
@ -41,7 +41,235 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 2. HTTP 基础认证
|
||||
## 2. 外部认证 (ngx_http_auth_request_module)
|
||||
|
||||
NGINX 的 `ngx_http_auth_request_module` 模块(从 NGINX 1.5.4+ 开始支持,需编译时启用)提供外部认证功能,允许通过发送子请求到认证服务来决定是否允许访问。
|
||||
|
||||
### 2.1 指令说明
|
||||
|
||||
| 指令 | 语法 | 默认值 | 上下文 |
|
||||
|------|------|--------|--------|
|
||||
| `auth_request` | `auth_request uri \| off;` | `off` | http, server, location |
|
||||
| `auth_request_set` | `auth_request_set $variable value;` | — | http, server, location |
|
||||
|
||||
### 2.2 工作原理
|
||||
|
||||
1. **NGINX 发送子请求**:当收到客户端请求时,NGINX 向指定的认证 URI 发送子请求
|
||||
2. **认证服务响应**:认证服务返回 HTTP 状态码(200 表示通过,401/403 表示拒绝)
|
||||
3. **结果判定**:
|
||||
- 返回 200:NGINX 继续处理原请求
|
||||
- 返回 401/403:NGINX 拒绝访问并返回相应状态码
|
||||
- 其他状态码:视为错误,返回 500
|
||||
|
||||
**流程图**:
|
||||
```
|
||||
Client → NGINX → auth_request (子请求)
|
||||
↓
|
||||
认证服务
|
||||
↓
|
||||
200 OK? → 是 → 处理原请求 → 后端服务
|
||||
↓
|
||||
否
|
||||
↓
|
||||
返回 401/403 → Client
|
||||
```
|
||||
|
||||
### 2.3 基础配置示例
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.example.com;
|
||||
|
||||
location /protected/ {
|
||||
auth_request /auth;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
# 认证子请求 location
|
||||
location = /auth {
|
||||
internal; # 仅限内部子请求访问
|
||||
proxy_pass http://auth-server/verify;
|
||||
proxy_pass_request_body off; # 不传递请求体到认证服务
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Client-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 带变量提取的认证
|
||||
|
||||
从认证服务响应中提取自定义头部到变量:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location /api/ {
|
||||
auth_request /auth;
|
||||
auth_request_set $auth_user $upstream_http_x_user;
|
||||
auth_request_set $auth_role $upstream_http_x_role;
|
||||
|
||||
proxy_pass http://backend;
|
||||
proxy_set_header X-User $auth_user;
|
||||
proxy_set_header X-Role $auth_role;
|
||||
}
|
||||
|
||||
location = /auth {
|
||||
internal;
|
||||
proxy_pass http://auth-service/verify;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**可用变量**:
|
||||
- `$upstream_http_*`:从认证服务响应中提取任意 HTTP 头部
|
||||
- `$upstream_status`:认证服务返回的状态码
|
||||
- `$upstream_response_time`:认证响应时间
|
||||
|
||||
### 2.5 JWT/OAuth2 集成示例
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name api.example.com;
|
||||
|
||||
location /api/ {
|
||||
auth_request /auth_jwt;
|
||||
auth_request_set $jwt_claims $upstream_http_x_jwt_claims;
|
||||
auth_request_set $jwt_sub $upstream_http_x_jwt_sub;
|
||||
|
||||
proxy_pass http://api_backend;
|
||||
proxy_set_header X-JWT-Claims $jwt_claims;
|
||||
proxy_set_header X-User-Id $jwt_sub;
|
||||
|
||||
# 自定义 401 响应
|
||||
error_page 401 = @unauthorized;
|
||||
}
|
||||
|
||||
location = /auth_jwt {
|
||||
internal;
|
||||
proxy_pass http://jwt-validator/verify;
|
||||
proxy_set_header Authorization $http_authorization;
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_pass_request_body off;
|
||||
}
|
||||
|
||||
location @unauthorized {
|
||||
default_type application/json;
|
||||
return 401 '{"error":"Unauthorized","code":"INVALID_TOKEN"}';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 多级认证组合
|
||||
|
||||
结合 `auth_request` 与 `auth_basic`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location /admin/ {
|
||||
# 先进行基础认证
|
||||
auth_basic "Admin Access";
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
|
||||
# 再进行外部权限校验
|
||||
auth_request /auth_admin;
|
||||
|
||||
proxy_pass http://admin_backend;
|
||||
}
|
||||
|
||||
location = /auth_admin {
|
||||
internal;
|
||||
proxy_pass http://permission-service/check-admin;
|
||||
proxy_set_header X-User $remote_user;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.7 错误处理
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location /api/ {
|
||||
auth_request /auth;
|
||||
|
||||
# 认证失败重定向到登录页
|
||||
error_page 401 = @login;
|
||||
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
|
||||
location @login {
|
||||
return 302 https://auth.example.com/login?redirect=$request_uri;
|
||||
}
|
||||
|
||||
# 认证服务不可用时
|
||||
error_page 500 = @auth_error;
|
||||
|
||||
location @auth_error {
|
||||
return 503 '{"error":"Authentication service unavailable"}';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.8 应用场景
|
||||
|
||||
| 场景 | 实现方式 |
|
||||
|------|----------|
|
||||
| **OAuth2/OIDC 集成** | 认证服务验证 access_token,返回用户信息 |
|
||||
| **JWT Token 验证** | 验证 JWT 签名和过期时间,提取 claims |
|
||||
| **统一认证网关** | 集中处理多个服务的认证逻辑 |
|
||||
| **权限分级验证** | 根据路径或资源进行细粒度权限检查 |
|
||||
| **多因素认证** | 组合多种认证方式(密码 + 短信/邮件) |
|
||||
| **API Key 验证** | 验证请求中的 API Key 有效性 |
|
||||
|
||||
### 2.9 最佳实践
|
||||
|
||||
**1. 认证服务高可用**
|
||||
```nginx
|
||||
upstream auth_backend {
|
||||
server 192.168.1.10:8080;
|
||||
server 192.168.1.11:8080 backup;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
location = /auth {
|
||||
internal;
|
||||
proxy_pass http://auth_backend/verify;
|
||||
proxy_connect_timeout 5s;
|
||||
proxy_send_timeout 5s;
|
||||
proxy_read_timeout 5s;
|
||||
}
|
||||
```
|
||||
|
||||
**2. 缓存认证结果**(减少重复验证)
|
||||
```nginx
|
||||
location /api/ {
|
||||
auth_request /auth;
|
||||
|
||||
# 启用缓存(需配合 proxy_cache)
|
||||
proxy_cache auth_cache;
|
||||
proxy_cache_valid 200 1m;
|
||||
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
```
|
||||
|
||||
**3. 调试认证流程**
|
||||
```nginx
|
||||
# 记录认证请求日志
|
||||
log_format auth_log '$remote_addr - $time_local '
|
||||
'auth_status=$auth_request_status '
|
||||
'user=$auth_user';
|
||||
|
||||
access_log /var/log/nginx/auth.log auth_log;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. HTTP 基础认证
|
||||
|
||||
### 配置认证
|
||||
|
||||
@ -93,7 +321,7 @@ location /admin/ {
|
||||
|
||||
---
|
||||
|
||||
## 3. 请求限制
|
||||
## 4. 请求限制
|
||||
|
||||
### 请求速率限制
|
||||
|
||||
@ -168,9 +396,34 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### Dry Run 模式(限流测试)
|
||||
|
||||
NGINX 支持限流 dry run 模式,用于测试限流配置而不实际拒绝请求:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
location /api/ {
|
||||
limit_req zone=req_limit burst=20 nodelay;
|
||||
limit_req_dry_run on; # 请求不被拒绝,但记录日志
|
||||
|
||||
limit_conn conn_limit 10;
|
||||
limit_conn_dry_run on; # 连接不被拒绝,但记录日志
|
||||
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Dry Run 作用**:
|
||||
- 限流判断正常执行,但不返回 503/429 错误
|
||||
- 记录 `limiting requests/connections, dry run` 到错误日志
|
||||
- 用于评估限流阈值是否设置合理
|
||||
|
||||
> **注意**:详细限流配置请参考 [20-nginx-rate-limiting.md](./20-nginx-rate-limiting.md)
|
||||
|
||||
---
|
||||
|
||||
## 4. 安全头部
|
||||
## 5. 安全头部
|
||||
|
||||
### 基础安全头部
|
||||
|
||||
@ -212,7 +465,7 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 5. 防盗链
|
||||
## 6. 防盗链
|
||||
|
||||
### 基础防盗链
|
||||
|
||||
@ -240,7 +493,7 @@ location ~* \.(jpg|jpeg|png|gif|webp|flv|mp4|swf)$ {
|
||||
|
||||
---
|
||||
|
||||
## 6. SSL/TLS 安全
|
||||
## 7. SSL/TLS 安全
|
||||
|
||||
### 协议配置
|
||||
|
||||
@ -278,7 +531,7 @@ openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
|
||||
|
||||
---
|
||||
|
||||
## 7. 防止常见攻击
|
||||
## 8. 防止常见攻击
|
||||
|
||||
### SQL 注入防护
|
||||
|
||||
@ -337,7 +590,7 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 8. 限制特定 User-Agent
|
||||
## 9. 限制特定 User-Agent
|
||||
|
||||
```nginx
|
||||
# 阻止恶意爬虫
|
||||
@ -365,7 +618,7 @@ if ($block_ua) {
|
||||
|
||||
---
|
||||
|
||||
## 9. WAF 配置(ModSecurity)
|
||||
## 10. WAF 配置(ModSecurity)
|
||||
|
||||
### 安装 ModSecurity
|
||||
|
||||
@ -392,7 +645,7 @@ modsecurity_rules '
|
||||
|
||||
---
|
||||
|
||||
## 10. fail2ban 集成
|
||||
## 11. fail2ban 集成
|
||||
|
||||
### 创建 filter
|
||||
|
||||
@ -419,7 +672,7 @@ maxretry = 10
|
||||
|
||||
---
|
||||
|
||||
## 11. 安全配置检查清单
|
||||
## 12. 安全配置检查清单
|
||||
|
||||
### 基础安全
|
||||
|
||||
@ -459,7 +712,7 @@ maxretry = 10
|
||||
|
||||
---
|
||||
|
||||
## 12. 安全测试工具
|
||||
## 13. 安全测试工具
|
||||
|
||||
### 在线测试
|
||||
|
||||
|
||||
@ -104,6 +104,8 @@ stream {
|
||||
| 轮询 | 默认 | 加权轮询 |
|
||||
| 最少连接 | `least_conn;` | 连接数最少优先 |
|
||||
| 哈希 | `hash key [consistent];` | 基于键哈希 |
|
||||
| 最少时间 | `least_time header \| last_byte \| last_byte inflight;` | 最小响应时间(NGINX Plus) |
|
||||
| 随机 | `random [two] [least_conn];` | 随机选择 |
|
||||
|
||||
### server 参数
|
||||
|
||||
@ -117,6 +119,67 @@ stream {
|
||||
| `down` | 标记不可用 |
|
||||
| `resolve` | 解析域名 IP 变化 |
|
||||
|
||||
### 新负载均衡算法
|
||||
|
||||
#### least_time(最小响应时间)
|
||||
|
||||
**版本要求**:NGINX Plus
|
||||
|
||||
```nginx
|
||||
upstream mysql_backend {
|
||||
least_time header; # 或 last_byte, last_byte inflight
|
||||
server 192.168.1.1:3306;
|
||||
server 192.168.1.2:3306;
|
||||
zone mysql 64k;
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `header` | 以接收到上游第一个字节的时间为度量 |
|
||||
| `last_byte` | 以接收到上游完整响应的时间为度量 |
|
||||
| `last_byte inflight` | 考虑正在传输的数据 |
|
||||
|
||||
#### random(随机选择)
|
||||
|
||||
```nginx
|
||||
upstream backend {
|
||||
random; # 纯随机
|
||||
# random two; # 随机选两个,按权重选
|
||||
# random two least_conn; # 随机选两个,选连接少的
|
||||
server 192.168.1.1:3306;
|
||||
server 192.168.1.2:3306;
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `two` | 随机选择两个上游服务器,再根据负载均衡策略选择 |
|
||||
| `least_conn` | 与 `two` 配合,选择连接数较少的 |
|
||||
|
||||
### upstream zone 共享内存
|
||||
|
||||
`zone` 指令启用 upstream 配置的共享内存,多 worker 进程间共享连接状态:
|
||||
|
||||
```nginx
|
||||
upstream backend {
|
||||
zone backend 64k; # 64k 共享内存
|
||||
server 192.168.1.1:3306;
|
||||
server 192.168.1.2:3306;
|
||||
keepalive 32;
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 共享内存使所有 worker 进程共享 upstream 状态
|
||||
- 必需用于 `least_conn`、`least_time`、健康检查等
|
||||
- 大小根据上游服务器数量调整
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 4. 核心指令
|
||||
@ -144,6 +207,7 @@ server {
|
||||
| `proxy_protocol` | 启用 PROXY 协议 |
|
||||
| `backlog=N` | 连接队列长度 |
|
||||
| `so_keepalive` | TCP keepalive |
|
||||
| `transparent` | 启用透明代理 |
|
||||
|
||||
### proxy_pass 指令
|
||||
|
||||
@ -173,6 +237,40 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
### 其他核心指令
|
||||
|
||||
| 指令 | 语法 | 默认值 | 上下文 | 说明 |
|
||||
|------|------|--------|--------|------|
|
||||
| `proxy_bind` | `proxy_bind address [transparent];` | — | stream, server | 指定代理连接使用的本地地址 |
|
||||
| `proxy_half_close` | `proxy_half_close on \| off;` | off | stream, server | 启用 TCP 半关闭支持 |
|
||||
| `proxy_responses` | `proxy_responses number;` | — | stream, server (UDP) | UDP 每请求期望响应数 |
|
||||
| `proxy_socket_keepalive` | `proxy_socket_keepalive on \| off;` | off | stream, server | 开启与上游的 TCP keepalive |
|
||||
|
||||
**示例配置**:
|
||||
|
||||
```nginx
|
||||
# 透明代理
|
||||
server {
|
||||
listen 3306 transparent;
|
||||
proxy_bind $remote_addr transparent;
|
||||
proxy_pass backend:3306;
|
||||
}
|
||||
|
||||
# UDP 配置
|
||||
server {
|
||||
listen 53 udp;
|
||||
proxy_pass dns_backend:53;
|
||||
proxy_responses 1; # 每请求期望1个响应
|
||||
}
|
||||
|
||||
# 半关闭支持(TCP 流式场景)
|
||||
server {
|
||||
listen 9000;
|
||||
proxy_half_close on;
|
||||
proxy_pass backend:9000;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. SSL/TLS 配置
|
||||
@ -220,6 +318,37 @@ server {
|
||||
| `proxy_ssl_verify` | 验证上游证书 |
|
||||
| `proxy_ssl_server_name` | 启用 SNI |
|
||||
|
||||
#### 完整上游 SSL 配置示例
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 3306;
|
||||
proxy_pass ssl_backend:3306;
|
||||
|
||||
proxy_ssl on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_ssl_ciphers HIGH:!aNULL;
|
||||
proxy_ssl_certificate /path/to/client.crt;
|
||||
proxy_ssl_certificate_key /path/to/client.key;
|
||||
proxy_ssl_verify on;
|
||||
proxy_ssl_trusted_certificate /path/to/ca.crt;
|
||||
proxy_ssl_verify_depth 2;
|
||||
proxy_ssl_name backend.example.com;
|
||||
proxy_ssl_session_reuse on;
|
||||
}
|
||||
```
|
||||
|
||||
**指令说明**:
|
||||
|
||||
| 指令 | 说明 |
|
||||
|------|------|
|
||||
| `proxy_ssl_certificate` | 客户端证书(mTLS) |
|
||||
| `proxy_ssl_certificate_key` | 客户端证书私钥 |
|
||||
| `proxy_ssl_ciphers` | 加密套件 |
|
||||
| `proxy_ssl_verify_depth` | 证书链验证深度 |
|
||||
| `proxy_ssl_name` | 验证上游证书的域名 |
|
||||
| `proxy_ssl_session_reuse` | 启用 SSL 会话复用 |
|
||||
|
||||
---
|
||||
|
||||
## 6. 基于名称的虚拟服务器(SNI)
|
||||
@ -254,6 +383,47 @@ stream {
|
||||
}
|
||||
```
|
||||
|
||||
### SSL Preread SNI 路由(无需终止 SSL)
|
||||
|
||||
**版本要求**:1.25.5+
|
||||
|
||||
使用 `ssl_preread` 模块在不解密的情况下读取 SNI 信息进行路由:
|
||||
|
||||
```nginx
|
||||
stream {
|
||||
ssl_preread on; # 启用 ssl_preread
|
||||
|
||||
map $ssl_preread_server_name $backend {
|
||||
mysql.example.com mysql_backend;
|
||||
redis.example.com redis_backend;
|
||||
default default_backend;
|
||||
}
|
||||
|
||||
upstream mysql_backend {
|
||||
server 192.168.1.1:3306;
|
||||
}
|
||||
|
||||
upstream redis_backend {
|
||||
server 192.168.1.2:6379;
|
||||
}
|
||||
|
||||
upstream default_backend {
|
||||
server 192.168.1.3:8080;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
ssl_preread on; # 在 server 上下文启用
|
||||
proxy_pass $backend;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `ssl_preread` 读取 ClientHello 中的 SNI 扩展
|
||||
- 无需配置 SSL 证书即可实现基于域名的路由
|
||||
- 适用于多服务共享端口的场景
|
||||
|
||||
---
|
||||
|
||||
## 7. PROXY 协议
|
||||
@ -332,15 +502,27 @@ server {
|
||||
|
||||
## 10. 连接保持
|
||||
|
||||
### keepalive 连接池配置
|
||||
|
||||
```nginx
|
||||
upstream backend {
|
||||
server 192.168.1.1:3306;
|
||||
keepalive 32; # 保持 32 个空闲连接
|
||||
keepalive_timeout 60s; # 空闲连接超时
|
||||
keepalive_requests 1000; # 单个连接最大请求数
|
||||
server 192.168.1.2:3306;
|
||||
|
||||
keepalive 32; # 空闲连接池大小
|
||||
keepalive_requests 100; # 单连接最大请求(HTTP 适用)
|
||||
keepalive_timeout 60s; # 空闲超时
|
||||
}
|
||||
```
|
||||
|
||||
**指令说明**:
|
||||
|
||||
| 指令 | 语法 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `keepalive` | `keepalive connections;` | — | 保持到上游的空闲连接数 |
|
||||
| `keepalive_requests` | `keepalive_requests number;` | 100 | 单连接最大请求数 |
|
||||
| `keepalive_timeout` | `keepalive_timeout timeout;` | 60s | 空闲连接超时时间 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 内置变量
|
||||
@ -358,6 +540,18 @@ upstream backend {
|
||||
| `$status` | 会话状态 |
|
||||
| `$ssl_preread_server_name` | SNI 名称 |
|
||||
|
||||
### 其他 stream 指令
|
||||
|
||||
| 指令 | 语法 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `preread_buffer_size` | `preread_buffer_size size;` | 16k | 预读缓冲区大小 |
|
||||
| `preread_timeout` | `preread_timeout timeout;` | 30s | 预读超时时间 |
|
||||
| `resolver` | `resolver address ... [valid=time];` | — | DNS 解析器 |
|
||||
| `resolver_timeout` | `resolver_timeout time;` | 30s | 解析超时 |
|
||||
| `tcp_nodelay` | `tcp_nodelay on \| off;` | on | 启用 TCP_NODELAY |
|
||||
| `variables_hash_max_size` | `variables_hash_max_size size;` | 1024 | 变量哈希表最大大小 |
|
||||
| `variables_hash_bucket_size` | `variables_hash_bucket_size size;` | 64 | 变量哈希表桶大小 |
|
||||
|
||||
---
|
||||
|
||||
## 12. 应用场景示例
|
||||
|
||||
@ -32,6 +32,7 @@ http {
|
||||
| `$remote_addr` | 客户端 IP 地址(文本格式) |
|
||||
| `zone=name:size` | 共享内存区域名称和大小 |
|
||||
| `rate=Nr/s` 或 `rate=Nr/m` | 限流速率(每秒/每分钟请求数) |
|
||||
| `sync` | 多 worker 间同步限流状态(1.15.2+) |
|
||||
|
||||
**内存使用估算**:
|
||||
- 1MB 共享内存约可存储 16,000 个 IP 地址状态(使用 `$binary_remote_addr`)
|
||||
@ -86,7 +87,54 @@ server {
|
||||
|
||||
**可选级别**:`info`, `notice`, `warn`, `error`
|
||||
|
||||
### 1.5 burst 和 nodelay 参数详解
|
||||
### 1.5 limit_req_dry_run 试运行模式
|
||||
|
||||
```
|
||||
语法: limit_req_dry_run on | off;
|
||||
默认: off
|
||||
上下文: http, server, location
|
||||
版本: 1.19.1+
|
||||
```
|
||||
|
||||
**功能**: 试运行模式,只记录限流事件但不实际拒绝请求。用于测试限流配置效果。
|
||||
|
||||
```nginx
|
||||
http {
|
||||
limit_req_zone $binary_remote_addr zone=prod:10m rate=100r/s;
|
||||
|
||||
server {
|
||||
location /api/ {
|
||||
limit_req zone=prod burst=200 nodelay;
|
||||
limit_req_dry_run on; # 试运行模式,不影响用户
|
||||
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**应用场景**:
|
||||
- 生产环境测试新限流策略
|
||||
- 验证限流阈值是否合理
|
||||
- 收集真实流量数据
|
||||
|
||||
### 1.6 $limit_req 变量
|
||||
|
||||
```
|
||||
变量: $limit_req
|
||||
功能: 存储请求因限流被延迟的时间(毫秒)
|
||||
```
|
||||
|
||||
**用途**: 在日志中记录限流延迟,用于监控分析。
|
||||
|
||||
```nginx
|
||||
log_format limit '$remote_addr - $limit_req ms - $request_uri';
|
||||
access_log /var/log/nginx/limit.log limit;
|
||||
```
|
||||
|
||||
**说明**: 如果请求未被限流延迟,变量值为空。
|
||||
|
||||
### 1.7 burst 和 nodelay 参数详解
|
||||
|
||||
**令牌桶算法原理**:
|
||||
|
||||
@ -132,7 +180,7 @@ limit_req zone=ip_limit burst=20 delay=10;
|
||||
- 第 11-20 个请求延迟处理
|
||||
- 超过 20 个请求返回 503
|
||||
|
||||
### 1.6 多区域限流配置
|
||||
### 1.8 多区域限流配置
|
||||
|
||||
**分层限流策略**:
|
||||
|
||||
@ -174,6 +222,26 @@ http {
|
||||
- 任一区域超限即触发限流
|
||||
- 可实现更精细的控制策略
|
||||
|
||||
### 1.9 动态试运行控制
|
||||
|
||||
```nginx
|
||||
# 通过请求头控制试运行模式
|
||||
map $http_x_dry_run $dry_run_mode {
|
||||
default off;
|
||||
test on;
|
||||
}
|
||||
|
||||
server {
|
||||
location /api/ {
|
||||
limit_req zone=api burst=200;
|
||||
limit_req_dry_run $dry_run_mode;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**说明**: `limit_req_dry_run` 支持使用变量动态控制,可根据请求头、Cookie 等条件灵活开启/关闭试运行模式。
|
||||
|
||||
---
|
||||
|
||||
## 2. ngx_http_limit_conn_module (连接限制)
|
||||
@ -199,6 +267,20 @@ http {
|
||||
- 不需要 `rate` 参数(只计数,不限速)
|
||||
- 统计的是并发连接数,不是请求速率
|
||||
|
||||
**sync 参数**(1.15.2+):
|
||||
|
||||
在多 worker 环境下(worker_processes > 1),默认各 worker 独立计数。sync 参数使所有 worker 共享计数,实现精确的全局限流。
|
||||
|
||||
```nginx
|
||||
limit_conn_zone $binary_remote_addr zone=addr:10m sync;
|
||||
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s sync;
|
||||
```
|
||||
|
||||
**适用场景**:
|
||||
- 多 worker 部署需要精确统计
|
||||
- 分布式限流场景
|
||||
- 高并发环境防止计数偏差
|
||||
|
||||
### 2.2 limit_conn 应用连接限制
|
||||
|
||||
```nginx
|
||||
@ -231,7 +313,32 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 limit_conn_status 设置拒绝状态码
|
||||
### 2.3 limit_conn_dry_run 试运行模式
|
||||
|
||||
```
|
||||
语法: limit_conn_dry_run on | off;
|
||||
默认: off
|
||||
上下文: http, server, location
|
||||
版本: 1.19.1+
|
||||
```
|
||||
|
||||
**功能**: 连接限制试运行模式。
|
||||
|
||||
```nginx
|
||||
http {
|
||||
limit_conn_zone $binary_remote_addr zone=addr:10m;
|
||||
|
||||
server {
|
||||
location /download/ {
|
||||
limit_conn addr 10;
|
||||
limit_conn_dry_run on;
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 limit_conn_status 设置拒绝状态码
|
||||
|
||||
```nginx
|
||||
server {
|
||||
@ -242,7 +349,7 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 limit_conn_log_level 设置日志级别
|
||||
### 2.5 limit_conn_log_level 设置日志级别
|
||||
|
||||
```nginx
|
||||
server {
|
||||
|
||||
@ -411,11 +411,160 @@ quic_retry on;
|
||||
quic_max_udp_payload_size 1200;
|
||||
```
|
||||
|
||||
### 6.6 http3_stream_buffer_size
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `http3_stream_buffer_size size;` | `64k` | `http`, `server` |
|
||||
|
||||
设置 HTTP/3 流缓冲区大小,用于控制单个流的内存使用。
|
||||
|
||||
```nginx
|
||||
# 大文件传输场景
|
||||
http3_stream_buffer_size 128k;
|
||||
```
|
||||
|
||||
### 6.7 http3_max_concurrent_streams
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `http3_max_concurrent_streams number;` | `128` | `http`, `server` |
|
||||
|
||||
设置单个 HTTP/3 连接中最大并发流数量。
|
||||
|
||||
```nginx
|
||||
# 高并发场景
|
||||
http3_max_concurrent_streams 256;
|
||||
```
|
||||
|
||||
### 6.8 http3_max_field_size
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `http3_max_field_size size;` | `4k` | `http`, `server` |
|
||||
|
||||
设置 QPACK 压缩后的请求头字段最大大小。
|
||||
|
||||
```nginx
|
||||
# 支持较大的 Cookie 头部
|
||||
http3_max_field_size 16k;
|
||||
```
|
||||
|
||||
### 6.9 http3_max_table_size
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `http3_max_table_size size;` | `16k` | `http`, `server` |
|
||||
|
||||
设置 QPACK 动态表最大大小,影响头部压缩效率。
|
||||
|
||||
```nginx
|
||||
# 提升压缩率
|
||||
http3_max_table_size 32k;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 0-RTT 连接配置
|
||||
## 7. ngx_http_quic_module 指令详解
|
||||
|
||||
### 7.1 ssl_early_data 指令
|
||||
### 7.1 quic_bpf
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_bpf on \| off;` | `off` | `http`, `server` |
|
||||
|
||||
启用 eBPF 加速 QUIC 连接路由,提升多 worker 场景性能。
|
||||
|
||||
**要求**:Linux 5.6+ 内核和 eBPF 支持。
|
||||
|
||||
```nginx
|
||||
# 高流量场景启用 eBPF 加速
|
||||
quic_bpf on;
|
||||
```
|
||||
|
||||
### 7.2 quic_cc_algorithm
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_cc_algorithm algorithm;` | `cubic` | `http`, `server` |
|
||||
|
||||
设置 QUIC 拥塞控制算法。
|
||||
|
||||
| 算法 | 说明 |
|
||||
|------|------|
|
||||
| `cubic` | 默认,适合大多数场景 |
|
||||
| `reno` | 经典 TCP 拥塞控制 |
|
||||
| `bbr` | Google BBR,高延迟网络推荐 |
|
||||
|
||||
```nginx
|
||||
# 高延迟网络优化
|
||||
quic_cc_algorithm bbr;
|
||||
```
|
||||
|
||||
### 7.3 quic_mtu
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_mtu size;` | `—` | `http`, `server` |
|
||||
|
||||
设置 QUIC MTU 大小,影响数据包分片。
|
||||
|
||||
```nginx
|
||||
# 以太网标准 MTU
|
||||
quic_mtu 1200;
|
||||
|
||||
# 数据中心内部网络
|
||||
quic_mtu 1400;
|
||||
```
|
||||
|
||||
### 7.4 quic_active_connection_id_limit
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_active_connection_id_limit number;` | `8` | `http`, `server` |
|
||||
|
||||
设置活跃连接 ID 数量限制,影响连接迁移能力。
|
||||
|
||||
```nginx
|
||||
# 增强连接迁移能力
|
||||
quic_active_connection_id_limit 16;
|
||||
```
|
||||
|
||||
### 7.5 quic_stack
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_stack ngx \| boringssl;` | `ngx` | `http`, `server` |
|
||||
|
||||
选择 QUIC 协议栈实现。
|
||||
|
||||
| 值 | 说明 |
|
||||
|------|------|
|
||||
| `ngx` | nginx 原生实现(推荐) |
|
||||
| `boringssl` | BoringSSL QUIC 实现 |
|
||||
|
||||
```nginx
|
||||
quic_stack ngx;
|
||||
```
|
||||
|
||||
### 7.6 quic_socket_options
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
| `quic_socket_options option ...;` | `—` | `http`, `server` |
|
||||
|
||||
设置 QUIC socket 选项,用于优化 UDP 性能。
|
||||
|
||||
```nginx
|
||||
# 优化 socket 缓冲区
|
||||
quic_socket_options receive_buffer=1m send_buffer=1m;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 0-RTT 连接配置
|
||||
|
||||
### 9.1 ssl_early_data 指令
|
||||
|
||||
| 语法 | 默认值 | 上下文 |
|
||||
|------|--------|--------|
|
||||
@ -438,7 +587,7 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 0-RTT 安全注意事项
|
||||
### 9.2 0-RTT 安全注意事项
|
||||
|
||||
```nginx
|
||||
# 限制 0-RTT 请求(防止重放攻击)
|
||||
@ -463,9 +612,9 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 8. HTTP/3 完整配置示例
|
||||
## 10. HTTP/3 完整配置示例
|
||||
|
||||
### 8.1 基础配置
|
||||
### 10.1 基础配置
|
||||
|
||||
```nginx
|
||||
http {
|
||||
@ -493,7 +642,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 生产环境配置
|
||||
### 10.2 生产环境配置
|
||||
|
||||
```nginx
|
||||
http {
|
||||
@ -553,9 +702,9 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 9. HTTP/1.1 vs HTTP/2 vs HTTP/3 对比
|
||||
## 11. HTTP/1.1 vs HTTP/2 vs HTTP/3 对比
|
||||
|
||||
### 9.1 特性对比表
|
||||
### 11.1 特性对比表
|
||||
|
||||
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|
||||
|------|----------|--------|--------|
|
||||
@ -570,7 +719,7 @@ http {
|
||||
| **握手延迟** | 高 | 中 | 低 |
|
||||
| **NAT 友好** | 是 | 是 | 需特殊处理 |
|
||||
|
||||
### 9.2 性能对比
|
||||
### 11.2 性能对比
|
||||
|
||||
| 场景 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|
||||
|------|----------|--------|--------|
|
||||
@ -579,7 +728,7 @@ http {
|
||||
| **丢包网络** | 基准 | 轻微下降 | 显著提升 |
|
||||
| **移动网络切换** | 需重连 | 需重连 | 无缝迁移 |
|
||||
|
||||
### 9.3 适用场景
|
||||
### 11.3 适用场景
|
||||
|
||||
| 协议 | 推荐场景 |
|
||||
|------|----------|
|
||||
@ -589,9 +738,9 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 10. 迁移指南和兼容性
|
||||
## 12. 迁移指南和兼容性
|
||||
|
||||
### 10.1 渐进式迁移策略
|
||||
### 12.1 渐进式迁移策略
|
||||
|
||||
```nginx
|
||||
http {
|
||||
@ -622,7 +771,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2 Alt-Svc 头部详解
|
||||
### 12.2 Alt-Svc 头部详解
|
||||
|
||||
```nginx
|
||||
# 基础声明
|
||||
@ -640,7 +789,7 @@ add_header Alt-Svc 'h3=":8443"; ma=3600' always;
|
||||
- `h3-29`:HTTP/3 草案版本(兼容旧客户端)
|
||||
- `ma`:最大有效期(秒)
|
||||
|
||||
### 10.3 浏览器兼容性
|
||||
### 12.3 浏览器兼容性
|
||||
|
||||
| 浏览器 | HTTP/2 | HTTP/3 |
|
||||
|--------|--------|--------|
|
||||
@ -649,7 +798,7 @@ add_header Alt-Svc 'h3=":8443"; ma=3600' always;
|
||||
| Safari 11+ | 支持 | 14+ 实验性,后续稳定 |
|
||||
| Edge 79+ | 支持 | 87+ 实验性,后续稳定 |
|
||||
|
||||
### 10.4 回退策略
|
||||
### 12.4 回退策略
|
||||
|
||||
```nginx
|
||||
map $http_user_agent $supports_http3 {
|
||||
@ -674,9 +823,9 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 11. 性能优化建议
|
||||
## 13. 性能优化建议
|
||||
|
||||
### 11.1 HTTP/2 优化
|
||||
### 13.1 HTTP/2 优化
|
||||
|
||||
```nginx
|
||||
http {
|
||||
@ -713,7 +862,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 11.2 HTTP/3 优化
|
||||
### 13.2 HTTP/3 优化
|
||||
|
||||
```nginx
|
||||
http {
|
||||
@ -746,7 +895,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 11.3 内核参数优化
|
||||
### 13.3 内核参数优化
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.conf
|
||||
@ -770,7 +919,7 @@ net.netfilter.nf_conntrack_udp_timeout_stream = 120
|
||||
sysctl -p
|
||||
```
|
||||
|
||||
### 11.4 监控指标
|
||||
### 13.4 监控指标
|
||||
|
||||
```nginx
|
||||
# 在日志中记录协议版本
|
||||
@ -784,7 +933,7 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
### 11.5 调试检查
|
||||
### 13.5 调试检查
|
||||
|
||||
```bash
|
||||
# 检查 HTTP/2 支持
|
||||
@ -805,9 +954,9 @@ curl -I https://www.example.com | grep -i alt-svc
|
||||
|
||||
---
|
||||
|
||||
## 12. 常见问题排查
|
||||
## 14. 常见问题排查
|
||||
|
||||
### 12.1 HTTP/2 问题
|
||||
### 14.1 HTTP/2 问题
|
||||
|
||||
| 问题 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
@ -815,7 +964,7 @@ curl -I https://www.example.com | grep -i alt-svc
|
||||
| 大量 STREAM_CLOSED 错误 | 客户端提前关闭 | 正常行为,无需处理 |
|
||||
| 内存占用高 | 流数量过多 | 减少 http2_max_concurrent_streams |
|
||||
|
||||
### 12.2 HTTP/3 问题
|
||||
### 14.2 HTTP/3 问题
|
||||
|
||||
| 问题 | 原因 | 解决 |
|
||||
|------|------|------|
|
||||
@ -826,7 +975,7 @@ curl -I https://www.example.com | grep -i alt-svc
|
||||
|
||||
---
|
||||
|
||||
## 13. 参考链接
|
||||
## 15. 参考链接
|
||||
|
||||
- [NGINX HTTP/2 文档](https://nginx.org/en/docs/http/ngx_http_v2_module.html)
|
||||
- [NGINX HTTP/3 文档](https://nginx.org/en/docs/http/ngx_http_v3_module.html)
|
||||
|
||||
@ -49,7 +49,7 @@ worker_processes 8;
|
||||
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
|
||||
|
||||
# 使用 auto 模式(自动绑定)
|
||||
worker_cpu affinity auto;
|
||||
worker_cpu_affinity auto;
|
||||
```
|
||||
|
||||
**位掩码说明**:
|
||||
@ -66,6 +66,31 @@ worker_cpu affinity auto;
|
||||
|
||||
---
|
||||
|
||||
#### worker_priority
|
||||
|
||||
设置 worker 进程的调度优先级(类似 nice 命令)。
|
||||
|
||||
**语法**:`worker_priority number;`
|
||||
|
||||
**默认值**:`worker_priority 0;`
|
||||
|
||||
**上下文**:main
|
||||
|
||||
```nginx
|
||||
# 高优先级(关键服务)
|
||||
worker_priority -5;
|
||||
|
||||
# 低优先级(后台任务)
|
||||
worker_priority 10;
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 负数表示更高优先级(-20 为最高)
|
||||
- 正数表示更低优先级(19 为最低)
|
||||
- 需要 root 权限才能设置为负值
|
||||
|
||||
---
|
||||
|
||||
#### worker_rlimit_nofile
|
||||
|
||||
设置每个 worker 进程可以打开的最大文件描述符数量。
|
||||
@ -338,7 +363,66 @@ include /etc/nginx/conf.d/stream.conf;
|
||||
|
||||
---
|
||||
|
||||
### 1.7 系统相关指令
|
||||
### 1.7 动态模块加载指令
|
||||
|
||||
#### load_module
|
||||
|
||||
动态加载 NGINX 模块(.so 文件)。
|
||||
|
||||
**语法**:`load_module file;`
|
||||
|
||||
**默认值**:无
|
||||
|
||||
**上下文**:main
|
||||
|
||||
**版本**:1.9.11+
|
||||
|
||||
```nginx
|
||||
# 加载动态模块
|
||||
load_module modules/ngx_http_geoip_module.so;
|
||||
load_module modules/ngx_stream_module.so;
|
||||
load_module modules/ngx_http_image_filter_module.so;
|
||||
|
||||
worker_processes auto;
|
||||
events { ... }
|
||||
```
|
||||
|
||||
**编译动态模块**:
|
||||
|
||||
```bash
|
||||
./configure --with-compat --add-dynamic-module=/path/to/module
|
||||
make modules
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `load_module` 指令必须在 `events` 和 `http` 块之前
|
||||
- 模块文件路径可以是绝对路径或相对于前缀目录的路径
|
||||
- 需要 `--with-compat` 选项确保模块兼容性
|
||||
|
||||
---
|
||||
|
||||
### 1.8 系统相关指令
|
||||
|
||||
#### ssl_engine
|
||||
|
||||
指定硬件 SSL 加速设备。
|
||||
|
||||
**语法**:`ssl_engine device;`
|
||||
|
||||
**默认值**:无
|
||||
|
||||
**上下文**:main
|
||||
|
||||
```nginx
|
||||
ssl_engine /dev/crypto; # 使用硬件加密设备
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 用于启用硬件 SSL 加速卡(如 OpenSSL 硬件引擎)
|
||||
- 需要系统支持相应的加密硬件设备
|
||||
- 可以显著提升 SSL/TLS 加密解密性能
|
||||
|
||||
---
|
||||
|
||||
#### timer_resolution
|
||||
|
||||
@ -605,9 +689,90 @@ events {
|
||||
|
||||
---
|
||||
|
||||
## 3. 连接处理方法详解
|
||||
### 2.5 哈希表优化指令
|
||||
|
||||
### 3.1 epoll (Linux)
|
||||
#### types_hash_max_size 与 types_hash_bucket_size
|
||||
|
||||
控制 MIME 类型哈希表的内存分配。
|
||||
|
||||
**语法**:
|
||||
- `types_hash_max_size size;`
|
||||
- `types_hash_bucket_size size;`
|
||||
|
||||
**默认值**:
|
||||
- `types_hash_max_size 1024;`
|
||||
- `types_hash_bucket_size 64;`
|
||||
|
||||
**上下文**:http, server, location
|
||||
|
||||
```nginx
|
||||
# 大量 MIME 类型时调整
|
||||
http {
|
||||
types_hash_max_size 2048;
|
||||
types_hash_bucket_size 128;
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- `types_hash_max_size`:设置 MIME 类型哈希表的最大条目数
|
||||
- `types_hash_bucket_size`:设置每个哈希桶的大小(必须是 2 的幂)
|
||||
- 当配置大量自定义 MIME 类型时需要调整
|
||||
|
||||
---
|
||||
|
||||
#### variables_hash_max_size 与 variables_hash_bucket_size
|
||||
|
||||
控制变量哈希表的大小。
|
||||
|
||||
**语法**:
|
||||
- `variables_hash_max_size size;`
|
||||
- `variables_hash_bucket_size size;`
|
||||
|
||||
**默认值**:
|
||||
- `variables_hash_max_size 1024;`
|
||||
- `variables_hash_bucket_size 64;`
|
||||
|
||||
**上下文**:http
|
||||
|
||||
```nginx
|
||||
# 大量自定义变量
|
||||
http {
|
||||
variables_hash_max_size 2048;
|
||||
variables_hash_bucket_size 128;
|
||||
}
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 当使用大量 `map` 指令或自定义变量时需要增大
|
||||
- 如果启动时报 "could not build variables_hash" 错误,需要调整这些值
|
||||
|
||||
---
|
||||
|
||||
## 3. 事件模型深入对比
|
||||
|
||||
| 模型 | 平台 | 内核要求 | FD 限制 | 触发模式 | 特性 |
|
||||
|------|------|----------|---------|----------|------|
|
||||
| epoll | Linux | 2.6+ | 内存限制 | ET/LT | EPOLLEXCLUSIVE (2.6.39+) |
|
||||
| kqueue | FreeBSD/macOS | 4.1+ | 内存限制 | ET | EVFILT_TIMER/SIGNAL/PROC |
|
||||
| eventport | Solaris | 10+ | 内存限制 | — | 多事件源支持 |
|
||||
| /dev/poll | Solaris | — | 内存限制 | — | 状态持久化 |
|
||||
| select | POSIX | — | 1024 | — | 不推荐生产使用 |
|
||||
| poll | POSIX | — | 内存限制 | — | 不推荐生产使用 |
|
||||
|
||||
**epoll vs kqueue 性能对比**:
|
||||
|
||||
- **epoll**:Linux 标准,ET 模式需要循环读取直到 EAGAIN,支持 `EPOLLEXCLUSIVE`(2.6.39+)解决惊群问题
|
||||
- **kqueue**:FreeBSD/macOS 标准,API 更优雅统一,支持定时器/信号/进程事件过滤,天然无惊群问题
|
||||
|
||||
**触发模式说明**:
|
||||
- **ET(Edge Triggered,边缘触发)**:仅在状态变化时通知,需一次性处理所有数据
|
||||
- **LT(Level Triggered,水平触发)**:只要就绪就通知,不需要一次性读完
|
||||
|
||||
---
|
||||
|
||||
## 4. 连接处理方法详解
|
||||
|
||||
### 4.1 epoll (Linux)
|
||||
|
||||
`epoll` 是 Linux 2.6 内核引入的高效 I/O 多路复用机制。
|
||||
|
||||
@ -659,7 +824,7 @@ NGINX 使用边缘触发模式,要求:
|
||||
|
||||
---
|
||||
|
||||
### 3.2 kqueue (FreeBSD/macOS)
|
||||
### 4.2 kqueue (FreeBSD/macOS)
|
||||
|
||||
`kqueue` 是 FreeBSD 引入的高性能事件通知机制,macOS 也支持。
|
||||
|
||||
@ -706,7 +871,7 @@ NGINX 使用边缘触发模式,要求:
|
||||
|
||||
---
|
||||
|
||||
### 3.3 /dev/poll (Solaris)
|
||||
### 4.3 /dev/poll (Solaris)
|
||||
|
||||
Solaris 特有的 I/O 多路复用机制。
|
||||
|
||||
@ -731,7 +896,7 @@ ioctl(dpfd, DP_POLL, &dvpoll);
|
||||
|
||||
---
|
||||
|
||||
### 3.4 eventport (Solaris)
|
||||
### 4.4 eventport (Solaris)
|
||||
|
||||
Solaris 10+ 引入的高性能事件端口机制。
|
||||
|
||||
@ -756,7 +921,7 @@ port_get(port, &event, NULL);
|
||||
|
||||
---
|
||||
|
||||
### 3.5 select/poll (通用)
|
||||
### 4.5 select/poll (通用)
|
||||
|
||||
标准的 POSIX I/O 多路复用机制,几乎所有平台都支持。
|
||||
|
||||
@ -793,9 +958,9 @@ poll(fds, nfds, timeout);
|
||||
|
||||
---
|
||||
|
||||
## 4. 各平台最佳配置
|
||||
## 5. 各平台最佳配置
|
||||
|
||||
### 4.1 Linux (2.6.39+)
|
||||
### 5.1 Linux (2.6.39+)
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/nginx.conf
|
||||
@ -822,7 +987,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 FreeBSD
|
||||
### 5.2 FreeBSD
|
||||
|
||||
```nginx
|
||||
# /usr/local/etc/nginx/nginx.conf
|
||||
@ -838,7 +1003,7 @@ events {
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 macOS (开发环境)
|
||||
### 5.3 macOS (开发环境)
|
||||
|
||||
```nginx
|
||||
# /usr/local/etc/nginx/nginx.conf
|
||||
@ -853,7 +1018,7 @@ events {
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Solaris
|
||||
### 5.4 Solaris
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/nginx.conf
|
||||
@ -870,9 +1035,9 @@ events {
|
||||
|
||||
---
|
||||
|
||||
## 5. 性能调优建议
|
||||
## 6. 性能调优建议
|
||||
|
||||
### 5.1 Worker 进程优化
|
||||
### 6.1 Worker 进程优化
|
||||
|
||||
```nginx
|
||||
# 匹配 CPU 核心数
|
||||
@ -887,7 +1052,7 @@ worker_rlimit_nofile 65535;
|
||||
# daemon off; # 前台运行(Docker/systemd)
|
||||
```
|
||||
|
||||
### 5.2 事件处理优化
|
||||
### 6.2 事件处理优化
|
||||
|
||||
```nginx
|
||||
events {
|
||||
@ -907,7 +1072,7 @@ events {
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 内核参数优化
|
||||
### 6.3 内核参数优化
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.conf
|
||||
@ -932,7 +1097,7 @@ fs.file-max = 2097152
|
||||
fs.nr_open = 2097152
|
||||
```
|
||||
|
||||
### 5.4 文件描述符限制
|
||||
### 6.4 文件描述符限制
|
||||
|
||||
```bash
|
||||
# /etc/security/limits.conf
|
||||
@ -947,15 +1112,15 @@ LimitNOFILE=65535
|
||||
|
||||
---
|
||||
|
||||
## 6. 连接数计算公式
|
||||
## 7. 连接数计算公式
|
||||
|
||||
### 6.1 基本公式
|
||||
### 7.1 基本公式
|
||||
|
||||
```
|
||||
总并发连接数 = worker_processes × worker_connections
|
||||
```
|
||||
|
||||
### 6.2 详细计算
|
||||
### 7.2 详细计算
|
||||
|
||||
#### Web 服务器场景
|
||||
|
||||
@ -983,7 +1148,7 @@ LimitNOFILE=65535
|
||||
# worker_connections = (50000 + 64) / 4 × 2 ≈ 25,032
|
||||
```
|
||||
|
||||
### 6.3 系统限制检查
|
||||
### 7.3 系统限制检查
|
||||
|
||||
```bash
|
||||
# 检查系统文件描述符限制
|
||||
@ -997,7 +1162,7 @@ ss -s
|
||||
cat /proc/$(pgrep -o nginx)/limits | grep "Max open files"
|
||||
```
|
||||
|
||||
### 6.4 配置示例
|
||||
### 7.4 配置示例
|
||||
|
||||
```nginx
|
||||
# 支持 100,000 并发连接的完整配置
|
||||
@ -1029,7 +1194,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 监控指标
|
||||
### 7.5 监控指标
|
||||
|
||||
```nginx
|
||||
# 启用 stub_status 监控
|
||||
@ -1058,9 +1223,9 @@ Reading: 6 Writing: 128 Waiting: 157 # 读/写/等待状态连接数
|
||||
|
||||
---
|
||||
|
||||
## 7. 完整配置示例
|
||||
## 8. 完整配置示例
|
||||
|
||||
### 7.1 高性能 Web 服务器
|
||||
### 8.1 高性能 Web 服务器
|
||||
|
||||
```nginx
|
||||
user nginx;
|
||||
@ -1117,7 +1282,7 @@ http {
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 高性能反向代理
|
||||
### 8.2 高性能反向代理
|
||||
|
||||
```nginx
|
||||
user nginx;
|
||||
@ -1180,9 +1345,9 @@ http {
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见问题排查
|
||||
## 9. 常见问题排查
|
||||
|
||||
### 8.1 "too many open files" 错误
|
||||
### 9.1 "too many open files" 错误
|
||||
|
||||
**原因**:
|
||||
- `worker_rlimit_nofile` 设置过低
|
||||
@ -1202,7 +1367,7 @@ sysctl -p
|
||||
ulimit -n
|
||||
```
|
||||
|
||||
### 8.2 "worker_connections are not enough" 错误
|
||||
### 9.2 "worker_connections are not enough" 错误
|
||||
|
||||
**原因**:并发连接数超过 `worker_connections` 限制
|
||||
|
||||
@ -1213,7 +1378,7 @@ events {
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 性能下降排查
|
||||
### 9.3 性能下降排查
|
||||
|
||||
```bash
|
||||
# 检查 worker 进程是否均匀分布
|
||||
@ -1229,7 +1394,7 @@ top -p $(pgrep -d',' nginx)
|
||||
cat /proc/$(pgrep -o nginx)/limits
|
||||
```
|
||||
|
||||
### 8.4 热升级失败
|
||||
### 9.4 热升级失败
|
||||
|
||||
**原因**:
|
||||
- PID 文件路径错误
|
||||
|
||||
255
docs/25-nginx-variables-reference.md
Normal file
255
docs/25-nginx-variables-reference.md
Normal file
@ -0,0 +1,255 @@
|
||||
# Nginx 内置变量速查表
|
||||
|
||||
本文档汇总 nginx 所有模块提供的内置变量,便于快速查阅。
|
||||
|
||||
---
|
||||
|
||||
## 1. HTTP 核心模块变量 (ngx_http_core_module)
|
||||
|
||||
### 请求信息
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$arg_name` | 请求参数 name 的值 | foo |
|
||||
| `$args` | 所有请求参数 | a=1&b=2 |
|
||||
| `$is_args` | 是否有参数 | ? 或 空 |
|
||||
| `$query_string` | 同 $args | a=1&b=2 |
|
||||
| `$content_length` | Content-Length 头 | 1024 |
|
||||
| `$content_type` | Content-Type 头 | application/json |
|
||||
| `$cookie_name` | Cookie name 的值 | session_id |
|
||||
|
||||
### 客户端信息
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$remote_addr` | 客户端 IP | 192.168.1.1 |
|
||||
| `$remote_port` | 客户端端口 | 54321 |
|
||||
| `$binary_remote_addr` | 二进制 IP(4/16字节) | 用于 limit_conn |
|
||||
| `$remote_user` | 认证用户名 | admin |
|
||||
|
||||
### URI 相关
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$uri` | 当前请求 URI(解码后) | /path/to/file |
|
||||
| `$document_uri` | 同 $uri | /path/to/file |
|
||||
| `$request_uri` | 原始请求 URI(含参数) | /path?a=1 |
|
||||
| `$host` | 请求主机名(优先 Host 头) | example.com |
|
||||
| `$hostname` | 服务器主机名 | server01 |
|
||||
| `$server_name` | server 配置的第一个名字 | example.com |
|
||||
|
||||
### 请求详情
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$request` | 完整请求行 | GET / HTTP/1.1 |
|
||||
| `$request_method` | 请求方法 | GET/POST |
|
||||
| `$request_body` | 请求体内容 | {...} |
|
||||
| `$request_body_file` | 请求体临时文件路径 | /tmp/... |
|
||||
| `$request_completion` | 请求完成状态 | OK 或 空 |
|
||||
| `$request_filename` | 映射的文件路径 | /var/www/index.html |
|
||||
| `$request_id` | 唯一请求 ID(1.11.0+) | abc123... |
|
||||
| `$request_length` | 请求长度(含头) | 2048 |
|
||||
| `$request_time` | 请求处理时间(秒) | 0.001 |
|
||||
| `$document_root` | root 指令值 | /var/www |
|
||||
| `$realpath_root` | root 的真实路径 | /var/www |
|
||||
|
||||
### 服务器信息
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$server_addr` | 服务器 IP | 10.0.0.1 |
|
||||
| `$server_port` | 服务器端口 | 80 |
|
||||
| `$scheme` | 协议 | http/https |
|
||||
| `$server_protocol` | HTTP 版本 | HTTP/1.1 |
|
||||
| `$https` | 是否 HTTPS | on 或 空 |
|
||||
|
||||
### 响应信息
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$status` | 响应状态码 | 200/404 |
|
||||
| `$body_bytes_sent` | 响应体字节数 | 1024 |
|
||||
| `$bytes_sent` | 总发送字节 | 2048 |
|
||||
|
||||
### 时间相关
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$time_local` | 本地时间 | 03/Apr/2026:14:30:00 |
|
||||
| `$time_iso8601` | ISO8601 时间 | 2026-04-03T14:30:00 |
|
||||
| `$msec` | 毫秒时间戳 | 1617456789.123 |
|
||||
|
||||
### 连接信息
|
||||
|
||||
| 变量 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| `$connection` | 连接序号 | 12345 |
|
||||
| `$connection_requests` | 连接请求数 | 10 |
|
||||
| `$pipe` | 是否管道化 | p 或 . |
|
||||
| `$pid` | worker 进程 PID | 12345 |
|
||||
|
||||
### PROXY 协议
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$proxy_protocol_addr` | 客户端真实 IP |
|
||||
| `$proxy_protocol_port` | 客户端真实端口 |
|
||||
| `$proxy_protocol_server_addr` | 服务器 IP |
|
||||
| `$proxy_protocol_server_port` | 服务器端口 |
|
||||
|
||||
### TCP 信息
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$tcpinfo_rtt` | RTT(微秒) |
|
||||
| `$tcpinfo_rttvar` | RTT 方差 |
|
||||
| `$tcpinfo_snd_cwnd` | 发送窗口 |
|
||||
| `$tcpinfo_rcv_space` | 接收窗口 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Upstream 模块变量 (ngx_http_upstream_module)
|
||||
|
||||
| 变量 | 说明 | 用途 |
|
||||
|------|------|------|
|
||||
| `$upstream_addr` | 后端地址 | 日志 |
|
||||
| `$upstream_status` | 后端状态码 | 监控 |
|
||||
| `$upstream_response_time` | 后端响应时间 | 性能分析 |
|
||||
| `$upstream_response_length` | 后端响应长度 | 日志 |
|
||||
| `$upstream_connect_time` | 连接耗时 | 性能分析 |
|
||||
| `$upstream_header_time` | 头部接收耗时 | 性能分析 |
|
||||
| `$upstream_first_byte_time` | 首字节时间 | 性能分析 |
|
||||
| `$upstream_bytes_sent` | 发送到后端字节 | 流量统计 |
|
||||
| `$upstream_bytes_received` | 从后端接收字节 | 流量统计 |
|
||||
| `$upstream_cache_status` | 缓存状态 | HIT/MISS/BYPASS |
|
||||
| `$upstream_http_name` | 后端响应头 | 提取认证信息 |
|
||||
| `$upstream_cookie_name` | 后端 Cookie | 提取 Cookie |
|
||||
|
||||
---
|
||||
|
||||
## 3. SSL/TLS 模块变量 (ngx_http_ssl_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$ssl_cipher` | 加密套件 |
|
||||
| `$ssl_ciphers` | 支持的加密套件列表 |
|
||||
| `$ssl_protocol` | SSL 协议版本 |
|
||||
| `$ssl_server_name` | SNI 服务器名 |
|
||||
| `$ssl_session_id` | 会话 ID |
|
||||
| `$ssl_session_reused` | 是否重用会话 |
|
||||
| `$ssl_client_cert` | 客户端证书 |
|
||||
| `$ssl_client_raw_cert` | 客户端原始证书 |
|
||||
| `$ssl_client_escaped_cert` | 转义证书 |
|
||||
| `$ssl_client_fingerprint` | 证书指纹 |
|
||||
| `$ssl_client_i_dn` | 签发者 DN |
|
||||
| `$ssl_client_s_dn` | 主体 DN |
|
||||
| `$ssl_client_serial` | 证书序列号 |
|
||||
| `$ssl_client_verify` | 验证结果 |
|
||||
| `$ssl_client_v_start` | 证书开始时间 |
|
||||
| `$ssl_client_v_end` | 证书结束时间 |
|
||||
| `$ssl_client_v_remain` | 证书剩余天数 |
|
||||
| `$ssl_curves` | 支持的曲线 |
|
||||
| `$ssl_early_data` | 是否早期数据 |
|
||||
|
||||
---
|
||||
|
||||
## 4. Proxy 模块变量 (ngx_http_proxy_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$proxy_add_x_forwarded_for` | X-Forwarded-For 链 |
|
||||
| `$proxy_host` | proxy_pass 主机 |
|
||||
| `$proxy_port` | proxy_pass 端口 |
|
||||
|
||||
---
|
||||
|
||||
## 5. FastCGI 模块变量
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$fastcgi_path_info` | PATH_INFO |
|
||||
| `$fastcgi_script_name` | SCRIPT_NAME |
|
||||
|
||||
---
|
||||
|
||||
## 6. Stream 模块变量 (ngx_stream_core_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$binary_remote_addr` | 二进制 IP |
|
||||
| `$connection` | 连接序号 |
|
||||
| `$remote_addr` | 客户端 IP |
|
||||
| `$remote_port` | 客户端端口 |
|
||||
| `$server_addr` | 服务器 IP |
|
||||
| `$server_port` | 服务器端口 |
|
||||
| `$status` | 状态码 |
|
||||
| `$time_iso8601` | ISO 时间 |
|
||||
| `$time_local` | 本地时间 |
|
||||
| `$upstream_addr` | 后端地址 |
|
||||
| `$upstream_bytes_sent` | 发送字节 |
|
||||
| `$upstream_bytes_received` | 接收字节 |
|
||||
| `$upstream_connect_time` | 连接时间 |
|
||||
|
||||
---
|
||||
|
||||
## 7. SSL Preread 模块变量 (ngx_stream_ssl_preread_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$ssl_preread_protocol` | SSL 协议版本 |
|
||||
| `$ssl_preread_server_name` | SNI 名称 |
|
||||
|
||||
---
|
||||
|
||||
## 8. Geo 模块变量 (ngx_http_geo_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| 自定义 | 根据 IP 映射的值 |
|
||||
|
||||
---
|
||||
|
||||
## 9. Map 模块变量 (ngx_http_map_module)
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| 自定义 | 映射规则生成的值 |
|
||||
|
||||
---
|
||||
|
||||
## 10. Limit Request 模块变量
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `$limit_req` | 限流延迟时间(毫秒) |
|
||||
|
||||
---
|
||||
|
||||
## 11. 常用组合示例
|
||||
|
||||
### 日志格式
|
||||
```nginx
|
||||
log_format main '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'rt=$request_time uct="$upstream_connect_time" '
|
||||
'uht="$upstream_header_time" urt="$upstream_response_time"';
|
||||
```
|
||||
|
||||
### 性能监控
|
||||
```nginx
|
||||
log_format perf '$request_id $request_time $upstream_response_time '
|
||||
'$upstream_connect_time $upstream_header_time';
|
||||
```
|
||||
|
||||
### 安全日志
|
||||
```nginx
|
||||
log_format security '$remote_addr $request_method $request_uri '
|
||||
'$status $ssl_protocol $ssl_cipher';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*文档生成时间:2026-04-03*
|
||||
*基于 nginx 1.24+ 版本*
|
||||
@ -30,6 +30,7 @@
|
||||
| 22 | [第三方扩展模块](./22-nginx-third-party-modules.md) | NJS/Lua/Brotli/Cache Purge/Headers More/RTMP/Sticky 模块 |
|
||||
| 23 | [特殊功能模块](./23-nginx-special-modules.md) | WebDAV/图像过滤/FLV/MP4/HLS 流媒体/XSLT 转换 |
|
||||
| 24 | [核心与事件模块](./24-nginx-core-events.md) | worker_processes/events/epoll/kqueue/连接数计算 |
|
||||
| 25 | [内置变量速查表](./25-nginx-variables-reference.md) | HTTP/Stream/SSL/Upstream 变量完整列表(150+个) |
|
||||
|
||||
---
|
||||
|
||||
@ -63,6 +64,9 @@
|
||||
### 扩展与第三方
|
||||
- [第三方扩展模块](./22-nginx-third-party-modules.md) - NJS, Lua, Brotli, RTMP 等
|
||||
|
||||
### 参考手册
|
||||
- [内置变量速查表](./25-nginx-variables-reference.md) - 150+ 个变量完整列表
|
||||
|
||||
## 快速参考
|
||||
|
||||
### 核心配置结构
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
/ultrawork 深度分析 https://nginx.org/en/docs/ nginx 的功能,@docs/ 目录下已经有一些分析过的文档了,看看有没有能完善的
|
||||
|
||||
/ultrawork 深度分析下当前的配置文件,是否有配置文件描述了,但代码未实现的功能
|
||||
/ultrawork 深度分析下当前的配置文件,是否有配置文件描述了,但代码未实现的功能。同时也分析下有没有代码实现了,但是配置文件缺失的地方
|
||||
|
||||
## 单元测试
|
||||
|
||||
|
||||
11
go.mod
11
go.mod
@ -3,19 +3,24 @@ module rua.plus/lolly
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.2.0
|
||||
github.com/fasthttp/router v1.5.4
|
||||
github.com/klauspost/compress v1.18.2
|
||||
github.com/quic-go/quic-go v0.59.0
|
||||
github.com/rs/zerolog v1.35.0
|
||||
github.com/valyala/fasthttp v1.69.0
|
||||
golang.org/x/crypto v0.49.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.51.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
)
|
||||
|
||||
28
go.sum
28
go.sum
@ -1,31 +1,53 @@
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fasthttp/router v1.5.4 h1:oxdThbBwQgsDIYZ3wR1IavsNl6ZS9WdjKukeMikOnC8=
|
||||
github.com/fasthttp/router v1.5.4/go.mod h1:3/hysWq6cky7dTfzaaEPZGdptwjwx0qzTgFCKEWRjgc=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=
|
||||
github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
||||
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"time"
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/http3"
|
||||
"rua.plus/lolly/internal/logging"
|
||||
"rua.plus/lolly/internal/server"
|
||||
"rua.plus/lolly/internal/stream"
|
||||
@ -55,7 +56,7 @@ var (
|
||||
|
||||
// App 应用程序结构。
|
||||
//
|
||||
// 管理服务器的完整生命周期,包括 HTTP 服务器、Stream 服务器
|
||||
// 管理服务器的完整生命周期,包括 HTTP 服务器、HTTP/3 服务器、Stream 服务器
|
||||
// 和热升级管理器。
|
||||
type App struct {
|
||||
// cfgPath 配置文件路径
|
||||
@ -67,6 +68,9 @@ type App struct {
|
||||
// srv HTTP 服务器实例
|
||||
srv *server.Server
|
||||
|
||||
// http3Srv HTTP/3 服务器实例(可选)
|
||||
http3Srv *http3.Server
|
||||
|
||||
// streamSrv Stream 服务器实例(可选)
|
||||
streamSrv *stream.Server
|
||||
|
||||
@ -221,6 +225,26 @@ func (a *App) Run() int {
|
||||
}()
|
||||
}
|
||||
|
||||
// 创建并启动 HTTP/3 服务器(如果启用)
|
||||
if a.cfg.HTTP3.Enabled && a.cfg.Server.SSL.Cert != "" {
|
||||
tlsConfig, err := a.srv.GetTLSConfig()
|
||||
if err != nil {
|
||||
a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/3")
|
||||
} else {
|
||||
a.http3Srv, err = http3.NewServer(&a.cfg.HTTP3, a.srv.GetHandler(), tlsConfig)
|
||||
if err != nil {
|
||||
a.logger.Error().Err(err).Msg("创建 HTTP/3 服务器失败")
|
||||
} else {
|
||||
go func() {
|
||||
a.logger.LogStartup("HTTP/3 服务器启动中", map[string]string{"listen": a.cfg.HTTP3.Listen})
|
||||
if err := a.http3Srv.Start(); err != nil {
|
||||
a.logger.Error().Err(err).Msg("HTTP/3 服务器启动失败")
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建升级管理器
|
||||
a.upgradeMgr = server.NewUpgradeManager(a.srv)
|
||||
if a.pidFile != "" {
|
||||
@ -276,12 +300,14 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
||||
case syscall.SIGQUIT:
|
||||
// 优雅停止:等待请求完成
|
||||
a.logger.LogSignal("SIGQUIT", fmt.Sprintf("优雅停止(等待 %v)", shutdownTimeout))
|
||||
a.shutdownHTTP3()
|
||||
a.srv.GracefulStop(shutdownTimeout)
|
||||
return false
|
||||
|
||||
case syscall.SIGTERM, syscall.SIGINT:
|
||||
// 快速停止
|
||||
a.logger.LogSignal(sigName(sig.(syscall.Signal)), "停止服务器")
|
||||
a.shutdownHTTP3()
|
||||
a.srv.Stop()
|
||||
return false
|
||||
|
||||
@ -309,6 +335,15 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// shutdownHTTP3 关闭 HTTP/3 服务器。
|
||||
func (a *App) shutdownHTTP3() {
|
||||
if a.http3Srv != nil {
|
||||
if err := a.http3Srv.Stop(); err != nil {
|
||||
a.logger.Error().Err(err).Msg("HTTP/3 服务器关闭失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reloadConfig 重载配置。
|
||||
func (a *App) reloadConfig() {
|
||||
newCfg, err := config.Load(a.cfgPath)
|
||||
@ -362,6 +397,7 @@ func (a *App) gracefulUpgrade() {
|
||||
a.logger.LogStartup("热升级已启动,新进程正在接管", nil)
|
||||
|
||||
// 当前进程优雅停止
|
||||
a.shutdownHTTP3()
|
||||
a.srv.GracefulStop(shutdownTimeout)
|
||||
}
|
||||
|
||||
|
||||
@ -31,11 +31,21 @@ type Config struct {
|
||||
Server ServerConfig `yaml:"server"` // 单服务器模式配置
|
||||
Servers []ServerConfig `yaml:"servers"` // 多虚拟主机模式配置
|
||||
Stream []StreamConfig `yaml:"stream"` // TCP/UDP Stream 代理配置
|
||||
HTTP3 HTTP3Config `yaml:"http3"` // HTTP/3 (QUIC) 配置
|
||||
Logging LoggingConfig `yaml:"logging"` // 日志配置
|
||||
Performance PerformanceConfig `yaml:"performance"` // 性能配置
|
||||
Monitoring MonitoringConfig `yaml:"monitoring"` // 监控配置
|
||||
}
|
||||
|
||||
// HTTP3Config HTTP/3 (QUIC) 配置。
|
||||
type HTTP3Config struct {
|
||||
Enabled bool `yaml:"enabled"` // 是否启用 HTTP/3
|
||||
Listen string `yaml:"listen"` // UDP 监听地址,如 ":443"
|
||||
MaxStreams int `yaml:"max_streams"` // 最大并发流
|
||||
IdleTimeout time.Duration `yaml:"idle_timeout"` // 空闲超时
|
||||
Enable0RTT bool `yaml:"enable_0rtt"` // 启用 0-RTT
|
||||
}
|
||||
|
||||
// ServerConfig 服务器配置,包含监听地址、静态文件、代理、SSL 等设置。
|
||||
type ServerConfig struct {
|
||||
Listen string `yaml:"listen"` // 监听地址,如 ":8080"
|
||||
@ -62,13 +72,15 @@ type StaticConfig struct {
|
||||
|
||||
// ProxyConfig 反向代理配置,支持负载均衡和健康检查。
|
||||
type ProxyConfig struct {
|
||||
Path string `yaml:"path"` // 匹配路径前缀
|
||||
Targets []ProxyTarget `yaml:"targets"` // 后端目标列表
|
||||
LoadBalance string `yaml:"load_balance"` // 负载均衡算法:round_robin, weighted_round_robin, least_conn, ip_hash
|
||||
HealthCheck HealthCheckConfig `yaml:"health_check"` // 健康检查配置
|
||||
Timeout ProxyTimeout `yaml:"timeout"` // 超时配置
|
||||
Headers ProxyHeaders `yaml:"headers"` // 请求/响应头修改
|
||||
Cache ProxyCacheConfig `yaml:"cache"` // 代理缓存配置
|
||||
Path string `yaml:"path"` // 匹配路径前缀
|
||||
Targets []ProxyTarget `yaml:"targets"` // 后端目标列表
|
||||
LoadBalance string `yaml:"load_balance"` // 负载均衡算法:round_robin, weighted_round_robin, least_conn, ip_hash, consistent_hash
|
||||
HashKey string `yaml:"hash_key"` // 一致性哈希键:ip, uri, header:X-Name
|
||||
VirtualNodes int `yaml:"virtual_nodes"` // 一致性哈希虚拟节点数,默认 150
|
||||
HealthCheck HealthCheckConfig `yaml:"health_check"` // 健康检查配置
|
||||
Timeout ProxyTimeout `yaml:"timeout"` // 超时配置
|
||||
Headers ProxyHeaders `yaml:"headers"` // 请求/响应头修改
|
||||
Cache ProxyCacheConfig `yaml:"cache"` // 代理缓存配置
|
||||
}
|
||||
|
||||
// ProxyTarget 后端目标配置。
|
||||
@ -141,10 +153,13 @@ type AccessConfig struct {
|
||||
|
||||
// RateLimitConfig 速率限制配置。
|
||||
type RateLimitConfig struct {
|
||||
RequestRate int `yaml:"request_rate"` // 每秒请求数限制
|
||||
Burst int `yaml:"burst"` // 突发流量上限
|
||||
ConnLimit int `yaml:"conn_limit"` // 连接数限制
|
||||
Key string `yaml:"key"` // 限流 key 来源:ip, header
|
||||
RequestRate int `yaml:"request_rate"` // 每秒请求数限制
|
||||
Burst int `yaml:"burst"` // 突发流量上限
|
||||
ConnLimit int `yaml:"conn_limit"` // 连接数限制
|
||||
Key string `yaml:"key"` // 限流 key 来源:ip, header
|
||||
Algorithm string `yaml:"algorithm"` // 限流算法:token_bucket, sliding_window
|
||||
SlidingWindowMode string `yaml:"sliding_window_mode"` // 滑动窗口模式:approximate, precise
|
||||
SlidingWindow int `yaml:"sliding_window"` // 滑动窗口大小(秒)
|
||||
}
|
||||
|
||||
// AuthConfig 认证配置。
|
||||
@ -181,10 +196,12 @@ type RewriteRule struct {
|
||||
|
||||
// CompressionConfig 响应压缩配置。
|
||||
type CompressionConfig struct {
|
||||
Type string `yaml:"type"` // 压缩类型:gzip, brotli, both
|
||||
Level int `yaml:"level"` // 压缩级别:1-9
|
||||
MinSize int `yaml:"min_size"` // 最小压缩大小(字节)
|
||||
Types []string `yaml:"types"` // 可压缩的 MIME 类型
|
||||
Type string `yaml:"type"` // 压缩类型:gzip, brotli, both
|
||||
Level int `yaml:"level"` // 压缩级别:1-9
|
||||
MinSize int `yaml:"min_size"` // 最小压缩大小(字节)
|
||||
Types []string `yaml:"types"` // 可压缩的 MIME 类型
|
||||
GzipStatic bool `yaml:"gzip_static"` // 启用预压缩文件支持
|
||||
GzipStaticExtensions []string `yaml:"gzip_static_extensions"` // 预压缩文件扩展名
|
||||
}
|
||||
|
||||
// LoggingConfig 日志配置。
|
||||
|
||||
@ -355,7 +355,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
|
||||
buf.WriteString(fmt.Sprintf(" format: \"%s\" # 全局日志格式(有效值: text, json),控制启动/停止日志格式\n", cfg.Logging.Format))
|
||||
buf.WriteString(" access:\n")
|
||||
buf.WriteString(" path: \"\" # 日志文件路径(空表示输出到 stdout)\n")
|
||||
buf.WriteString(fmt.Sprintf(" format: \"%s\" # 访问日志格式,近似 nginx combined\n", cfg.Logging.Access.Format))
|
||||
buf.WriteString(fmt.Sprintf(" format: '%s' # 访问日志格式,近似 nginx combined\n", cfg.Logging.Access.Format))
|
||||
buf.WriteString(" # 支持变量: $remote_addr, $remote_user, $request, $status, $body_bytes_sent, $request_time, $http_referer, $http_user_agent, $time\n")
|
||||
buf.WriteString(" # 特殊值 \"json\" 输出结构化 JSON\n")
|
||||
buf.WriteString(" error:\n")
|
||||
|
||||
251
internal/http3/adapter.go
Normal file
251
internal/http3/adapter.go
Normal file
@ -0,0 +1,251 @@
|
||||
// Package http3 提供 HTTP/3 请求适配层。
|
||||
//
|
||||
// 该文件实现 fasthttp.RequestHandler 与 http.Handler 之间的适配,
|
||||
// 使 HTTP/3 服务器能够复用现有的 fasthttp 处理器。
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 将 quic-go 的 http.Handler 接口适配为 fasthttp.RequestHandler。
|
||||
//
|
||||
// 作者:xfy
|
||||
package http3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Adapter 将 fasthttp.RequestHandler 适配为 http.Handler。
|
||||
//
|
||||
// 由于 quic-go 使用标准库的 http.Handler 接口,
|
||||
// 而 lolly 使用 fasthttp,需要通过适配层进行转换。
|
||||
type Adapter struct {
|
||||
// ctxPool 用于复用 RequestCtx 对象
|
||||
ctxPool sync.Pool
|
||||
}
|
||||
|
||||
// NewAdapter 创建新的适配器。
|
||||
func NewAdapter() *Adapter {
|
||||
return &Adapter{
|
||||
ctxPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &fasthttp.RequestCtx{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap 包装 fasthttp handler 为 http.Handler。
|
||||
//
|
||||
// 将 http.Request 转换为 fasthttp.RequestCtx,
|
||||
// 调用 fasthttp handler,然后将响应写回 http.ResponseWriter。
|
||||
//
|
||||
// 参数:
|
||||
// - handler: fasthttp 请求处理器
|
||||
//
|
||||
// 返回值:
|
||||
// - http.Handler: 标准库兼容的 HTTP 处理器
|
||||
func (a *Adapter) Wrap(handler fasthttp.RequestHandler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 从池中获取 RequestCtx
|
||||
ctx := a.ctxPool.Get().(*fasthttp.RequestCtx)
|
||||
defer a.ctxPool.Put(ctx)
|
||||
|
||||
// 初始化 ctx(fasthttp 的 RequestCtx 需要 Init 方法)
|
||||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||||
|
||||
// 转换请求
|
||||
a.convertRequest(r, ctx)
|
||||
|
||||
// 设置 ResponseWriter 用于后续写入
|
||||
ctx.SetUserValue("http3_response_writer", w)
|
||||
|
||||
// 调用 fasthttp handler
|
||||
handler(ctx)
|
||||
|
||||
// 转换响应
|
||||
a.convertResponse(ctx, w)
|
||||
})
|
||||
}
|
||||
|
||||
// convertRequest 将 net/http.Request 转换为 fasthttp.RequestCtx。
|
||||
//
|
||||
// 参数:
|
||||
// - r: 标准库 HTTP 请求
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
func (a *Adapter) convertRequest(r *http.Request, ctx *fasthttp.RequestCtx) {
|
||||
// 设置方法
|
||||
ctx.Request.Header.SetMethod(r.Method)
|
||||
|
||||
// 设置 URI
|
||||
uri := r.URL.Path
|
||||
if r.URL.RawQuery != "" {
|
||||
uri += "?" + r.URL.RawQuery
|
||||
}
|
||||
ctx.Request.SetRequestURI(uri)
|
||||
|
||||
// 设置 Host 头
|
||||
ctx.Request.Header.SetHost(r.Host)
|
||||
|
||||
// 复制头部
|
||||
for k, v := range r.Header {
|
||||
for _, vv := range v {
|
||||
ctx.Request.Header.Add(k, vv)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置请求体
|
||||
if r.Body != nil {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err == nil {
|
||||
ctx.Request.SetBody(body)
|
||||
}
|
||||
r.Body.Close()
|
||||
}
|
||||
|
||||
// 设置远程地址
|
||||
if r.RemoteAddr != "" {
|
||||
if addr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr); err == nil {
|
||||
ctx.SetRemoteAddr(addr)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置协议版本
|
||||
ctx.Request.Header.SetProtocol("HTTP/3")
|
||||
}
|
||||
|
||||
// convertResponse 将 fasthttp.RequestCtx 响应写入 http.ResponseWriter。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
// - w: 标准库 ResponseWriter
|
||||
func (a *Adapter) convertResponse(ctx *fasthttp.RequestCtx, w http.ResponseWriter) {
|
||||
// 设置状态码
|
||||
statusCode := ctx.Response.StatusCode()
|
||||
if statusCode == 0 {
|
||||
statusCode = 200
|
||||
}
|
||||
|
||||
// 复制响应头
|
||||
ctx.Response.Header.VisitAll(func(k, v []byte) {
|
||||
w.Header().Add(string(k), string(v))
|
||||
})
|
||||
|
||||
// 写入状态码
|
||||
w.WriteHeader(statusCode)
|
||||
|
||||
// 写入响应体
|
||||
body := ctx.Response.Body()
|
||||
if len(body) > 0 {
|
||||
w.Write(body)
|
||||
}
|
||||
}
|
||||
|
||||
// WrapHandler 包装特定的 fasthttp handler。
|
||||
//
|
||||
// 返回一个可以直接用于 http3.Server 的 http.Handler。
|
||||
//
|
||||
// 参数:
|
||||
// - handler: fasthttp 请求处理器
|
||||
//
|
||||
// 返回值:
|
||||
// - http.Handler: 标准库兼容的处理器
|
||||
func (a *Adapter) WrapHandler(handler fasthttp.RequestHandler) http.Handler {
|
||||
return a.Wrap(handler)
|
||||
}
|
||||
|
||||
// FastHTTPHandler 从 http.Handler 提取并调用 fasthttp 处理器。
|
||||
//
|
||||
// 这是一个便捷方法,用于在需要时反向转换。
|
||||
//
|
||||
// 参数:
|
||||
// - h: 标准库 http.Handler
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
func FastHTTPHandler(h http.Handler, ctx *fasthttp.RequestCtx) {
|
||||
// 创建虚拟 ResponseWriter
|
||||
rw := &fastHTTPResponseWriter{
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
// 转换请求
|
||||
r := convertToHTTPRequest(ctx)
|
||||
|
||||
// 调用标准库 handler
|
||||
h.ServeHTTP(rw, r)
|
||||
}
|
||||
|
||||
// fastHTTPResponseWriter 实现 http.ResponseWriter 接口。
|
||||
type fastHTTPResponseWriter struct {
|
||||
ctx *fasthttp.RequestCtx
|
||||
status int
|
||||
header http.Header
|
||||
written bool
|
||||
}
|
||||
|
||||
func (w *fastHTTPResponseWriter) Header() http.Header {
|
||||
if w.header == nil {
|
||||
w.header = make(http.Header)
|
||||
}
|
||||
return w.header
|
||||
}
|
||||
|
||||
func (w *fastHTTPResponseWriter) Write(data []byte) (int, error) {
|
||||
if !w.written {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return w.ctx.Write(data)
|
||||
}
|
||||
|
||||
func (w *fastHTTPResponseWriter) WriteHeader(statusCode int) {
|
||||
if w.written {
|
||||
return
|
||||
}
|
||||
w.written = true
|
||||
w.status = statusCode
|
||||
|
||||
// 复制头部
|
||||
for k, v := range w.header {
|
||||
for _, vv := range v {
|
||||
w.ctx.Response.Header.Add(k, vv)
|
||||
}
|
||||
}
|
||||
|
||||
w.ctx.SetStatusCode(statusCode)
|
||||
}
|
||||
|
||||
// convertToHTTPRequest 将 fasthttp.RequestCtx 转换为 http.Request。
|
||||
func convertToHTTPRequest(ctx *fasthttp.RequestCtx) *http.Request {
|
||||
r := &http.Request{
|
||||
Method: string(ctx.Method()),
|
||||
Host: string(ctx.Host()),
|
||||
RemoteAddr: ctx.RemoteAddr().String(),
|
||||
Proto: "HTTP/3",
|
||||
ProtoMajor: 3,
|
||||
ProtoMinor: 0,
|
||||
}
|
||||
|
||||
// 构建 URL
|
||||
r.URL = &url.URL{
|
||||
Path: string(ctx.Path()),
|
||||
RawQuery: string(ctx.URI().QueryString()),
|
||||
}
|
||||
|
||||
// 复制头部
|
||||
r.Header = make(http.Header)
|
||||
ctx.Request.Header.VisitAll(func(k, v []byte) {
|
||||
r.Header.Add(string(k), string(v))
|
||||
})
|
||||
|
||||
// 设置请求体
|
||||
if len(ctx.PostBody()) > 0 {
|
||||
r.Body = io.NopCloser(bytes.NewReader(ctx.PostBody()))
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
288
internal/http3/server.go
Normal file
288
internal/http3/server.go
Normal file
@ -0,0 +1,288 @@
|
||||
// Package http3 提供 HTTP/3 (QUIC) 协议支持。
|
||||
//
|
||||
// 该文件包含 HTTP/3 服务器的核心实现,包括:
|
||||
// - 基于 quic-go 的 HTTP/3 服务器
|
||||
// - 支持 0-RTT 连接
|
||||
// - 优雅关闭支持
|
||||
// - 与现有 fasthttp handler 的集成
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于提供 HTTP/3 协议支持,提升网站性能和用户体验。
|
||||
//
|
||||
// 作者:xfy
|
||||
package http3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
quichttp3 "github.com/quic-go/quic-go/http3"
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/config"
|
||||
"rua.plus/lolly/internal/logging"
|
||||
)
|
||||
|
||||
// Server HTTP/3 服务器。
|
||||
//
|
||||
// 使用 QUIC 协议提供 HTTP/3 服务,与现有的 TCP 服务器并行运行。
|
||||
type Server struct {
|
||||
// config HTTP/3 配置
|
||||
config *config.HTTP3Config
|
||||
|
||||
// http3Server HTTP/3 服务器实例
|
||||
http3Server *quichttp3.Server
|
||||
|
||||
// handler fasthttp 请求处理器
|
||||
handler fasthttp.RequestHandler
|
||||
|
||||
// adapter 请求适配器
|
||||
adapter *Adapter
|
||||
|
||||
// tlsConfig TLS 配置
|
||||
tlsConfig *tls.Config
|
||||
|
||||
// listener QUIC 监听器
|
||||
listener *quic.EarlyListener
|
||||
|
||||
// running 服务器运行状态
|
||||
running bool
|
||||
|
||||
// mu 读写锁
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewServer 创建 HTTP/3 服务器。
|
||||
//
|
||||
// 参数:
|
||||
// - cfg: HTTP/3 配置
|
||||
// - handler: fasthttp 请求处理器
|
||||
// - tlsConfig: TLS 配置(必须)
|
||||
//
|
||||
// 返回值:
|
||||
// - *Server: HTTP/3 服务器实例
|
||||
// - error: 配置无效时返回错误
|
||||
func NewServer(cfg *config.HTTP3Config, handler fasthttp.RequestHandler, tlsConfig *tls.Config) (*Server, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("http3 config is nil")
|
||||
}
|
||||
|
||||
if handler == nil {
|
||||
return nil, fmt.Errorf("handler is nil")
|
||||
}
|
||||
|
||||
if tlsConfig == nil {
|
||||
return nil, fmt.Errorf("tls config is required for HTTP/3")
|
||||
}
|
||||
|
||||
adapter := NewAdapter()
|
||||
|
||||
return &Server{
|
||||
config: cfg,
|
||||
handler: handler,
|
||||
adapter: adapter,
|
||||
tlsConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start 启动 HTTP/3 服务器。
|
||||
//
|
||||
// 创建 UDP 监听器并开始接受 QUIC 连接。
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 启动失败时返回错误
|
||||
func (s *Server) Start() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.running {
|
||||
return fmt.Errorf("server already running")
|
||||
}
|
||||
|
||||
// 创建 QUIC 配置
|
||||
quicConfig := &quic.Config{
|
||||
MaxIncomingStreams: int64(s.config.MaxStreams),
|
||||
MaxIdleTimeout: s.config.IdleTimeout,
|
||||
KeepAlivePeriod: 30 * time.Second,
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if quicConfig.MaxIncomingStreams == 0 {
|
||||
quicConfig.MaxIncomingStreams = 100
|
||||
}
|
||||
if quicConfig.MaxIdleTimeout == 0 {
|
||||
quicConfig.MaxIdleTimeout = 30 * time.Second
|
||||
}
|
||||
|
||||
// 创建 UDP 监听器
|
||||
listenAddr := s.config.Listen
|
||||
if listenAddr == "" {
|
||||
listenAddr = ":443"
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to resolve UDP address: %w", err)
|
||||
}
|
||||
|
||||
udpConn, err := net.ListenUDP("udp", udpAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen UDP: %w", err)
|
||||
}
|
||||
|
||||
// 创建 QUIC 监听器
|
||||
s.listener, err = quic.ListenEarly(udpConn, s.tlsConfig, quicConfig)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
return fmt.Errorf("failed to listen QUIC: %w", err)
|
||||
}
|
||||
|
||||
// 创建 HTTP/3 服务器
|
||||
s.http3Server = &quichttp3.Server{
|
||||
Handler: s.adapter.Wrap(s.handler),
|
||||
}
|
||||
|
||||
s.running = true
|
||||
|
||||
logging.Info().
|
||||
Str("listen", listenAddr).
|
||||
Bool("0rtt", s.config.Enable0RTT).
|
||||
Msg("HTTP/3 server started")
|
||||
|
||||
// 开始服务
|
||||
go func() {
|
||||
if err := s.http3Server.ServeListener(s.listener); err != nil {
|
||||
s.mu.RLock()
|
||||
running := s.running
|
||||
s.mu.RUnlock()
|
||||
|
||||
if running {
|
||||
logging.Error().Err(err).Msg("HTTP/3 server error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop 停止 HTTP/3 服务器。
|
||||
//
|
||||
// 优雅关闭服务器,等待现有连接完成。
|
||||
//
|
||||
// 返回值:
|
||||
// - error: 关闭失败时返回错误
|
||||
func (s *Server) Stop() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if !s.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.running = false
|
||||
|
||||
if s.http3Server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := s.http3Server.Close(); err != nil {
|
||||
logging.Error().Err(err).Msg("HTTP/3 server close error")
|
||||
}
|
||||
|
||||
// 等待服务完全停止
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
logging.Info().Msg("HTTP/3 server stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GracefulStop 优雅停止服务器。
|
||||
//
|
||||
// 等待指定时间让现有连接完成。
|
||||
//
|
||||
// 参数:
|
||||
// - timeout: 等待超时时间
|
||||
func (s *Server) GracefulStop(timeout time.Duration) error {
|
||||
s.mu.Lock()
|
||||
s.running = false
|
||||
s.mu.Unlock()
|
||||
|
||||
if s.http3Server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
s.http3Server.Close()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
logging.Info().Msg("HTTP/3 server graceful stop completed")
|
||||
case <-ctx.Done():
|
||||
logging.Warn().Msg("HTTP/3 server graceful stop timeout")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRunning 检查服务器是否正在运行。
|
||||
func (s *Server) IsRunning() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.running
|
||||
}
|
||||
|
||||
// GetAltSvcHeader 返回 Alt-Svc 响应头值。
|
||||
//
|
||||
// 用于告知客户端可以使用 HTTP/3。
|
||||
//
|
||||
// 返回值:
|
||||
// - string: Alt-Svc 头值,如 `h3=":443"; ma=86400`
|
||||
func (s *Server) GetAltSvcHeader() string {
|
||||
if s.config == nil || !s.config.Enabled {
|
||||
return ""
|
||||
}
|
||||
|
||||
listen := s.config.Listen
|
||||
if listen == "" {
|
||||
listen = ":443"
|
||||
}
|
||||
|
||||
// 移除前导冒号,保留端口
|
||||
port := listen
|
||||
if port[0] == ':' {
|
||||
port = port[1:]
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`h3=":%s"; ma=86400`, port)
|
||||
}
|
||||
|
||||
// Stats 返回服务器统计信息。
|
||||
type Stats struct {
|
||||
Running bool // 是否运行中
|
||||
Listen string // 监听地址
|
||||
Enable0RTT bool // 是否启用 0-RTT
|
||||
MaxStreams int // 最大并发流
|
||||
}
|
||||
|
||||
// GetStats 返回服务器统计信息。
|
||||
func (s *Server) GetStats() Stats {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return Stats{
|
||||
Running: s.running,
|
||||
Listen: s.config.Listen,
|
||||
Enable0RTT: s.config.Enable0RTT,
|
||||
MaxStreams: s.config.MaxStreams,
|
||||
}
|
||||
}
|
||||
184
internal/loadbalance/consistent_hash.go
Normal file
184
internal/loadbalance/consistent_hash.go
Normal file
@ -0,0 +1,184 @@
|
||||
// Package loadbalance 提供一致性哈希负载均衡算法实现。
|
||||
//
|
||||
// 该文件实现基于虚拟节点的一致性哈希算法,适用于缓存代理场景。
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于将相同键的请求始终路由到同一后端服务器,提高缓存命中率。
|
||||
//
|
||||
// 算法特点:
|
||||
// - 使用虚拟节点解决数据倾斜问题
|
||||
// - 支持 FNV-64a 哈希算法
|
||||
// - 支持多种哈希键来源(IP、URI、Header)
|
||||
//
|
||||
// 作者:xfy
|
||||
package loadbalance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ConsistentHash 一致性哈希负载均衡器。
|
||||
//
|
||||
// 使用虚拟节点将请求均匀分布到各个目标,同时保证相同键的请求
|
||||
// 始终路由到同一目标。
|
||||
type ConsistentHash struct {
|
||||
// virtualNodes 每个目标的虚拟节点数,默认 150
|
||||
virtualNodes int
|
||||
|
||||
// circle 哈希环,key 为哈希值,value 为目标
|
||||
circle map[uint64]*Target
|
||||
|
||||
// sortedHashes 排序后的哈希值列表,用于二分查找
|
||||
sortedHashes []uint64
|
||||
|
||||
// hashKey 哈希键来源:ip, uri, header:xxx
|
||||
hashKey string
|
||||
|
||||
// mu 读写锁
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewConsistentHash 创建一致性哈希负载均衡器。
|
||||
//
|
||||
// 参数:
|
||||
// - virtualNodes: 每个目标的虚拟节点数,默认 150
|
||||
// - hashKey: 哈希键来源,支持 ip、uri、header:X-Name
|
||||
func NewConsistentHash(virtualNodes int, hashKey string) *ConsistentHash {
|
||||
if virtualNodes <= 0 {
|
||||
virtualNodes = 150
|
||||
}
|
||||
return &ConsistentHash{
|
||||
virtualNodes: virtualNodes,
|
||||
circle: make(map[uint64]*Target),
|
||||
hashKey: hashKey,
|
||||
}
|
||||
}
|
||||
|
||||
// Select 根据默认键选择目标。
|
||||
//
|
||||
// 由于一致性哈希需要具体键值,此方法返回 nil。
|
||||
// 请使用 SelectByKey 方法。
|
||||
func (c *ConsistentHash) Select(targets []*Target) *Target {
|
||||
return c.SelectByKey(targets, "")
|
||||
}
|
||||
|
||||
// SelectByKey 根据指定键选择目标。
|
||||
//
|
||||
// 参数:
|
||||
// - targets: 可用目标列表
|
||||
// - key: 哈希键值(如客户端 IP、URI 等)
|
||||
//
|
||||
// 返回值:
|
||||
// - *Target: 选中的目标,如果没有健康目标则返回 nil
|
||||
func (c *ConsistentHash) SelectByKey(targets []*Target, key string) *Target {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
// 如果环为空,重建哈希环
|
||||
if len(c.circle) == 0 {
|
||||
c.mu.RUnlock()
|
||||
c.rebuildCircle(targets)
|
||||
c.mu.RLock()
|
||||
}
|
||||
|
||||
if len(c.sortedHashes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 计算键的哈希值
|
||||
hash := c.hashKeyString(key)
|
||||
|
||||
// 二分查找最近的节点
|
||||
idx := sort.Search(len(c.sortedHashes), func(i int) bool {
|
||||
return c.sortedHashes[i] >= hash
|
||||
})
|
||||
|
||||
// 环形回绕
|
||||
if idx >= len(c.sortedHashes) {
|
||||
idx = 0
|
||||
}
|
||||
|
||||
return c.circle[c.sortedHashes[idx]]
|
||||
}
|
||||
|
||||
// Rebuild 重建哈希环。
|
||||
//
|
||||
// 当目标列表发生变化时应调用此方法。
|
||||
//
|
||||
// 参数:
|
||||
// - targets: 新的目标列表
|
||||
func (c *ConsistentHash) Rebuild(targets []*Target) {
|
||||
c.rebuildCircle(targets)
|
||||
}
|
||||
|
||||
// rebuildCircle 重建哈希环(内部方法,需要持有锁)。
|
||||
func (c *ConsistentHash) rebuildCircle(targets []*Target) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
// 清空现有环
|
||||
c.circle = make(map[uint64]*Target)
|
||||
c.sortedHashes = make([]uint64, 0)
|
||||
|
||||
// 为每个目标添加虚拟节点
|
||||
for _, target := range targets {
|
||||
if !target.Healthy.Load() {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i < c.virtualNodes; i++ {
|
||||
key := fmt.Sprintf("%s#%d", target.URL, i)
|
||||
hash := c.hashKeyString(key)
|
||||
c.circle[hash] = target
|
||||
c.sortedHashes = append(c.sortedHashes, hash)
|
||||
}
|
||||
}
|
||||
|
||||
// 排序哈希值
|
||||
sort.Slice(c.sortedHashes, func(i, j int) bool {
|
||||
return c.sortedHashes[i] < c.sortedHashes[j]
|
||||
})
|
||||
}
|
||||
|
||||
// hashKeyString 计算字符串的哈希值(使用 FNV-64a)。
|
||||
func (c *ConsistentHash) hashKeyString(key string) uint64 {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(key))
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
// GetHashKey 返回哈希键配置。
|
||||
func (c *ConsistentHash) GetHashKey() string {
|
||||
return c.hashKey
|
||||
}
|
||||
|
||||
// GetVirtualNodes 返回虚拟节点数。
|
||||
func (c *ConsistentHash) GetVirtualNodes() int {
|
||||
return c.virtualNodes
|
||||
}
|
||||
|
||||
// Stats 返回一致性哈希统计信息。
|
||||
type ConsistentHashStats struct {
|
||||
VirtualNodes int // 虚拟节点数
|
||||
CircleSize int // 哈希环大小
|
||||
SortedHashes int // 排序哈希值数量
|
||||
}
|
||||
|
||||
// GetStats 返回统计信息。
|
||||
func (c *ConsistentHash) GetStats() ConsistentHashStats {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return ConsistentHashStats{
|
||||
VirtualNodes: c.virtualNodes,
|
||||
CircleSize: len(c.circle),
|
||||
SortedHashes: len(c.sortedHashes),
|
||||
}
|
||||
}
|
||||
|
||||
// 验证接口实现
|
||||
var _ Balancer = (*ConsistentHash)(nil)
|
||||
@ -175,6 +175,21 @@ func Error() *zerolog.Event {
|
||||
return log.Error()
|
||||
}
|
||||
|
||||
// Info 返回 Info 级别日志记录器(全局实例)。
|
||||
func Info() *zerolog.Event {
|
||||
return log.Info()
|
||||
}
|
||||
|
||||
// Warn 返回 Warn 级别日志记录器(全局实例)。
|
||||
func Warn() *zerolog.Event {
|
||||
return log.Warn()
|
||||
}
|
||||
|
||||
// Debug 返回 Debug 级别日志记录器(全局实例)。
|
||||
func Debug() *zerolog.Event {
|
||||
return log.Debug()
|
||||
}
|
||||
|
||||
// parseLevel 解析日志级别。
|
||||
func parseLevel(level string) zerolog.Level {
|
||||
switch strings.ToLower(level) {
|
||||
|
||||
147
internal/middleware/compression/gzip_static.go
Normal file
147
internal/middleware/compression/gzip_static.go
Normal file
@ -0,0 +1,147 @@
|
||||
// Package compression 提供 gzip_static 预压缩文件支持。
|
||||
//
|
||||
// 该文件实现预压缩文件的检测和发送,避免实时压缩开销。
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于发送预压缩的 .gz 文件,减少服务器 CPU 开销,提升响应速度。
|
||||
//
|
||||
// 使用场景:
|
||||
// - 静态资源预先压缩(如 CSS、JS、HTML 文件)
|
||||
// - 构建时生成 .gz 文件
|
||||
// - 运行时直接发送预压缩文件
|
||||
//
|
||||
// 作者:xfy
|
||||
package compression
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// GzipStatic 预压缩文件支持。
|
||||
//
|
||||
// 检查是否存在预压缩的 .gz 文件,如果存在则直接发送,
|
||||
// 避免实时压缩的 CPU 开销。
|
||||
type GzipStatic struct {
|
||||
// enabled 是否启用
|
||||
enabled bool
|
||||
|
||||
// root 静态文件根目录
|
||||
root string
|
||||
|
||||
// extensions 支持的扩展名
|
||||
extensions []string
|
||||
}
|
||||
|
||||
// NewGzipStatic 创建预压缩文件处理器。
|
||||
//
|
||||
// 参数:
|
||||
// - enabled: 是否启用预压缩支持
|
||||
// - root: 静态文件根目录
|
||||
// - extensions: 支持预压缩的文件扩展名,为空则使用默认值
|
||||
func NewGzipStatic(enabled bool, root string, extensions []string) *GzipStatic {
|
||||
if len(extensions) == 0 {
|
||||
extensions = []string{".html", ".css", ".js", ".json", ".xml", ".svg", ".txt"}
|
||||
}
|
||||
return &GzipStatic{
|
||||
enabled: enabled,
|
||||
root: root,
|
||||
extensions: extensions,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeFile 发送预压缩文件(如果存在)。
|
||||
//
|
||||
// 检查是否存在对应的 .gz 文件,如果存在且客户端支持 gzip,
|
||||
// 则发送预压缩文件。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
// - filePath: 请求的文件路径
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示已发送预压缩文件,false 表示未发送
|
||||
func (g *GzipStatic) ServeFile(ctx *fasthttp.RequestCtx, filePath string) bool {
|
||||
if !g.enabled {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查客户端是否支持 gzip
|
||||
acceptEncoding := ctx.Request.Header.Peek("Accept-Encoding")
|
||||
if !bytes.Contains(acceptEncoding, []byte("gzip")) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查文件扩展名
|
||||
if !g.matchExtension(filePath) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查预压缩文件是否存在
|
||||
gzPath := filePath + ".gz"
|
||||
fullGzPath := filepath.Join(g.root, gzPath)
|
||||
|
||||
// 安全检查:防止目录遍历
|
||||
if strings.Contains(gzPath, "..") {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(fullGzPath); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 发送预压缩文件
|
||||
ctx.Response.Header.Set("Content-Encoding", "gzip")
|
||||
ctx.Response.Header.Set("Vary", "Accept-Encoding")
|
||||
fasthttp.ServeFile(ctx, fullGzPath)
|
||||
return true
|
||||
}
|
||||
|
||||
// TryServeFile 尝试发送预压缩文件的静态方法。
|
||||
//
|
||||
// 用于在静态文件处理器中调用。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: FastHTTP 请求上下文
|
||||
// - root: 静态文件根目录
|
||||
// - filePath: 请求的文件路径
|
||||
// - extensions: 支持的扩展名
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示已发送预压缩文件
|
||||
func TryServeFile(ctx *fasthttp.RequestCtx, root, filePath string, extensions []string) bool {
|
||||
g := NewGzipStatic(true, root, extensions)
|
||||
return g.ServeFile(ctx, filePath)
|
||||
}
|
||||
|
||||
// matchExtension 检查文件扩展名是否匹配。
|
||||
func (g *GzipStatic) matchExtension(filePath string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(filePath))
|
||||
for _, e := range g.extensions {
|
||||
if strings.ToLower(e) == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Enabled 返回是否启用预压缩。
|
||||
func (g *GzipStatic) Enabled() bool {
|
||||
return g.enabled
|
||||
}
|
||||
|
||||
// Extensions 返回支持的扩展名列表。
|
||||
func (g *GzipStatic) Extensions() []string {
|
||||
return g.extensions
|
||||
}
|
||||
|
||||
// DefaultExtensions 返回默认支持的扩展名。
|
||||
func DefaultExtensions() []string {
|
||||
return []string{".html", ".css", ".js", ".json", ".xml", ".svg", ".txt"}
|
||||
}
|
||||
263
internal/middleware/security/sliding_window.go
Normal file
263
internal/middleware/security/sliding_window.go
Normal file
@ -0,0 +1,263 @@
|
||||
// Package security 提供滑动窗口限流算法实现。
|
||||
//
|
||||
// 该文件实现基于滑动窗口的请求限流,支持近似和精确两种模式。
|
||||
//
|
||||
// 主要用途:
|
||||
//
|
||||
// 用于更精确地控制请求速率,相比令牌桶算法提供更平滑的限流效果。
|
||||
//
|
||||
// 算法特点:
|
||||
// - 近似模式:O(1) 时间复杂度,内存占用低
|
||||
// - 精确模式:O(n) 时间复杂度,限流更精确
|
||||
//
|
||||
// 作者:xfy
|
||||
package security
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SlidingWindowLimiter 滑动窗口限流器。
|
||||
//
|
||||
// 使用滑动窗口算法限制请求速率,支持近似和精确两种模式。
|
||||
type SlidingWindowLimiter struct {
|
||||
// window 窗口大小
|
||||
window time.Duration
|
||||
|
||||
// limit 窗口内最大请求数
|
||||
limit int
|
||||
|
||||
// precise 是否使用精确模式
|
||||
precise bool
|
||||
|
||||
// counters 窗口计数器,key 为窗口起始时间
|
||||
counters map[string]*windowCounter
|
||||
|
||||
// mu 读写锁
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// windowCounter 窗口计数器。
|
||||
type windowCounter struct {
|
||||
count int64
|
||||
timestamps []time.Time // precise 模式下记录每个请求时间
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewSlidingWindowLimiter 创建滑动窗口限流器。
|
||||
//
|
||||
// 参数:
|
||||
// - window: 窗口大小(如 1s、1m)
|
||||
// - limit: 窗口内最大请求数
|
||||
// - precise: 是否使用精确模式
|
||||
func NewSlidingWindowLimiter(window time.Duration, limit int, precise bool) *SlidingWindowLimiter {
|
||||
return &SlidingWindowLimiter{
|
||||
window: window,
|
||||
limit: limit,
|
||||
precise: precise,
|
||||
counters: make(map[string]*windowCounter),
|
||||
}
|
||||
}
|
||||
|
||||
// Allow 检查是否允许请求。
|
||||
//
|
||||
// 参数:
|
||||
// - key: 限流键(如 IP 地址)
|
||||
//
|
||||
// 返回值:
|
||||
// - bool: true 表示允许请求,false 表示拒绝
|
||||
func (s *SlidingWindowLimiter) Allow(key string) bool {
|
||||
if s.precise {
|
||||
return s.allowPrecise(key)
|
||||
}
|
||||
return s.allowApproximate(key)
|
||||
}
|
||||
|
||||
// allowApproximate 近似滑动窗口(推荐,内存 O(1))。
|
||||
//
|
||||
// 使用两个固定窗口估算滑动窗口内的请求数,性能优于精确模式。
|
||||
func (s *SlidingWindowLimiter) allowApproximate(key string) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
windowNanos := s.window.Nanoseconds()
|
||||
_ = windowNanos // 用于近似计算
|
||||
|
||||
// 获取或创建当前窗口计数器
|
||||
current, ok := s.counters[key]
|
||||
if !ok {
|
||||
current = &windowCounter{}
|
||||
s.counters[key] = current
|
||||
}
|
||||
|
||||
current.mu.Lock()
|
||||
defer current.mu.Unlock()
|
||||
|
||||
// 检查是否需要重置窗口
|
||||
if current.count == 0 || current.timestamps == nil || len(current.timestamps) == 0 {
|
||||
// 首次请求或新窗口
|
||||
current.timestamps = []time.Time{now}
|
||||
} else {
|
||||
// 检查是否仍在当前窗口
|
||||
lastTime := current.timestamps[0]
|
||||
if now.Sub(lastTime) > s.window {
|
||||
// 新窗口,重置
|
||||
current.count = 0
|
||||
current.timestamps = []time.Time{}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取上一个窗口的计数(用于估算)
|
||||
// 简化实现:直接计算当前窗口内的请求数
|
||||
count := int64(len(current.timestamps))
|
||||
|
||||
// 计算滑动窗口内的请求数
|
||||
// 公式:当前窗口计数 × 1.0 + 上一窗口计数 × (1 - 当前窗口已过比例)
|
||||
elapsed := float64(now.UnixNano()%windowNanos) / float64(windowNanos)
|
||||
adjustedCount := float64(count) * (1.0 - elapsed)
|
||||
|
||||
if int(adjustedCount) >= s.limit {
|
||||
return false
|
||||
}
|
||||
|
||||
// 记录请求
|
||||
current.timestamps = append(current.timestamps, now)
|
||||
current.count = int64(len(current.timestamps))
|
||||
return true
|
||||
}
|
||||
|
||||
// allowPrecise 精确滑动窗口(内存 O(n),精确限流)。
|
||||
//
|
||||
// 记录每个请求的时间戳,精确计算滑动窗口内的请求数。
|
||||
func (s *SlidingWindowLimiter) allowPrecise(key string) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
windowStart := now.Add(-s.window)
|
||||
|
||||
// 获取或创建计数器
|
||||
counter, ok := s.counters[key]
|
||||
if !ok {
|
||||
counter = &windowCounter{
|
||||
timestamps: make([]time.Time, 0, s.limit),
|
||||
}
|
||||
s.counters[key] = counter
|
||||
}
|
||||
|
||||
counter.mu.Lock()
|
||||
defer counter.mu.Unlock()
|
||||
|
||||
// 清理过期的时间戳
|
||||
valid := make([]time.Time, 0, len(counter.timestamps))
|
||||
for _, t := range counter.timestamps {
|
||||
if t.After(windowStart) {
|
||||
valid = append(valid, t)
|
||||
}
|
||||
}
|
||||
counter.timestamps = valid
|
||||
|
||||
// 检查是否超过限制
|
||||
if len(counter.timestamps) >= s.limit {
|
||||
return false
|
||||
}
|
||||
|
||||
counter.timestamps = append(counter.timestamps, now)
|
||||
return true
|
||||
}
|
||||
|
||||
// Reset 重置指定键的计数器。
|
||||
//
|
||||
// 参数:
|
||||
// - key: 要重置的限流键
|
||||
func (s *SlidingWindowLimiter) Reset(key string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.counters, key)
|
||||
}
|
||||
|
||||
// ResetAll 重置所有计数器。
|
||||
func (s *SlidingWindowLimiter) ResetAll() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.counters = make(map[string]*windowCounter)
|
||||
}
|
||||
|
||||
// Cleanup 清理长时间未使用的计数器。
|
||||
//
|
||||
// 参数:
|
||||
// - maxAge: 未使用计数器的最大保留时间
|
||||
func (s *SlidingWindowLimiter) Cleanup(maxAge time.Duration) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
for key, counter := range s.counters {
|
||||
counter.mu.Lock()
|
||||
if len(counter.timestamps) > 0 {
|
||||
lastTime := counter.timestamps[len(counter.timestamps)-1]
|
||||
if now.Sub(lastTime) > maxAge {
|
||||
delete(s.counters, key)
|
||||
}
|
||||
}
|
||||
counter.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Stats 返回限流器统计信息。
|
||||
type SlidingWindowStats struct {
|
||||
Window time.Duration // 窗口大小
|
||||
Limit int // 窗口内最大请求数
|
||||
Precise bool // 是否精确模式
|
||||
CounterKeys int // 当前活跃的键数量
|
||||
}
|
||||
|
||||
// GetStats 返回统计信息。
|
||||
func (s *SlidingWindowLimiter) GetStats() SlidingWindowStats {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return SlidingWindowStats{
|
||||
Window: s.window,
|
||||
Limit: s.limit,
|
||||
Precise: s.precise,
|
||||
CounterKeys: len(s.counters),
|
||||
}
|
||||
}
|
||||
|
||||
// GetCount 获取指定键的当前计数。
|
||||
//
|
||||
// 参数:
|
||||
// - key: 限流键
|
||||
//
|
||||
// 返回值:
|
||||
// - int: 当前窗口内的请求数
|
||||
func (s *SlidingWindowLimiter) GetCount(key string) int {
|
||||
s.mu.RLock()
|
||||
counter, ok := s.counters[key]
|
||||
s.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
counter.mu.Lock()
|
||||
defer counter.mu.Unlock()
|
||||
|
||||
if s.precise {
|
||||
// 精确模式:计算窗口内的有效请求数
|
||||
now := time.Now()
|
||||
windowStart := now.Add(-s.window)
|
||||
count := 0
|
||||
for _, t := range counter.timestamps {
|
||||
if t.After(windowStart) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
return int(counter.count)
|
||||
}
|
||||
@ -21,6 +21,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
@ -151,6 +152,30 @@ func (s *Server) SetListeners(listeners []net.Listener) {
|
||||
s.listeners = listeners
|
||||
}
|
||||
|
||||
// GetTLSConfig 获取 TLS 配置。
|
||||
//
|
||||
// 返回服务器的 TLS 配置,用于 HTTP/3 等需要 TLS 的协议。
|
||||
//
|
||||
// 返回值:
|
||||
// - *tls.Config: TLS 配置对象
|
||||
// - error: 未配置 TLS 或配置无效时返回错误
|
||||
func (s *Server) GetTLSConfig() (*tls.Config, error) {
|
||||
if s.tlsManager == nil {
|
||||
return nil, fmt.Errorf("TLS not configured")
|
||||
}
|
||||
return s.tlsManager.GetTLSConfig(), nil
|
||||
}
|
||||
|
||||
// GetHandler 获取请求处理器。
|
||||
//
|
||||
// 返回服务器的请求处理器,用于 HTTP/3 等需要复用处理器的场景。
|
||||
//
|
||||
// 返回值:
|
||||
// - fasthttp.RequestHandler: 请求处理器
|
||||
func (s *Server) GetHandler() fasthttp.RequestHandler {
|
||||
return s.handler
|
||||
}
|
||||
|
||||
// buildMiddlewareChain 构建中间件链。
|
||||
//
|
||||
// 根据服务器配置按顺序构建中间件链,顺序为:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user