Disk cache implementation was previously removed but config structs remained. Remove ProxyCachePathConfig, Config.CachePath field, e2e WithCachePath helper, and docs reference.
1125 lines
24 KiB
Plaintext
1125 lines
24 KiB
Plaintext
# Lolly - AI Agent 使用指南
|
||
|
||
> 本文档专为 AI Agent 设计,提供 Lolly HTTP 服务器的完整使用参考。
|
||
|
||
## 项目概述
|
||
|
||
Lolly 是一个高性能 HTTP 服务器与反向代理,使用 Go 语言编写,基于 fasthttp 构建。提供类似 nginx 的功能,但使用 YAML 配置格式,单二进制部署。
|
||
|
||
**核心特性**:
|
||
- 静态文件服务(零拷贝传输、sendfile)
|
||
- 反向代理(7 种负载均衡算法、健康检查、故障转移)
|
||
- HTTP/2 和 HTTP/3 (QUIC) 支持
|
||
- WebSocket 代理
|
||
- Lua 脚本扩展(nginx-lua 兼容 API)
|
||
- SSL/TLS(OCSP Stapling、Session Tickets)
|
||
- 安全特性(访问控制、速率限制、GeoIP 过滤)
|
||
- 热升级和配置热重载
|
||
|
||
## 快速开始
|
||
|
||
### 构建与运行
|
||
|
||
```bash
|
||
# 构建
|
||
make build # 生产构建(静态链接)
|
||
make build-pgo # PGO 优化构建
|
||
|
||
# 运行
|
||
./bin/lolly # 使用默认配置 lolly.yaml
|
||
./bin/lolly -c /path/to/config.yaml # 指定配置文件
|
||
./bin/lolly -g -o config.yaml # 生成默认配置
|
||
./bin/lolly -i nginx.conf -o lolly.yaml # 导入 nginx 配置
|
||
./bin/lolly -v # 显示版本
|
||
```
|
||
|
||
### 最小配置示例
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":8080"
|
||
static:
|
||
- path: "/"
|
||
root: "/var/www/html"
|
||
```
|
||
|
||
## 配置结构
|
||
|
||
### 根配置 (Config)
|
||
|
||
```yaml
|
||
mode: auto # 运行模式: single/vhost/multi_server/auto
|
||
variables: {} # 自定义变量
|
||
logging: {} # 日志配置
|
||
servers: [] # 服务器列表(必填)
|
||
stream: [] # TCP/UDP Stream 代理
|
||
monitoring: {} # 监控配置
|
||
http3: {} # HTTP/3 配置
|
||
resolver: {} # DNS 解析配置
|
||
performance: {} # 性能配置
|
||
shutdown: {} # 关闭配置
|
||
include: [] # 配置引入
|
||
```
|
||
|
||
### 运行模式
|
||
|
||
| 模式 | 说明 | 自动推断条件 |
|
||
|------|------|--------------|
|
||
| `single` | 单服务器 | servers 数量 = 1 |
|
||
| `vhost` | 虚拟主机 | servers 数量 > 1 且所有 listen 相同 |
|
||
| `multi_server` | 多服务器 | servers 数量 > 1 且 listen 不同 |
|
||
| `auto` | 自动推断 | 默认值 |
|
||
|
||
### 服务器配置 (ServerConfig)
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":8080" # 监听地址(必填)
|
||
name: "api.example.com" # 服务器名称(虚拟主机匹配)
|
||
server_names: [] # 多域名支持(支持通配符)
|
||
default: false # 虚拟主机默认服务器
|
||
|
||
# 超时配置
|
||
read_timeout: 60s
|
||
write_timeout: 60s
|
||
idle_timeout: 120s
|
||
|
||
# 连接限制
|
||
max_conns_per_ip: 0 # 单 IP 最大连接数(0=不限制)
|
||
max_requests_per_conn: 0 # 单连接最大请求数
|
||
concurrency: 262144 # 最大并发连接数
|
||
client_max_body_size: "100MB"
|
||
|
||
# 缓冲区
|
||
read_buffer_size: 16384 # 16KB
|
||
write_buffer_size: 16384
|
||
|
||
# 功能模块
|
||
static: [] # 静态文件服务
|
||
proxy: [] # 反向代理
|
||
rewrite: [] # URL 重写
|
||
ssl: {} # SSL/TLS 配置
|
||
security: {} # 安全配置
|
||
compression: {} # 压缩配置
|
||
lua: {} # Lua 脚本配置
|
||
```
|
||
|
||
## 静态文件服务
|
||
|
||
### 基本配置
|
||
|
||
```yaml
|
||
static:
|
||
- path: "/" # 路径前缀
|
||
root: "/var/www/html" # 根目录
|
||
index: ["index.html", "index.htm"]
|
||
|
||
- path: "/assets/"
|
||
alias: "/var/www/files/" # 别名(与 root 互斥)
|
||
```
|
||
|
||
### 高级配置
|
||
|
||
```yaml
|
||
static:
|
||
- path: "/"
|
||
root: "/var/www/html"
|
||
|
||
# try_files 回退
|
||
try_files: ["$uri", "$uri.html", "/index.html"]
|
||
try_files_pass: false # 回退是否触发中间件
|
||
|
||
# 缓存
|
||
expires: "30d" # max/epoch/off 或时间
|
||
|
||
# 目录列表
|
||
auto_index: false # 启用目录列表
|
||
auto_index_format: "html" # html/json/xml
|
||
auto_index_exact_size: false
|
||
auto_index_localtime: false
|
||
|
||
# 安全
|
||
symlink_check: false # 符号链接检查
|
||
|
||
# Location 匹配类型
|
||
location_type: "prefix" # exact/prefix/regex/regex_caseless/prefix_priority/named
|
||
internal: false # 仅允许内部访问
|
||
```
|
||
|
||
### try_files 变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$uri` | 请求路径 |
|
||
| `$uri/` | 请求路径加斜杠 |
|
||
| `$uri.<ext>` | 请求路径加扩展名(如 `$uri.html`) |
|
||
|
||
## 反向代理
|
||
|
||
### 基本配置
|
||
|
||
```yaml
|
||
proxy:
|
||
- path: "/api/"
|
||
targets:
|
||
- url: "http://backend1:8080"
|
||
weight: 3
|
||
- url: "http://backend2:8080"
|
||
weight: 1
|
||
load_balance: "weighted_round_robin"
|
||
```
|
||
|
||
### 负载均衡算法
|
||
|
||
| 算法 | 说明 | 配置 |
|
||
|------|------|------|
|
||
| `round_robin` | 轮询 | 默认 |
|
||
| `weighted_round_robin` | 加权轮询 | 使用 weight 字段 |
|
||
| `least_conn` | 最少连接 | 选择活跃连接最少的目标 |
|
||
| `ip_hash` | IP 哈希 | 同一客户端路由到同一目标 |
|
||
| `consistent_hash` | 一致性哈希 | 配置 hash_key 和 virtual_nodes |
|
||
| `random` | 随机选择 | Power of Two Choices |
|
||
|
||
### 目标配置 (ProxyTarget)
|
||
|
||
```yaml
|
||
targets:
|
||
- url: "http://backend:8080"
|
||
weight: 3 # 权重
|
||
max_conns: 1000 # 最大并发连接
|
||
max_fails: 3 # 最大失败次数
|
||
fail_timeout: 30s # 失败超时
|
||
backup: false # 备份服务器
|
||
down: false # 标记为不可用
|
||
proxy_uri: "/api" # 替换请求路径
|
||
```
|
||
|
||
### 健康检查
|
||
|
||
```yaml
|
||
health_check:
|
||
path: "/health"
|
||
interval: 10s
|
||
timeout: 5s
|
||
slow_start: 30s # 慢启动时间
|
||
match: # 匹配条件
|
||
status: ["200-299"]
|
||
body: "ok"
|
||
headers:
|
||
X-Health: "ok"
|
||
```
|
||
|
||
### 故障转移
|
||
|
||
```yaml
|
||
next_upstream:
|
||
tries: 3 # 重试次数
|
||
http_codes: [502, 503, 504] # 触发重试的状态码
|
||
```
|
||
|
||
### 代理头部
|
||
|
||
```yaml
|
||
headers:
|
||
set_request: # 设置请求头
|
||
X-Real-IP: "$remote_addr"
|
||
X-Forwarded-For: "$proxy_add_x_forwarded_for"
|
||
X-Forwarded-Proto: "$scheme"
|
||
Host: "$host"
|
||
set_response: # 设置响应头
|
||
X-Proxy-By: "lolly"
|
||
remove: # 移除请求头
|
||
- "X-Internal"
|
||
hide_response: # 隐藏响应头
|
||
- "X-Frame-Options"
|
||
set_forwarded_host: true # 设置 X-Forwarded-Host
|
||
set_forwarded_proto: true # 设置 X-Forwarded-Proto
|
||
```
|
||
|
||
### 代理缓存
|
||
|
||
```yaml
|
||
cache:
|
||
enabled: true
|
||
zone: "api_cache"
|
||
key: "$request_uri" # 缓存键
|
||
duration: 10m # 缓存时间
|
||
|
||
# 缓存锁(防止缓存击穿)
|
||
cache_lock: true
|
||
cache_lock_timeout: 5s
|
||
|
||
# 过期复用
|
||
stale_while_revalidate: 30s
|
||
stale_if_error: 300s
|
||
stale_if_timeout: 60s
|
||
|
||
cache_valid:
|
||
200: 10m # 状态码缓存时间
|
||
301: 1h
|
||
404: 1m
|
||
any: 5s # 其他状态码
|
||
```
|
||
|
||
### 上游 SSL
|
||
|
||
```yaml
|
||
proxy_ssl:
|
||
enabled: true
|
||
server_name: "api.internal" # SNI
|
||
trusted_ca: "/etc/ssl/ca.crt"
|
||
client_cert: "/etc/ssl/client.crt"
|
||
client_key: "/etc/ssl/client.key"
|
||
min_version: "TLSv1.2"
|
||
insecure_skip_verify: false
|
||
```
|
||
|
||
### Location/Refresh 头改写
|
||
|
||
```yaml
|
||
redirect_rewrite:
|
||
mode: "default" # default/off/custom
|
||
rules: # custom 模式规则
|
||
- pattern: "http://backend:8000/"
|
||
replacement: "$scheme://$host:$server_port/"
|
||
```
|
||
|
||
### 代理超时
|
||
|
||
```yaml
|
||
timeout:
|
||
dial: 5s # TCP 连接超时
|
||
connect: 30s # 总连接超时(含 DNS/TLS)
|
||
read: 30s # 读取超时
|
||
write: 30s # 写入超时
|
||
```
|
||
|
||
### 缓冲配置
|
||
|
||
```yaml
|
||
buffering:
|
||
mode: "default" # default/on/off
|
||
buffer_size: 16384 # 单缓冲区大小
|
||
buffers: "8 16k" # 8 个 16KB 缓冲区
|
||
```
|
||
|
||
## SSL/TLS
|
||
|
||
### 服务端 SSL
|
||
|
||
```yaml
|
||
ssl:
|
||
enabled: true
|
||
cert: "/etc/ssl/server.crt"
|
||
key: "/etc/ssl/server.key"
|
||
|
||
# 双向 TLS
|
||
client_ca: "/etc/ssl/ca.crt"
|
||
client_verify: "require" # none/optional/require
|
||
|
||
# 协议和加密套件
|
||
protocols: ["TLSv1.2", "TLSv1.3"]
|
||
ciphers: ["ECDHE-RSA-AES256-GCM-SHA384"]
|
||
|
||
# OCSP Stapling
|
||
ocsp_stapling: true
|
||
|
||
# Session Tickets
|
||
session_tickets: true
|
||
session_tickets_keys:
|
||
- "/etc/ssl/ticket.key"
|
||
```
|
||
|
||
## 安全配置
|
||
|
||
### 访问控制
|
||
|
||
```yaml
|
||
security:
|
||
access:
|
||
allow: # 白名单
|
||
- "192.168.0.0/16"
|
||
- "10.0.0.0/8"
|
||
deny: # 黑名单
|
||
- "192.168.100.0/24"
|
||
default: "allow" # 默认策略
|
||
trusted_proxies: # 信任的代理 IP
|
||
- "10.0.0.1"
|
||
```
|
||
|
||
### 速率限制
|
||
|
||
```yaml
|
||
security:
|
||
rate_limit:
|
||
request_rate: 100 # 每秒请求数
|
||
burst: 10 # 突发容量
|
||
mode: "exact" # exact/approximate
|
||
|
||
# 滑动窗口
|
||
sliding_window:
|
||
enabled: true
|
||
window: 1s
|
||
limit: 100
|
||
```
|
||
|
||
### 连接限制
|
||
|
||
```yaml
|
||
security:
|
||
rate_limit:
|
||
conn_limit: 100 # 单 IP 并发连接数
|
||
```
|
||
|
||
### 认证
|
||
|
||
```yaml
|
||
security:
|
||
auth:
|
||
type: "basic"
|
||
realm: "Restricted"
|
||
htpasswd: "/etc/lolly/.htpasswd"
|
||
algorithm: "argon2id" # bcrypt/argon2id
|
||
|
||
# 子请求认证
|
||
auth_request:
|
||
enabled: true
|
||
uri: "/internal/auth"
|
||
timeout: 5s
|
||
```
|
||
|
||
### 安全头部
|
||
|
||
```yaml
|
||
security:
|
||
headers:
|
||
x_frame_options: "DENY"
|
||
x_content_type_options: "nosniff"
|
||
x_xss_protection: "1; mode=block"
|
||
referrer_policy: "strict-origin-when-cross-origin"
|
||
content_security_policy: "default-src 'self'"
|
||
permissions_policy: "geolocation=()"
|
||
strict_transport_security: "max-age=31536000; includeSubDomains"
|
||
```
|
||
|
||
### GeoIP 过滤
|
||
|
||
```yaml
|
||
security:
|
||
geoip:
|
||
enabled: true
|
||
database: "/etc/geoip/GeoLite2-Country.mmdb"
|
||
allow:
|
||
- "CN"
|
||
- "US"
|
||
deny:
|
||
- "XX"
|
||
default: "allow"
|
||
```
|
||
|
||
### 错误页面
|
||
|
||
```yaml
|
||
security:
|
||
error_page:
|
||
pages:
|
||
404: "/errors/404.html"
|
||
500: "/errors/500.html"
|
||
502: "/errors/502.html"
|
||
default: "/errors/default.html"
|
||
response_code: 200 # 返回状态码
|
||
```
|
||
|
||
## 压缩配置
|
||
|
||
```yaml
|
||
compression:
|
||
enabled: true
|
||
min_length: 1024 # 最小压缩长度
|
||
level: 6 # 压缩级别(1-9)
|
||
types: # 压缩的 MIME 类型
|
||
- "text/html"
|
||
- "text/css"
|
||
- "application/json"
|
||
|
||
# Gzip 静态
|
||
gzip_static: true # 使用预压缩的 .gz 文件
|
||
|
||
# Brotli
|
||
brotli:
|
||
enabled: true
|
||
level: 4
|
||
```
|
||
|
||
## Lua 脚本
|
||
|
||
### 配置
|
||
|
||
```yaml
|
||
lua:
|
||
enabled: true
|
||
scripts:
|
||
# 路由脚本(有 route,无 phase)- 直接处理请求
|
||
- path: "./lib/lua/status.lua"
|
||
route: "/api/status" # 路由路径
|
||
route_type: "exact" # exact/prefix/prefix_priority/regex/regex_caseless
|
||
timeout: 30s # 执行超时
|
||
|
||
- path: "./lib/lua/echo.lua"
|
||
route: "/api/echo"
|
||
route_type: "prefix" # 前缀匹配,支持子路径
|
||
|
||
# 中间件脚本(有 phase,无 route)- 在请求处理阶段执行
|
||
- path: "/etc/lolly/rewrite.lua"
|
||
phase: "rewrite"
|
||
timeout: 10s
|
||
- path: "/etc/lolly/access.lua"
|
||
phase: "access"
|
||
- path: "/etc/lolly/content.lua"
|
||
phase: "content"
|
||
- path: "/etc/lolly/header_filter.lua"
|
||
phase: "header_filter"
|
||
- path: "/etc/lolly/body_filter.lua"
|
||
phase: "body_filter"
|
||
- path: "/etc/lolly/log.lua"
|
||
phase: "log"
|
||
global_settings:
|
||
max_concurrent_coroutines: 1000
|
||
coroutine_timeout: 30s
|
||
code_cache_size: 100
|
||
coroutine_stack_size: 64
|
||
minimize_stack_memory: true
|
||
coroutine_pool_warmup: 100
|
||
enable_file_watch: true # 启用文件监控,支持热更新
|
||
```
|
||
|
||
### 脚本类型
|
||
|
||
| 类型 | 配置 | 说明 |
|
||
|------|------|------|
|
||
| 路由脚本 | `route` + 无 `phase` | 直接处理 HTTP 请求,类似 nginx location |
|
||
| 中间件脚本 | `phase` + 无 `route` | 在请求处理阶段执行,可修改请求/响应 |
|
||
|
||
### 路由类型 (route_type)
|
||
|
||
| 类型 | 说明 | 示例 |
|
||
|------|------|------|
|
||
| `exact` | 精确匹配 | `/api/status` 只匹配该路径 |
|
||
| `prefix` | 前缀匹配 | `/api/echo` 匹配 `/api/echo/` 和 `/api/echo/xxx` |
|
||
| `prefix_priority` | 优先前缀匹配 (^~) | 优先于正则匹配 |
|
||
| `regex` | 正则匹配(区分大小写) | `^/api/v[0-9]+/` |
|
||
| `regex_caseless` | 正则匹配(不区分大小写) | `(?i)^/api/` |
|
||
|
||
### 执行阶段
|
||
|
||
```
|
||
请求 → rewrite → access → content → header_filter → body_filter → log → 响应
|
||
```
|
||
|
||
### 常用 API
|
||
|
||
```lua
|
||
-- 日志
|
||
ngx.log(ngx.ERR, "error message")
|
||
ngx.log(ngx.WARN, "warning")
|
||
ngx.log(ngx.INFO, "info")
|
||
|
||
-- 变量
|
||
local uri = ngx.var.uri
|
||
local method = ngx.var.request_method
|
||
local remote_addr = ngx.var.remote_addr
|
||
local host = ngx.var.host
|
||
local scheme = ngx.var.scheme
|
||
local request_uri = ngx.var.request_uri
|
||
|
||
-- 请求操作
|
||
ngx.req.get_method()
|
||
ngx.req.get_uri()
|
||
ngx.req.set_uri("/new_path")
|
||
ngx.req.set_uri_args("a=1&b=2")
|
||
ngx.req.get_uri_args()
|
||
ngx.req.get_headers()
|
||
ngx.req.set_header("X-Custom", "value")
|
||
ngx.req.clear_header("X-Custom")
|
||
ngx.req.get_body_data()
|
||
ngx.req.read_body()
|
||
|
||
-- 响应操作
|
||
ngx.resp.get_status()
|
||
ngx.resp.set_status(200)
|
||
ngx.resp.get_headers()
|
||
ngx.resp.set_header("X-Response", "value")
|
||
|
||
-- 快捷响应
|
||
ngx.say("Hello World")
|
||
ngx.print("Hello")
|
||
ngx.flush()
|
||
ngx.redirect("/new", 302)
|
||
ngx.exit(200)
|
||
|
||
-- 上下文
|
||
ngx.ctx.user_id = 123
|
||
local id = ngx.ctx.user_id
|
||
|
||
-- 共享字典
|
||
local dict = ngx.shared.DICT
|
||
dict:set("key", "value", 3600)
|
||
local val = dict:get("key")
|
||
dict:incr("counter", 1)
|
||
dict:delete("key")
|
||
|
||
-- 子请求
|
||
local res = ngx.location.capture("/internal/auth", {
|
||
method = ngx.HTTP_GET,
|
||
body = "data",
|
||
args = "a=1"
|
||
})
|
||
-- res.status, res.body, res.headers
|
||
|
||
-- 动态负载均衡
|
||
ngx.balancer.set_current_peer("backend:8080")
|
||
ngx.balancer.set_more_tries(3)
|
||
```
|
||
|
||
### 状态码常量
|
||
|
||
```lua
|
||
ngx.HTTP_OK = 200
|
||
ngx.HTTP_CREATED = 201
|
||
ngx.HTTP_MOVED_PERMANENTLY = 301
|
||
ngx.HTTP_FOUND = 302
|
||
ngx.HTTP_BAD_REQUEST = 400
|
||
ngx.HTTP_UNAUTHORIZED = 401
|
||
ngx.HTTP_FORBIDDEN = 403
|
||
ngx.HTTP_NOT_FOUND = 404
|
||
ngx.HTTP_INTERNAL_SERVER_ERROR = 500
|
||
ngx.HTTP_BAD_GATEWAY = 502
|
||
ngx.HTTP_SERVICE_UNAVAILABLE = 503
|
||
ngx.HTTP_GATEWAY_TIMEOUT = 504
|
||
```
|
||
|
||
### 路由脚本示例
|
||
|
||
**状态检查端点** (`lib/lua/status.lua`):
|
||
```lua
|
||
-- 返回服务器状态
|
||
ngx.say('{')
|
||
ngx.say(' "status": "ok",')
|
||
ngx.say(' "server": "lolly",')
|
||
ngx.say(' "version": "0.2.2"')
|
||
ngx.say('}')
|
||
ngx.exit(200)
|
||
```
|
||
|
||
**健康检查端点** (`lib/lua/health.lua`):
|
||
```lua
|
||
-- 用于负载均衡器健康检查
|
||
ngx.resp.set_status(200)
|
||
ngx.resp.set_header("Content-Type", "application/json")
|
||
ngx.say('{')
|
||
ngx.say(' "status": "healthy",')
|
||
ngx.say(' "server": "lolly"')
|
||
ngx.say('}')
|
||
ngx.exit(200)
|
||
```
|
||
|
||
**Echo 服务** (`lib/lua/echo.lua`):
|
||
```lua
|
||
-- 返回请求信息
|
||
local method = ngx.req.get_method()
|
||
local uri = ngx.req.get_uri()
|
||
local args = ngx.req.get_uri_args()
|
||
|
||
ngx.say('{')
|
||
ngx.say(' "method": "' .. method .. '",')
|
||
ngx.say(' "uri": "' .. uri .. '"')
|
||
|
||
if args and next(args) then
|
||
ngx.say(' ,"args": {')
|
||
local first = true
|
||
for k, v in pairs(args) do
|
||
if not first then ngx.say(',') end
|
||
first = false
|
||
ngx.say(' "' .. k .. '": "' .. v .. '"')
|
||
end
|
||
ngx.say(' }')
|
||
end
|
||
|
||
ngx.say('}')
|
||
ngx.exit(200)
|
||
```
|
||
|
||
**获取客户端 IP** (`lib/lua/ip.lua`):
|
||
```lua
|
||
local remote_addr = ngx.var.remote_addr or "unknown"
|
||
ngx.say('{')
|
||
ngx.say(' "remote_addr": "' .. remote_addr .. '"')
|
||
ngx.say('}')
|
||
ngx.exit(200)
|
||
```
|
||
|
||
**JSON 响应示例** (`lib/lua/json.lua`):
|
||
```lua
|
||
ngx.resp.set_header("Content-Type", "application/json")
|
||
ngx.resp.set_header("X-Server", "lolly")
|
||
ngx.say('{')
|
||
ngx.say(' "success": true,')
|
||
ngx.say(' "data": {')
|
||
ngx.say(' "message": "Hello from Lua!"')
|
||
ngx.say(' }')
|
||
ngx.say('}')
|
||
ngx.exit(200)
|
||
```
|
||
|
||
**HTTP 重定向** (`lib/lua/redirect.lua`):
|
||
```lua
|
||
local target = ngx.req.get_uri_args()["target"] or "https://example.com"
|
||
return ngx.redirect(target, 302)
|
||
```
|
||
|
||
## URL 重写
|
||
|
||
```yaml
|
||
rewrite:
|
||
- pattern: "^/old/(.*)$"
|
||
replacement: "/new/$1"
|
||
flag: "last" # last/break/redirect/permanent
|
||
|
||
- pattern: "^/api/v1/"
|
||
replacement: "/v1/"
|
||
flag: "break"
|
||
```
|
||
|
||
## TCP/UDP Stream 代理
|
||
|
||
```yaml
|
||
stream:
|
||
- listen: ":3306"
|
||
protocol: "tcp"
|
||
upstream:
|
||
load_balance: "round_robin"
|
||
targets:
|
||
- addr: "mysql1:3306"
|
||
weight: 3
|
||
- addr: "mysql2:3306"
|
||
weight: 1
|
||
ssl: # Stream SSL 终端
|
||
enabled: true
|
||
cert: "/etc/ssl/server.crt"
|
||
key: "/etc/ssl/server.key"
|
||
proxy_ssl: # 上游 SSL
|
||
enabled: true
|
||
verify: true
|
||
trusted_ca: "/etc/ssl/ca.crt"
|
||
```
|
||
|
||
## HTTP/3 配置
|
||
|
||
```yaml
|
||
http3:
|
||
enabled: true
|
||
listen: ":443" # UDP 监听地址
|
||
```
|
||
|
||
## 日志配置
|
||
|
||
```yaml
|
||
logging:
|
||
format: "json" # json/text
|
||
|
||
access:
|
||
path: "/var/log/lolly/access.log"
|
||
format: '$remote_addr - $remote_user [$time] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"'
|
||
|
||
error:
|
||
path: "/var/log/lolly/error.log"
|
||
level: "info" # debug/info/warn/error
|
||
```
|
||
|
||
### 日志变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$remote_addr` | 客户端 IP |
|
||
| `$remote_user` | 认证用户名 |
|
||
| `$time` | 时间 |
|
||
| `$request` | 请求行 |
|
||
| `$status` | 响应状态码 |
|
||
| `$body_bytes_sent` | 响应体大小 |
|
||
| `$http_referer` | Referer 头 |
|
||
| `$http_user_agent` | User-Agent 头 |
|
||
| `$request_time` | 请求处理时间 |
|
||
| `$upstream_response_time` | 上游响应时间 |
|
||
|
||
## 性能配置
|
||
|
||
```yaml
|
||
performance:
|
||
# Goroutine 池
|
||
goroutine_pool:
|
||
enabled: true
|
||
max_workers: 10000
|
||
min_workers: 100
|
||
idle_timeout: 60s
|
||
|
||
# 文件缓存
|
||
file_cache:
|
||
max_entries: 50000
|
||
max_size: 268435456 # 256MB
|
||
inactive: 20s
|
||
|
||
# 连接池
|
||
transport:
|
||
idle_conn_timeout: 90s
|
||
max_conns_per_host: 500
|
||
max_idle_conns: 100
|
||
max_idle_conns_per_host: 20
|
||
```
|
||
|
||
### 容量规划
|
||
|
||
| 流量级别 | max_workers | file_cache.max_size | max_conns_per_host |
|
||
|----------|-------------|---------------------|-------------------|
|
||
| <10K RPS | 500 | 64MB | 100 |
|
||
| 10K-100K RPS | 5000 | 256MB | 300 |
|
||
| >100K RPS | 10000 | 512MB | 500 |
|
||
|
||
## 监控配置
|
||
|
||
```yaml
|
||
monitoring:
|
||
# 状态端点
|
||
status:
|
||
enabled: true
|
||
path: "/_status"
|
||
format: "json"
|
||
allow:
|
||
- "127.0.0.1"
|
||
- "localhost"
|
||
|
||
# pprof 端点
|
||
pprof:
|
||
enabled: true
|
||
path: "/debug/pprof"
|
||
allow:
|
||
- "127.0.0.1"
|
||
```
|
||
|
||
### 状态端点响应
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.2",
|
||
"uptime": "2h30m15s",
|
||
"connections": 150,
|
||
"requests": 125000,
|
||
"bytes_sent": 5368709120,
|
||
"bytes_received": 1073741824,
|
||
"pool": {
|
||
"workers": 256,
|
||
"idle_workers": 200,
|
||
"max_workers": 10000
|
||
},
|
||
"upstreams": [...],
|
||
"cache": {...}
|
||
}
|
||
```
|
||
|
||
## DNS 解析配置
|
||
|
||
```yaml
|
||
resolver:
|
||
servers:
|
||
- "8.8.8.8:53"
|
||
- "8.8.4.4:53"
|
||
timeout: 5s
|
||
cache_ttl: 30s
|
||
```
|
||
|
||
## 关闭配置
|
||
|
||
```yaml
|
||
shutdown:
|
||
graceful_timeout: 30s # 优雅关闭超时
|
||
fast_timeout: 5s # 快速关闭超时
|
||
```
|
||
|
||
## 信号处理
|
||
|
||
| 信号 | 行为 |
|
||
|------|------|
|
||
| SIGTERM, SIGINT | 快速停止 |
|
||
| SIGQUIT | 优雅停止 |
|
||
| SIGHUP | 重载配置 |
|
||
| SIGUSR1 | 重新打开日志 |
|
||
| SIGUSR2 | 热升级 |
|
||
|
||
## 配置引入
|
||
|
||
```yaml
|
||
include:
|
||
- path: "/etc/lolly/conf.d/*.yaml"
|
||
required: false # 文件不存在是否报错
|
||
```
|
||
|
||
## 内置变量
|
||
|
||
### 请求变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$uri` | 请求 URI(不含查询参数) |
|
||
| `$request_uri` | 完整请求 URI |
|
||
| `$args` | 查询参数 |
|
||
| `$query_string` | 查询参数(同 $args) |
|
||
| `$request_method` | 请求方法 |
|
||
| `$request_body` | 请求体 |
|
||
| `$host` | Host 头 |
|
||
| `$hostname` | 服务器主机名 |
|
||
| `$scheme` | 协议(http/https) |
|
||
| `$server_port` | 服务器端口 |
|
||
| `$server_addr` | 服务器地址 |
|
||
| `$remote_addr` | 客户端 IP |
|
||
| `$remote_port` | 客户端端口 |
|
||
| `$request_length` | 请求长度 |
|
||
| `$request_time` | 请求处理时间 |
|
||
|
||
### 头部变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$http_<name>` | 请求头(如 `$http_user_agent`) |
|
||
| `$sent_http_<name>` | 响应头 |
|
||
|
||
### Cookie 变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$cookie_<name>` | Cookie 值 |
|
||
|
||
### 查询参数变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$arg_<name>` | 查询参数值 |
|
||
|
||
### SSL 变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$ssl_protocol` | SSL 协议 |
|
||
| `$ssl_cipher` | 加密套件 |
|
||
| `$ssl_client_s_dn` | 客户端证书 DN |
|
||
| `$ssl_client_verify` | 客户端证书验证结果 |
|
||
|
||
### 代理变量
|
||
|
||
| 变量 | 说明 |
|
||
|------|------|
|
||
| `$proxy_add_x_forwarded_for` | 追加 X-Forwarded-For |
|
||
| `$upstream_addr` | 上游地址 |
|
||
| `$upstream_response_time` | 上游响应时间 |
|
||
|
||
## nginx 配置导入
|
||
|
||
支持的指令:
|
||
|
||
**server 块**:
|
||
- listen, server_name, ssl_certificate, ssl_certificate_key
|
||
|
||
**location 块**:
|
||
- proxy_pass, root, alias, index, try_files
|
||
|
||
**upstream 块**:
|
||
- server(weight, max_fails, fail_timeout, backup, down)
|
||
- least_conn, ip_hash, hash, random
|
||
|
||
**其他**:
|
||
- gzip, gzip_types, gzip_min_length
|
||
- client_max_body_size
|
||
- access_log, error_log
|
||
- rewrite, return(301/302)
|
||
- error_page, auth_basic
|
||
|
||
## 测试
|
||
|
||
```bash
|
||
make test # 单元测试
|
||
make test-integration # 集成测试
|
||
make test-e2e # E2E 测试(需要 Docker)
|
||
make test-cover # 测试覆盖率
|
||
make bench # 基准测试
|
||
```
|
||
|
||
## Docker
|
||
|
||
```bash
|
||
# 构建镜像
|
||
make docker
|
||
|
||
# 运行容器
|
||
docker run -d \
|
||
--name lolly \
|
||
-p 80:80 -p 443:443 -p 443:443/udp \
|
||
-v /etc/lolly:/etc/lolly:ro \
|
||
-v /var/www:/var/www:ro \
|
||
lolly:latest
|
||
```
|
||
|
||
## 常见配置模式
|
||
|
||
### 静态网站
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":80"
|
||
static:
|
||
- path: "/"
|
||
root: "/var/www/html"
|
||
try_files: ["$uri", "$uri.html", "/index.html"]
|
||
compression:
|
||
enabled: true
|
||
```
|
||
|
||
### API 网关
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":80"
|
||
proxy:
|
||
- path: "/api/"
|
||
targets:
|
||
- url: "http://api1:8080"
|
||
- url: "http://api2:8080"
|
||
load_balance: "least_conn"
|
||
health_check:
|
||
path: "/health"
|
||
interval: 10s
|
||
```
|
||
|
||
### 虚拟主机
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":80"
|
||
name: "api.example.com"
|
||
proxy:
|
||
- path: "/"
|
||
targets:
|
||
- url: "http://api:8080"
|
||
|
||
- listen: ":80"
|
||
name: "static.example.com"
|
||
static:
|
||
- path: "/"
|
||
root: "/var/www/static"
|
||
```
|
||
|
||
### SSL 终端
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":443"
|
||
ssl:
|
||
enabled: true
|
||
cert: "/etc/ssl/server.crt"
|
||
key: "/etc/ssl/server.key"
|
||
protocols: ["TLSv1.2", "TLSv1.3"]
|
||
proxy:
|
||
- path: "/"
|
||
targets:
|
||
- url: "http://backend:8080"
|
||
```
|
||
|
||
### 完整生产配置
|
||
|
||
```yaml
|
||
servers:
|
||
- listen: ":443"
|
||
name: "example.com"
|
||
|
||
ssl:
|
||
enabled: true
|
||
cert: "/etc/ssl/server.crt"
|
||
key: "/etc/ssl/server.key"
|
||
protocols: ["TLSv1.2", "TLSv1.3"]
|
||
ocsp_stapling: true
|
||
|
||
security:
|
||
access:
|
||
default: "allow"
|
||
rate_limit:
|
||
request_rate: 100
|
||
burst: 20
|
||
headers:
|
||
x_frame_options: "DENY"
|
||
x_content_type_options: "nosniff"
|
||
|
||
compression:
|
||
enabled: true
|
||
min_length: 1024
|
||
|
||
proxy:
|
||
- path: "/api/"
|
||
targets:
|
||
- url: "http://backend1:8080"
|
||
weight: 3
|
||
- url: "http://backend2:8080"
|
||
weight: 1
|
||
load_balance: "weighted_round_robin"
|
||
health_check:
|
||
path: "/health"
|
||
interval: 10s
|
||
next_upstream:
|
||
tries: 3
|
||
http_codes: [502, 503, 504]
|
||
|
||
performance:
|
||
goroutine_pool:
|
||
enabled: true
|
||
max_workers: 10000
|
||
file_cache:
|
||
max_entries: 50000
|
||
max_size: 268435456
|
||
|
||
monitoring:
|
||
status:
|
||
enabled: true
|
||
path: "/_status"
|
||
pprof:
|
||
enabled: true
|
||
```
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
internal/
|
||
├── app/ # 应用入口、信号处理
|
||
├── config/ # 配置加载、验证
|
||
├── server/ # HTTP 服务器核心
|
||
├── handler/ # 请求处理器(静态文件)
|
||
├── proxy/ # 反向代理
|
||
├── loadbalance/ # 负载均衡算法
|
||
├── matcher/ # Location 匹配器
|
||
├── middleware/ # 中间件链
|
||
├── lua/ # Lua 脚本引擎
|
||
├── ssl/ # TLS 配置
|
||
├── cache/ # 缓存系统
|
||
├── resolver/ # DNS 解析
|
||
├── variable/ # 变量系统
|
||
└── ...
|
||
```
|
||
|
||
## 依赖
|
||
|
||
| 库 | 用途 |
|
||
|---|---|
|
||
| fasthttp | HTTP 服务器核心 |
|
||
| fasthttp/router | HTTP 路由器 |
|
||
| quic-go | QUIC/HTTP/3 |
|
||
| zerolog | 日志 |
|
||
| gopher-lua | Lua 脚本引擎 |
|
||
| klauspost/compress | Gzip/Brotli 压缩 |
|
||
| geoip2-golang | GeoIP 查询 |
|
||
| yaml.v3 | YAML 配置解析 |
|