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:
xfy 2026-04-03 15:39:06 +08:00
parent a627d58832
commit d6367a1c38
22 changed files with 2785 additions and 114 deletions

View File

@ -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:

View File

@ -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 {

View File

@ -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. **结果判定**
- 返回 200NGINX 继续处理原请求
- 返回 401/403NGINX 拒绝访问并返回相应状态码
- 其他状态码:视为错误,返回 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. 安全测试工具
### 在线测试

View File

@ -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. 应用场景示例

View File

@ -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 {

View File

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

View File

@ -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 更优雅统一,支持定时器/信号/进程事件过滤,天然无惊群问题
**触发模式说明**
- **ETEdge Triggered边缘触发**:仅在状态变化时通知,需一次性处理所有数据
- **LTLevel 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 文件路径错误

View 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` | 二进制 IP4/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` | 唯一请求 ID1.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+ 版本*

View File

@ -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+ 个变量完整列表
## 快速参考
### 核心配置结构

View File

@ -7,7 +7,7 @@
/ultrawork 深度分析 https://nginx.org/en/docs/ nginx 的功能,@docs/ 目录下已经有一些分析过的文档了,看看有没有能完善的
/ultrawork 深度分析下当前的配置文件,是否有配置文件描述了,但代码未实现的功能
/ultrawork 深度分析下当前的配置文件,是否有配置文件描述了,但代码未实现的功能。同时也分析下有没有代码实现了,但是配置文件缺失的地方
## 单元测试

11
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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)
}

View File

@ -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 日志配置。

View File

@ -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
View 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)
// 初始化 ctxfasthttp 的 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
View 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,
}
}

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

View File

@ -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) {

View 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"}
}

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

View File

@ -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 构建中间件链。
//
// 根据服务器配置按顺序构建中间件链,顺序为: