docs: 精简并重构 README 文档结构
移除冗余的配置示例、生产清单和常见问题章节,新增核心模块依赖关系、
设计模式和性能优化设计等架构文档,统一 Markdown 表格格式。
💘 Generated with Crush
Assisted-by: mimo-v2.5-pro via Crush <crush@charm.land>
This commit is contained in:
parent
3a09ca4f48
commit
0e8b99e17f
714
README.md
714
README.md
@ -28,14 +28,14 @@
|
|||||||
|
|
||||||
### 负载均衡
|
### 负载均衡
|
||||||
|
|
||||||
| 算法 | 说明 |
|
| 算法 | 说明 |
|
||||||
|------|------|
|
| -------------------- | -------------------------------------------------------- |
|
||||||
| Round Robin | 轮询,均匀分配 |
|
| Round Robin | 轮询,均匀分配 |
|
||||||
| Weighted Round Robin | 加权轮询,按权重分配 |
|
| Weighted Round Robin | 加权轮询,按权重分配 |
|
||||||
| Least Connections | 最少连接,选择活跃连接最少的目标 |
|
| Least Connections | 最少连接,选择活跃连接最少的目标 |
|
||||||
| IP Hash | IP 哈希,同一客户端始终路由到同一目标 |
|
| IP Hash | IP 哈希,同一客户端始终路由到同一目标 |
|
||||||
| Consistent Hash | 一致性哈希,支持虚拟节点(默认 150),最小化节点变更影响 |
|
| Consistent Hash | 一致性哈希,支持虚拟节点(默认 150),最小化节点变更影响 |
|
||||||
| Random | Power of Two Choices,随机选择两个后比较,简单高效 |
|
| Random | Power of Two Choices,随机选择两个后比较,简单高效 |
|
||||||
|
|
||||||
### 故障转移
|
### 故障转移
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ make build-all
|
|||||||
./bin/lolly -c /path/to/lolly.yaml
|
./bin/lolly -c /path/to/lolly.yaml
|
||||||
|
|
||||||
# 生成默认配置
|
# 生成默认配置
|
||||||
./bin/lolly --generate-config -o lolly.yaml
|
./bin/lolly -g -o lolly.yaml
|
||||||
|
|
||||||
# 导入 nginx 配置
|
# 导入 nginx 配置
|
||||||
./bin/lolly --import /etc/nginx/nginx.conf -o lolly.yaml
|
./bin/lolly --import /etc/nginx/nginx.conf -o lolly.yaml
|
||||||
@ -138,6 +138,7 @@ lolly 支持将 nginx 配置文件转换为 YAML 格式:
|
|||||||
```
|
```
|
||||||
|
|
||||||
支持的 nginx 指令:
|
支持的 nginx 指令:
|
||||||
|
|
||||||
- `server` 块:listen、server_name、ssl_certificate、ssl_certificate_key
|
- `server` 块:listen、server_name、ssl_certificate、ssl_certificate_key
|
||||||
- `location` 块:proxy_pass、root、alias、index、try_files
|
- `location` 块:proxy_pass、root、alias、index、try_files
|
||||||
- `upstream` 块:server(含 weight、max_fails、fail_timeout、backup、down)、least_conn、ip_hash、hash、random
|
- `upstream` 块:server(含 weight、max_fails、fail_timeout、backup、down)、least_conn、ip_hash、hash、random
|
||||||
@ -145,392 +146,10 @@ lolly 支持将 nginx 配置文件转换为 YAML 格式:
|
|||||||
|
|
||||||
不支持的指令会在转换时显示警告,需要手动处理。
|
不支持的指令会在转换时显示警告,需要手动处理。
|
||||||
|
|
||||||
## 配置
|
|
||||||
|
|
||||||
配置文件使用 YAML 格式。以下是完整配置示例:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
mode: "auto" # single/vhost/multi_server/auto
|
|
||||||
|
|
||||||
variables:
|
|
||||||
set:
|
|
||||||
app_name: "lolly"
|
|
||||||
version: "1.0.0"
|
|
||||||
|
|
||||||
servers:
|
|
||||||
- listen: ":8080"
|
|
||||||
name: "example.com"
|
|
||||||
default: true # vhost 默认主机
|
|
||||||
server_names: ["example.com", "*.example.com"]
|
|
||||||
read_timeout: 30s
|
|
||||||
write_timeout: 30s
|
|
||||||
idle_timeout: 120s
|
|
||||||
max_conns_per_ip: 100
|
|
||||||
max_requests_per_conn: 1000
|
|
||||||
concurrency: 262144 # 最大并发连接数
|
|
||||||
read_buffer_size: 16384 # 16KB
|
|
||||||
write_buffer_size: 16384
|
|
||||||
client_max_body_size: "10MB"
|
|
||||||
server_tokens: false # 隐藏版本号
|
|
||||||
|
|
||||||
static:
|
|
||||||
- path: "/"
|
|
||||||
root: "/var/www/html"
|
|
||||||
index: ["index.html", "index.htm"]
|
|
||||||
try_files: ["$uri", "$uri/", "/index.html"]
|
|
||||||
try_files_pass: false
|
|
||||||
symlink_check: true
|
|
||||||
location_type: "prefix"
|
|
||||||
- path: "/assets/"
|
|
||||||
root: "/var/www/assets"
|
|
||||||
internal: true # 仅允许内部重定向访问
|
|
||||||
|
|
||||||
proxy:
|
|
||||||
- path: "/api"
|
|
||||||
targets:
|
|
||||||
- url: "http://backend1:8080"
|
|
||||||
weight: 2
|
|
||||||
max_conns: 100
|
|
||||||
max_fails: 3
|
|
||||||
fail_timeout: 30s
|
|
||||||
backup: false
|
|
||||||
down: false
|
|
||||||
proxy_uri: "/v1" # 替换请求路径
|
|
||||||
- url: "http://backend2:8080"
|
|
||||||
weight: 1
|
|
||||||
load_balance: "weighted_round_robin"
|
|
||||||
virtual_nodes: 150 # 一致性哈希虚拟节点数
|
|
||||||
hash_key: "$uri" # 一致性哈希键
|
|
||||||
location_type: "prefix_priority"
|
|
||||||
internal: false
|
|
||||||
health_check:
|
|
||||||
interval: 10s
|
|
||||||
path: "/health"
|
|
||||||
timeout: 5s
|
|
||||||
slow_start: 30s # 慢启动时间
|
|
||||||
match:
|
|
||||||
status: ["200-299"]
|
|
||||||
body: "ok"
|
|
||||||
headers:
|
|
||||||
Content-Type: "application/json"
|
|
||||||
next_upstream:
|
|
||||||
tries: 3
|
|
||||||
http_codes: [502, 503, 504]
|
|
||||||
timeout:
|
|
||||||
connect: 5s
|
|
||||||
read: 30s
|
|
||||||
write: 30s
|
|
||||||
headers:
|
|
||||||
set_request:
|
|
||||||
X-Forwarded-For: "$remote_addr"
|
|
||||||
X-Real-IP: "$remote_addr"
|
|
||||||
set_response:
|
|
||||||
X-Proxy-By: "lolly"
|
|
||||||
remove: ["X-Internal-Header"]
|
|
||||||
hide_response: ["X-Powered-By"]
|
|
||||||
pass_response: ["Content-Type", "Content-Length"]
|
|
||||||
cookie_domain: ".example.com"
|
|
||||||
cookie_path: "/"
|
|
||||||
cache:
|
|
||||||
enabled: true
|
|
||||||
max_age: 5m
|
|
||||||
stale_while_revalidate: 1m
|
|
||||||
stale_if_error: 10m
|
|
||||||
stale_if_timeout: 5m
|
|
||||||
cache_lock: true
|
|
||||||
cache_lock_timeout: 5s
|
|
||||||
methods: ["GET", "HEAD"]
|
|
||||||
min_uses: 1 # 缓存阈值
|
|
||||||
background_update_disable: false
|
|
||||||
cache_ignore_headers: ["Set-Cookie"]
|
|
||||||
revalidate: true # 条件请求
|
|
||||||
cache_valid:
|
|
||||||
ok: 10m # 200-299
|
|
||||||
redirect: 1h # 301/302
|
|
||||||
not_found: 1m
|
|
||||||
client_error: 0
|
|
||||||
server_error: 0
|
|
||||||
buffering:
|
|
||||||
mode: "default" # default/on/off
|
|
||||||
buffer_size: 16384
|
|
||||||
buffers: "8 16k" # 8 个 16KB 缓冲区
|
|
||||||
proxy_ssl:
|
|
||||||
enabled: true
|
|
||||||
server_name: "backend.internal"
|
|
||||||
trusted_ca: "/etc/ssl/ca/upstream-ca.crt"
|
|
||||||
client_cert: "/etc/ssl/client.crt"
|
|
||||||
client_key: "/etc/ssl/client.key"
|
|
||||||
min_version: "TLSv1.2"
|
|
||||||
insecure_skip_verify: false
|
|
||||||
redirect_rewrite:
|
|
||||||
mode: "default" # default/off/custom
|
|
||||||
rules:
|
|
||||||
- pattern: "http://backend:8000/"
|
|
||||||
replacement: "$scheme://$host:$server_port/"
|
|
||||||
client_max_body_size: "50MB"
|
|
||||||
balancer_by_lua:
|
|
||||||
enabled: false
|
|
||||||
script: "/etc/lolly/scripts/balancer.lua"
|
|
||||||
timeout: 100ms
|
|
||||||
fallback: "round_robin"
|
|
||||||
|
|
||||||
ssl:
|
|
||||||
cert: "/etc/ssl/certs/server.crt"
|
|
||||||
key: "/etc/ssl/private/server.key"
|
|
||||||
cert_chain: "/etc/ssl/certs/chain.crt"
|
|
||||||
protocols: ["TLSv1.2", "TLSv1.3"]
|
|
||||||
ciphers: ["TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384"]
|
|
||||||
ocsp_stapling: true
|
|
||||||
session_tickets:
|
|
||||||
enabled: true
|
|
||||||
key_file: "/var/lib/lolly/session_tickets.key"
|
|
||||||
rotate_interval: 1h
|
|
||||||
retain_keys: 3
|
|
||||||
client_verify:
|
|
||||||
enabled: false
|
|
||||||
mode: "require" # none/request/require/optional_no_ca
|
|
||||||
client_ca: "/etc/ssl/ca/client-ca.crt"
|
|
||||||
verify_depth: 1
|
|
||||||
crl: "/etc/ssl/ca/client-ca.crl"
|
|
||||||
http2:
|
|
||||||
enabled: true
|
|
||||||
max_concurrent_streams: 128
|
|
||||||
max_header_list_size: 16384
|
|
||||||
idle_timeout: 90s
|
|
||||||
push_enabled: false
|
|
||||||
h2c_enabled: false
|
|
||||||
graceful_shutdown_timeout: 30s
|
|
||||||
hsts:
|
|
||||||
max_age: 31536000
|
|
||||||
include_sub_domains: true
|
|
||||||
preload: false
|
|
||||||
|
|
||||||
security:
|
|
||||||
access:
|
|
||||||
allow: ["192.168.1.0/24", "10.0.0.0/8"]
|
|
||||||
deny: []
|
|
||||||
default: "deny"
|
|
||||||
trusted_proxies: ["172.16.0.0/16"]
|
|
||||||
geoip:
|
|
||||||
enabled: false
|
|
||||||
database: "/var/lib/GeoIP/GeoIP2-Country.mmdb"
|
|
||||||
allow_countries: ["CN", "US"]
|
|
||||||
deny_countries: []
|
|
||||||
default: "deny"
|
|
||||||
cache_size: 10000
|
|
||||||
cache_ttl: 1h
|
|
||||||
private_ip_behavior: "allow"
|
|
||||||
rate_limit:
|
|
||||||
request_rate: 100
|
|
||||||
burst: 200
|
|
||||||
conn_limit: 50
|
|
||||||
algorithm: "token_bucket" # token_bucket/sliding_window
|
|
||||||
key: "ip"
|
|
||||||
sliding_window_mode: "approximate" # exact/approximate
|
|
||||||
sliding_window: 60
|
|
||||||
auth:
|
|
||||||
type: "basic"
|
|
||||||
require_tls: true
|
|
||||||
algorithm: "bcrypt" # bcrypt/argon2id
|
|
||||||
realm: "Secure Area"
|
|
||||||
min_password_length: 8
|
|
||||||
users:
|
|
||||||
- name: "admin"
|
|
||||||
password: "$2y$10$N9qo8uLOickgx2ZMRZoMy..."
|
|
||||||
auth_request:
|
|
||||||
enabled: false
|
|
||||||
uri: "/auth"
|
|
||||||
method: "GET"
|
|
||||||
auth_timeout: 5s
|
|
||||||
forward_headers: ["Cookie", "Authorization"]
|
|
||||||
headers:
|
|
||||||
X-Original-Uri: "$request_uri"
|
|
||||||
X-Original-Host: "$host"
|
|
||||||
headers:
|
|
||||||
x_frame_options: "DENY"
|
|
||||||
x_content_type_options: "nosniff"
|
|
||||||
content_security_policy: "default-src 'self'"
|
|
||||||
referrer_policy: "strict-origin-when-cross-origin"
|
|
||||||
permissions_policy: "geolocation=(), microphone=()"
|
|
||||||
error_page:
|
|
||||||
pages:
|
|
||||||
404: "/var/www/errors/404.html"
|
|
||||||
500: "/var/www/errors/500.html"
|
|
||||||
default: "/var/www/errors/error.html"
|
|
||||||
response_code: 0 # 覆盖响应状态码
|
|
||||||
|
|
||||||
rewrite:
|
|
||||||
- pattern: "^/old/(.*)$"
|
|
||||||
replacement: "/new/$1"
|
|
||||||
flag: "permanent"
|
|
||||||
- pattern: "^/api/v1/(.*)$"
|
|
||||||
replacement: "/v1/$1"
|
|
||||||
flag: "last"
|
|
||||||
|
|
||||||
compression:
|
|
||||||
type: "gzip" # gzip/brotli/both
|
|
||||||
level: 6
|
|
||||||
min_size: 1024
|
|
||||||
types: ["text/html", "text/css", "application/javascript", "application/json"]
|
|
||||||
gzip_static: true
|
|
||||||
gzip_static_extensions: [".gz", ".br"]
|
|
||||||
|
|
||||||
lua:
|
|
||||||
enabled: true
|
|
||||||
scripts:
|
|
||||||
- path: "/etc/lolly/lua/rewrite.lua"
|
|
||||||
phase: "rewrite"
|
|
||||||
timeout: 30s
|
|
||||||
enabled: true
|
|
||||||
- path: "/etc/lolly/lua/access.lua"
|
|
||||||
phase: "access"
|
|
||||||
timeout: 30s
|
|
||||||
- path: "/etc/lolly/lua/content.lua"
|
|
||||||
phase: "content"
|
|
||||||
timeout: 30s
|
|
||||||
- path: "/etc/lolly/lua/log.lua"
|
|
||||||
phase: "log"
|
|
||||||
timeout: 30s
|
|
||||||
global_settings:
|
|
||||||
max_concurrent_coroutines: 1000
|
|
||||||
coroutine_timeout: 30s
|
|
||||||
code_cache_size: 1000
|
|
||||||
max_execution_time: 30s
|
|
||||||
coroutine_stack_size: 64
|
|
||||||
coroutine_pool_warmup: 4
|
|
||||||
enable_file_watch: true
|
|
||||||
minimize_stack_memory: true
|
|
||||||
|
|
||||||
limit_rate:
|
|
||||||
rate: 1048576 # 1MB/s
|
|
||||||
burst: 524288 # 512KB 突发
|
|
||||||
large_file_threshold: 10485760 # 10MB
|
|
||||||
large_file_strategy: "skip" # skip/coarse
|
|
||||||
|
|
||||||
types:
|
|
||||||
default_type: "application/octet-stream"
|
|
||||||
map:
|
|
||||||
".html": "text/html"
|
|
||||||
".css": "text/css"
|
|
||||||
".js": "application/javascript"
|
|
||||||
".json": "application/json"
|
|
||||||
|
|
||||||
unix_socket:
|
|
||||||
mode: 0666
|
|
||||||
user: "www-data"
|
|
||||||
group: "www-data"
|
|
||||||
|
|
||||||
cache_api:
|
|
||||||
enabled: false
|
|
||||||
path: "/_cache/purge"
|
|
||||||
allow: ["127.0.0.1", "10.0.0.0/8"]
|
|
||||||
auth:
|
|
||||||
type: "token"
|
|
||||||
token: "${CACHE_API_TOKEN}"
|
|
||||||
|
|
||||||
http3:
|
|
||||||
enabled: true
|
|
||||||
listen: ":443"
|
|
||||||
max_streams: 1000
|
|
||||||
idle_timeout: 30s
|
|
||||||
enable_0rtt: true
|
|
||||||
|
|
||||||
stream:
|
|
||||||
- listen: ":3306"
|
|
||||||
protocol: "tcp"
|
|
||||||
upstream:
|
|
||||||
targets:
|
|
||||||
- addr: "mysql1:3306"
|
|
||||||
weight: 3
|
|
||||||
- addr: "mysql2:3306"
|
|
||||||
weight: 1
|
|
||||||
load_balance: "round_robin"
|
|
||||||
ssl:
|
|
||||||
enabled: false
|
|
||||||
cert: "/etc/ssl/stream.crt"
|
|
||||||
key: "/etc/ssl/stream.key"
|
|
||||||
client_ca: ""
|
|
||||||
protocols: ["TLSv1.2", "TLSv1.3"]
|
|
||||||
ciphers: []
|
|
||||||
verify_depth: 1
|
|
||||||
proxy_ssl:
|
|
||||||
enabled: false
|
|
||||||
trusted_ca: "/etc/ssl/ca.crt"
|
|
||||||
server_name: "mysql.internal"
|
|
||||||
cert: ""
|
|
||||||
key: ""
|
|
||||||
protocols: ["TLSv1.2", "TLSv1.3"]
|
|
||||||
verify: true
|
|
||||||
session_reuse: true
|
|
||||||
|
|
||||||
resolver:
|
|
||||||
enabled: false
|
|
||||||
addresses:
|
|
||||||
- "8.8.8.8:53"
|
|
||||||
- "8.8.4.4:53"
|
|
||||||
valid: 30s # 缓存 TTL
|
|
||||||
timeout: 5s
|
|
||||||
ipv4: true
|
|
||||||
ipv6: false
|
|
||||||
cache_size: 1024
|
|
||||||
|
|
||||||
cache_path:
|
|
||||||
path: "/var/cache/lolly"
|
|
||||||
levels: "1:2"
|
|
||||||
max_size: 1073741824 # 1GB
|
|
||||||
inactive: 60m
|
|
||||||
purger: true
|
|
||||||
purger_interval: 1m
|
|
||||||
l1_max_entries: 10000
|
|
||||||
l1_max_size: 67108864 # 64MB
|
|
||||||
promote_threshold: 3
|
|
||||||
|
|
||||||
logging:
|
|
||||||
format: "json" # text/json
|
|
||||||
access:
|
|
||||||
path: "/var/log/lolly/access.log"
|
|
||||||
format: "combined"
|
|
||||||
error:
|
|
||||||
path: "/var/log/lolly/error.log"
|
|
||||||
level: "info" # debug/info/warn/error
|
|
||||||
|
|
||||||
performance:
|
|
||||||
goroutine_pool:
|
|
||||||
enabled: true
|
|
||||||
max_workers: 10000
|
|
||||||
min_workers: 100
|
|
||||||
idle_timeout: 60s
|
|
||||||
file_cache:
|
|
||||||
max_entries: 50000
|
|
||||||
max_size: 268435456 # 256MB
|
|
||||||
inactive: 60s
|
|
||||||
transport:
|
|
||||||
idle_conn_timeout: 90s
|
|
||||||
max_conns_per_host: 500
|
|
||||||
|
|
||||||
monitoring:
|
|
||||||
status:
|
|
||||||
enabled: true
|
|
||||||
path: "/status"
|
|
||||||
format: "json" # json/text/html/prometheus
|
|
||||||
allow: ["127.0.0.1", "10.0.0.0/8"]
|
|
||||||
pprof:
|
|
||||||
enabled: false
|
|
||||||
path: "/debug/pprof"
|
|
||||||
allow: ["127.0.0.1"]
|
|
||||||
|
|
||||||
shutdown:
|
|
||||||
graceful_timeout: 30s # SIGQUIT 优雅停止
|
|
||||||
fast_timeout: 5s # SIGINT/SIGTERM 快速停止
|
|
||||||
|
|
||||||
include:
|
|
||||||
- path: "conf.d/*.yaml" # glob 模式展开
|
|
||||||
```
|
|
||||||
|
|
||||||
完整配置说明请参考 `config.example.yaml` 和源码 `internal/config/config.go`。
|
|
||||||
|
|
||||||
## 架构
|
## 架构
|
||||||
|
|
||||||
|
### 请求处理流程
|
||||||
|
|
||||||
```
|
```
|
||||||
+------------------+
|
+------------------+
|
||||||
| Client |
|
| Client |
|
||||||
@ -565,6 +184,84 @@ include:
|
|||||||
+---------+ +-----------+ +---------+
|
+---------+ +-----------+ +---------+
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 核心模块依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
main.go (CLI 入口)
|
||||||
|
│ app.Run()
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ App (app/) │
|
||||||
|
│ 生命周期: loadConfig → initServer → Start → handleSignal │
|
||||||
|
│ 组合: Server + HTTP3Server + HTTP2Server + StreamServer │
|
||||||
|
└─────────────────────────────┬───────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────────────┼───────────────────────┐
|
||||||
|
│ │ │
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌───────────┐ ┌──────────────┐ ┌───────────┐
|
||||||
|
│ config │ │ server │ │ upgrade │
|
||||||
|
│ (YAML) │ │ (HTTP) │ │ (热升级) │
|
||||||
|
│ 18 文件 │ │ GoroutinePool│ │ FD 继承 │
|
||||||
|
└─────┬─────┘ └─────┬────────┘ └───────────┘
|
||||||
|
│ │
|
||||||
|
│ ▼
|
||||||
|
│ ┌───────────────────────────────────────┐
|
||||||
|
│ │ Middleware Chain │
|
||||||
|
│ │ AccessLog → Security → Compression │
|
||||||
|
│ │ → BodyLimit → Rewrite → Lua │
|
||||||
|
│ └─────┬─────────────────────────────────┘
|
||||||
|
│ │
|
||||||
|
│ ▼
|
||||||
|
│ ┌───────────┐
|
||||||
|
│ │ matcher │
|
||||||
|
│ │ (Location)│
|
||||||
|
│ │ Radix Tree│
|
||||||
|
│ └─────┬─────┘
|
||||||
|
│ │
|
||||||
|
│ ┌───────────┴───────────┐
|
||||||
|
│ │ │
|
||||||
|
│ ▼ ▼
|
||||||
|
│ ┌───────────┐ ┌───────────┐
|
||||||
|
│ │ handler │ │ proxy │
|
||||||
|
│ │ (Static) │ │ (反向代理)│
|
||||||
|
│ │ sendfile │ │ HostClient│
|
||||||
|
│ └─────┬─────┘ └─────┬─────┘
|
||||||
|
│ │ │
|
||||||
|
│ ▼ ▼
|
||||||
|
│ ┌───────────┐ ┌───────────┐
|
||||||
|
│ │ cache │ │loadbalance│
|
||||||
|
│ │ FileCache │ │ 7 种算法 │
|
||||||
|
│ └───────────┘ └─────┬─────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌───────────┐ ┌───────────┐
|
||||||
|
│ logging │ │ resolver │
|
||||||
|
│ (zerolog) │ │ (DNS) │
|
||||||
|
└───────────┘ └───────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 设计模式应用
|
||||||
|
|
||||||
|
| 模式 | 应用位置 | 说明 |
|
||||||
|
| ---------- | ------------------------------------- | -------------------------------------------- |
|
||||||
|
| 策略模式 | `loadbalance/` | 7 种 LB 算法可插拔切换,实现 `Balancer` 接口 |
|
||||||
|
| 责任链模式 | `middleware/` | 中间件逆序包装链式调用,洋葱模型 |
|
||||||
|
| 工厂模式 | `proxy/proxy.go` | `createBalancerByName` 根据名称创建均衡器 |
|
||||||
|
| 对象池模式 | `proxy/headersPool`, `cache/`, `lua/` | `sync.Pool` 复用高频对象 |
|
||||||
|
| 观察者模式 | `server/upgrade.go` | 信号监听与处理,优雅升级触发 |
|
||||||
|
|
||||||
|
### 性能优化设计
|
||||||
|
|
||||||
|
| 优化点 | 实现方式 | 收益 |
|
||||||
|
| -------------- | -------------------- | ---------------------------- |
|
||||||
|
| fasthttp | 替代 net/http | 零分配请求处理,10x 性能提升 |
|
||||||
|
| sendfile | Linux 内核级传输 | 大文件零拷贝,减少 CPU 开销 |
|
||||||
|
| sync.Pool | headers/buffers 复用 | 减少 GC 压力 |
|
||||||
|
| Goroutine Pool | worker 池复用 | 避免 goroutine 爆炸 |
|
||||||
|
| 原子计数器 | `atomic.AddUint64` | 负载均衡无锁选择 |
|
||||||
|
| LRU 缓存 | GeoIP/FileCache | 减少重复查询/读取 |
|
||||||
|
|
||||||
### 目录结构
|
### 目录结构
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -729,6 +426,7 @@ type Balancer interface {
|
|||||||
#### 代理缓存
|
#### 代理缓存
|
||||||
|
|
||||||
支持:
|
支持:
|
||||||
|
|
||||||
- **缓存锁**(cache_lock)- 防止缓存击穿,同一缓存键只允许一个请求访问后端
|
- **缓存锁**(cache_lock)- 防止缓存击穿,同一缓存键只允许一个请求访问后端
|
||||||
- **过期复用**(stale-while-revalidate)- 允许在后台刷新时返回过期缓存
|
- **过期复用**(stale-while-revalidate)- 允许在后台刷新时返回过期缓存
|
||||||
- **后台刷新** - 异步更新缓存,不影响请求响应时间
|
- **后台刷新** - 异步更新缓存,不影响请求响应时间
|
||||||
@ -745,14 +443,14 @@ Lolly 提供基于 gopher-lua 的 Lua 脚本扩展能力,支持 nginx-lua 兼
|
|||||||
请求到达 → rewrite → access → content → header_filter → body_filter → log → 响应返回
|
请求到达 → rewrite → access → content → header_filter → body_filter → log → 响应返回
|
||||||
```
|
```
|
||||||
|
|
||||||
| 阶段 | 说明 | 可用操作 |
|
| 阶段 | 说明 | 可用操作 |
|
||||||
|------|------|----------|
|
| ------------- | ---------- | ------------------------------------- |
|
||||||
| rewrite | URL 重写 | ngx.req.set_uri, ngx.req.set_uri_args |
|
| rewrite | URL 重写 | ngx.req.set_uri, ngx.req.set_uri_args |
|
||||||
| access | 访问控制 | ngx.exit, ngx.req.get_headers |
|
| access | 访问控制 | ngx.exit, ngx.req.get_headers |
|
||||||
| content | 内容生成 | ngx.say, ngx.print, ngx.exit |
|
| content | 内容生成 | ngx.say, ngx.print, ngx.exit |
|
||||||
| header_filter | 响应头修改 | ngx.header[key] = value |
|
| header_filter | 响应头修改 | ngx.header[key] = value |
|
||||||
| body_filter | 响应体修改 | ngx.arg[1] 操作响应体 |
|
| body_filter | 响应体修改 | ngx.arg[1] 操作响应体 |
|
||||||
| log | 日志记录 | ngx.log, 记录请求信息 |
|
| log | 日志记录 | ngx.log, 记录请求信息 |
|
||||||
|
|
||||||
### 支持的 API
|
### 支持的 API
|
||||||
|
|
||||||
@ -908,6 +606,7 @@ ngx.exit(200)
|
|||||||
### 安全限制
|
### 安全限制
|
||||||
|
|
||||||
默认沙箱模式禁用以下 Lua 库:
|
默认沙箱模式禁用以下 Lua 库:
|
||||||
|
|
||||||
- `os`(除 os.time)
|
- `os`(除 os.time)
|
||||||
- `io`
|
- `io`
|
||||||
- `file`
|
- `file`
|
||||||
@ -953,7 +652,7 @@ docker run -d \
|
|||||||
### Docker Compose
|
### Docker Compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
lolly:
|
lolly:
|
||||||
image: lolly:latest
|
image: lolly:latest
|
||||||
@ -1028,8 +727,8 @@ sudo systemctl status lolly # 查看状态
|
|||||||
{
|
{
|
||||||
"name": "backend1",
|
"name": "backend1",
|
||||||
"targets": [
|
"targets": [
|
||||||
{"url": "http://10.0.0.1:8080", "healthy": true, "latency_ms": 5},
|
{ "url": "http://10.0.0.1:8080", "healthy": true, "latency_ms": 5 },
|
||||||
{"url": "http://10.0.0.2:8080", "healthy": true, "latency_ms": 8}
|
{ "url": "http://10.0.0.2:8080", "healthy": true, "latency_ms": 8 }
|
||||||
],
|
],
|
||||||
"healthy_count": 2,
|
"healthy_count": 2,
|
||||||
"unhealthy_count": 0,
|
"unhealthy_count": 0,
|
||||||
@ -1078,13 +777,13 @@ sudo systemctl status lolly # 查看状态
|
|||||||
|
|
||||||
## 信号处理
|
## 信号处理
|
||||||
|
|
||||||
| 信号 | 行为 |
|
| 信号 | 行为 |
|
||||||
|------|------|
|
| --------------- | ---------------------- |
|
||||||
| SIGTERM, SIGINT | 快速停止 |
|
| SIGTERM, SIGINT | 快速停止 |
|
||||||
| SIGQUIT | 优雅停止,等待请求完成 |
|
| SIGQUIT | 优雅停止,等待请求完成 |
|
||||||
| SIGHUP | 重载配置 |
|
| SIGHUP | 重载配置 |
|
||||||
| SIGUSR1 | 重新打开日志文件 |
|
| SIGUSR1 | 重新打开日志文件 |
|
||||||
| SIGUSR2 | 热升级 |
|
| SIGUSR2 | 热升级 |
|
||||||
|
|
||||||
## 性能
|
## 性能
|
||||||
|
|
||||||
@ -1100,13 +799,13 @@ sudo systemctl status lolly # 查看状态
|
|||||||
|
|
||||||
在 4 核 8GB 服务器上测试(使用 `internal/benchmark/tools/loadgen`):
|
在 4 核 8GB 服务器上测试(使用 `internal/benchmark/tools/loadgen`):
|
||||||
|
|
||||||
| 场景 | RPS | 平均延迟 | P99 延迟 |
|
| 场景 | RPS | 平均延迟 | P99 延迟 |
|
||||||
|------|-----|----------|----------|
|
| ------------------ | ----------- | -------- | -------- |
|
||||||
| 静态文件(1KB) | 120,000+ | 0.8ms | 3ms |
|
| 静态文件(1KB) | 120,000+ | 0.8ms | 3ms |
|
||||||
| 静态文件(100KB) | 15,000+ | 6ms | 25ms |
|
| 静态文件(100KB) | 15,000+ | 6ms | 25ms |
|
||||||
| 反向代理(无缓存) | 80,000+ | 1.2ms | 5ms |
|
| 反向代理(无缓存) | 80,000+ | 1.2ms | 5ms |
|
||||||
| 反向代理(有缓存) | 150,000+ | 0.6ms | 2ms |
|
| 反向代理(有缓存) | 150,000+ | 0.6ms | 2ms |
|
||||||
| WebSocket 连接 | 50,000 连接 | - | - |
|
| WebSocket 连接 | 50,000 连接 | - | - |
|
||||||
|
|
||||||
### PGO 优化
|
### PGO 优化
|
||||||
|
|
||||||
@ -1128,47 +827,49 @@ make build-pgo
|
|||||||
performance:
|
performance:
|
||||||
goroutine_pool:
|
goroutine_pool:
|
||||||
enabled: true
|
enabled: true
|
||||||
max_workers: 10000 # 根据 CPU 核数调整
|
max_workers: 10000 # 根据 CPU 核数调整
|
||||||
min_workers: 100
|
min_workers: 100
|
||||||
idle_timeout: 60s
|
idle_timeout: 60s
|
||||||
file_cache:
|
file_cache:
|
||||||
max_entries: 50000 # 根据内存调整
|
max_entries: 50000 # 根据内存调整
|
||||||
max_size: 268435456 # 256MB
|
max_size: 268435456 # 256MB
|
||||||
transport:
|
transport:
|
||||||
idle_conn_timeout: 90s
|
idle_conn_timeout: 90s
|
||||||
max_conns_per_host: 500 # 根据后端容量调整
|
max_conns_per_host: 500 # 根据后端容量调整
|
||||||
```
|
```
|
||||||
|
|
||||||
容量规划建议:
|
容量规划建议:
|
||||||
|
|
||||||
| 流量级别 | max_workers | file_cache.max_size | max_conns_per_host |
|
| 流量级别 | max_workers | file_cache.max_size | max_conns_per_host |
|
||||||
|----------|-------------|---------------------|--------------------|
|
| ---------------------- | ----------- | ------------------- | ------------------ |
|
||||||
| 低流量(<10K RPS) | 500 | 64MB | 100 |
|
| 低流量(<10K RPS) | 500 | 64MB | 100 |
|
||||||
| 中流量(10K-100K RPS) | 5000 | 256MB | 300 |
|
| 中流量(10K-100K RPS) | 5000 | 256MB | 300 |
|
||||||
| 高流量(>100K RPS) | 10000 | 512MB | 500 |
|
| 高流量(>100K RPS) | 10000 | 512MB | 500 |
|
||||||
|
|
||||||
## 与 NGINX 对比
|
## 与 NGINX 对比
|
||||||
|
|
||||||
| 特性 | Lolly | NGINX |
|
| 特性 | Lolly | NGINX |
|
||||||
|------|-------|-------|
|
| -------------- | --------------- | ------------ |
|
||||||
| HTTP/3 | 支持(quic-go) | 1.25+ 支持 |
|
| HTTP/3 | 支持(quic-go) | 1.25+ 支持 |
|
||||||
| HTTP/2 | 支持(需 TLS) | 支持 |
|
| HTTP/2 | 支持(需 TLS) | 支持 |
|
||||||
| 配置格式 | YAML | 自定义 DSL |
|
| 配置格式 | YAML | 自定义 DSL |
|
||||||
| 热升级 | 支持 | 支持 |
|
| 热升级 | 支持 | 支持 |
|
||||||
| 扩展方式 | Go 代码/Lua | C 模块/Lua |
|
| 扩展方式 | Go 代码/Lua | C 模块/Lua |
|
||||||
| 部署方式 | 单二进制 | 需安装 |
|
| 部署方式 | 单二进制 | 需安装 |
|
||||||
| TCP/UDP Stream | 支持 | 支持 |
|
| TCP/UDP Stream | 支持 | 支持 |
|
||||||
| 代理缓存 | 支持(带锁) | 支持 |
|
| 代理缓存 | 支持(带锁) | 支持 |
|
||||||
| GeoIP | MaxMind GeoIP2 | GeoIP 模块 |
|
| GeoIP | MaxMind GeoIP2 | GeoIP 模块 |
|
||||||
| 一致性哈希 | 支持(内置) | 需第三方模块 |
|
| 一致性哈希 | 支持(内置) | 需第三方模块 |
|
||||||
|
|
||||||
**Lolly 优势**:
|
**Lolly 优势**:
|
||||||
|
|
||||||
- YAML 配置更易读、易于解析和生成
|
- YAML 配置更易读、易于解析和生成
|
||||||
- 单二进制部署,无依赖
|
- 单二进制部署,无依赖
|
||||||
- Go 原生扩展,无需 C 编译环境
|
- Go 原生扩展,无需 C 编译环境
|
||||||
- 现代 Go 库生态系统
|
- 现代 Go 库生态系统
|
||||||
|
|
||||||
**NGINX 优势**:
|
**NGINX 优势**:
|
||||||
|
|
||||||
- 经过大规模生产验证
|
- 经过大规模生产验证
|
||||||
- 丰富的第三方模块生态
|
- 丰富的第三方模块生态
|
||||||
- 更完整的功能集(邮件代理等)
|
- 更完整的功能集(邮件代理等)
|
||||||
@ -1177,106 +878,24 @@ performance:
|
|||||||
|
|
||||||
常见配置转换示例:
|
常见配置转换示例:
|
||||||
|
|
||||||
| NGINX | Lolly |
|
| NGINX | Lolly |
|
||||||
|-------|-------|
|
| ------------------------------------------ | ---------------------------------------------- |
|
||||||
| `listen 80;` | `listen: ":80"` |
|
| `listen 80;` | `listen: ":80"` |
|
||||||
| `root /var/www/html;` | `root: "/var/www/html"` |
|
| `root /var/www/html;` | `root: "/var/www/html"` |
|
||||||
| `proxy_pass http://backend;` | `url: "http://backend"` |
|
| `proxy_pass http://backend;` | `url: "http://backend"` |
|
||||||
| `proxy_set_header X-Real-IP $remote_addr;` | `set_request: { X-Real-IP: "$remote_addr" }` |
|
| `proxy_set_header X-Real-IP $remote_addr;` | `set_request: { X-Real-IP: "$remote_addr" }` |
|
||||||
| `limit_req zone=one burst=10;` | `rate_limit: { request_rate: 100, burst: 10 }` |
|
| `limit_req zone=one burst=10;` | `rate_limit: { request_rate: 100, burst: 10 }` |
|
||||||
| `ssl_certificate /path/cert.pem;` | `cert: "/path/cert.pem"` |
|
| `ssl_certificate /path/cert.pem;` | `cert: "/path/cert.pem"` |
|
||||||
|
|
||||||
迁移注意事项:
|
迁移注意事项:
|
||||||
|
|
||||||
1. 变量系统语法相同($remote_addr 等)
|
1. 变量系统语法相同($remote_addr 等)
|
||||||
2. try_files 语法兼容
|
2. try_files 语法兼容
|
||||||
3. Lua API 与 OpenResty 高度兼容
|
3. Lua API 与 OpenResty 高度兼容
|
||||||
4. 健康检查配置略有差异
|
4. 健康检查配置略有差异
|
||||||
|
|
||||||
## 生产清单
|
|
||||||
|
|
||||||
### 安全加固
|
|
||||||
|
|
||||||
- [ ] 启用 TLS 1.2+,禁用 TLS 1.0/1.1
|
|
||||||
- [ ] 配置安全头部(HSTS、X-Frame-Options、CSP)
|
|
||||||
- [ ] 启用访问控制,限制管理端点访问
|
|
||||||
- [ ] 配置速率限制,防止 DDoS
|
|
||||||
- [ ] 启用请求体大小限制
|
|
||||||
- [ ] 配置 GeoIP 过滤(如需地区限制)
|
|
||||||
- [ ] 定期更新 TLS Session Tickets 密钥
|
|
||||||
|
|
||||||
### 配置验证
|
|
||||||
|
|
||||||
- [ ] 检查 `ssl.protocols` 仅包含 TLSv1.2/1.3
|
|
||||||
- [ ] 检查 `security.headers` 配置完整
|
|
||||||
- [ ] 检查 `monitoring.status.allow` 仅允许可信 IP
|
|
||||||
- [ ] 检查 `max_conns_per_ip` 合理限制
|
|
||||||
- [ ] 检查 `client_max_body_size` 符合业务需求
|
|
||||||
|
|
||||||
### 性能优化
|
|
||||||
|
|
||||||
- [ ] 启用 Goroutine 池
|
|
||||||
- [ ] 配置文件缓存
|
|
||||||
- [ ] 启用代理缓存(如适用)
|
|
||||||
- [ ] 配置连接池参数
|
|
||||||
- [ ] 调整文件缓存大小匹配可用内存
|
|
||||||
|
|
||||||
### 运维准备
|
|
||||||
|
|
||||||
- [ ] 配置日志轮转
|
|
||||||
- [ ] 测试热升级流程
|
|
||||||
- [ ] 测试优雅关闭超时
|
|
||||||
- [ ] 配置 systemd 服务
|
|
||||||
- [ ] 设置监控告警阈值
|
|
||||||
|
|
||||||
## 故障排除
|
## 故障排除
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
**启动失败:端口被占用**
|
|
||||||
```bash
|
|
||||||
# 检查端口占用
|
|
||||||
sudo lsof -i :80
|
|
||||||
sudo lsof -i :443
|
|
||||||
|
|
||||||
# 或更改监听端口
|
|
||||||
listen: ":8080"
|
|
||||||
```
|
|
||||||
|
|
||||||
**TLS 证书加载失败**
|
|
||||||
```bash
|
|
||||||
# 检查证书文件权限
|
|
||||||
ls -la /etc/ssl/certs/server.crt
|
|
||||||
ls -la /etc/ssl/private/server.key
|
|
||||||
|
|
||||||
# 确保证书格式正确(PEM)
|
|
||||||
openssl x509 -in /etc/ssl/certs/server.crt -text -noout
|
|
||||||
```
|
|
||||||
|
|
||||||
**代理超时**
|
|
||||||
```yaml
|
|
||||||
# 增加超时时间
|
|
||||||
timeout:
|
|
||||||
connect: 10s
|
|
||||||
read: 60s
|
|
||||||
write: 60s
|
|
||||||
```
|
|
||||||
|
|
||||||
**内存占用过高**
|
|
||||||
```yaml
|
|
||||||
# 减少缓存大小
|
|
||||||
file_cache:
|
|
||||||
max_entries: 10000
|
|
||||||
max_size: 67108864 # 64MB
|
|
||||||
```
|
|
||||||
|
|
||||||
**Lua 脚本超时**
|
|
||||||
```yaml
|
|
||||||
# 增加执行超时
|
|
||||||
lua:
|
|
||||||
timeout: 60s
|
|
||||||
max_concurrent: 500
|
|
||||||
```
|
|
||||||
|
|
||||||
### 调试模式
|
### 调试模式
|
||||||
|
|
||||||
启用详细日志:
|
启用详细日志:
|
||||||
@ -1356,23 +975,23 @@ make lint
|
|||||||
|
|
||||||
## 依赖
|
## 依赖
|
||||||
|
|
||||||
| 库 | 版本 | 用途 |
|
| 库 | 版本 | 用途 |
|
||||||
|------|---------|------|
|
| ------------------------------------------------------------------------ | ------- | ---------------- |
|
||||||
| [fasthttp](https://github.com/valyala/fasthttp) | v1.70.0 | HTTP 服务器核心 |
|
| [fasthttp](https://github.com/valyala/fasthttp) | v1.70.0 | HTTP 服务器核心 |
|
||||||
| [fasthttp/router](https://github.com/fasthttp/router) | v1.5.4 | HTTP 路由器 |
|
| [fasthttp/router](https://github.com/fasthttp/router) | v1.5.4 | HTTP 路由器 |
|
||||||
| [quic-go](https://github.com/quic-go/quic-go) | v0.59.0 | QUIC/HTTP/3 实现 |
|
| [quic-go](https://github.com/quic-go/quic-go) | v0.59.0 | QUIC/HTTP/3 实现 |
|
||||||
| [zerolog](https://github.com/rs/zerolog) | v1.35.0 | 零分配 JSON 日志 |
|
| [zerolog](https://github.com/rs/zerolog) | v1.35.0 | 零分配 JSON 日志 |
|
||||||
| [gopher-lua](https://github.com/yuin/gopher-lua) | v1.1.2 | Lua 脚本引擎 |
|
| [gopher-lua](https://github.com/yuin/gopher-lua) | v1.1.2 | Lua 脚本引擎 |
|
||||||
| [klauspost/compress](https://github.com/klauspost/compress) | v1.18.5 | Gzip/Brotli 压缩 |
|
| [klauspost/compress](https://github.com/klauspost/compress) | v1.18.5 | Gzip/Brotli 压缩 |
|
||||||
| [brotli](https://github.com/andybalholm/brotli) | v1.2.1 | Brotli 压缩编码 |
|
| [brotli](https://github.com/andybalholm/brotli) | v1.2.1 | Brotli 压缩编码 |
|
||||||
| [geoip2-golang](https://github.com/oschwald/geoip2-golang) | v1.13.0 | GeoIP 查询 |
|
| [geoip2-golang](https://github.com/oschwald/geoip2-golang) | v1.13.0 | GeoIP 查询 |
|
||||||
| [golang-lru/v2](https://github.com/hashicorp/golang-lru) | v2.0.7 | LRU 缓存 |
|
| [golang-lru/v2](https://github.com/hashicorp/golang-lru) | v2.0.7 | LRU 缓存 |
|
||||||
| [uuid](https://github.com/google/uuid) | v1.6.0 | UUID 生成 |
|
| [uuid](https://github.com/google/uuid) | v1.6.0 | UUID 生成 |
|
||||||
| [testcontainers-go](https://github.com/testcontainers/testcontainers-go) | v0.42.0 | 集成测试容器 |
|
| [testcontainers-go](https://github.com/testcontainers/testcontainers-go) | v0.42.0 | 集成测试容器 |
|
||||||
| [testify](https://github.com/stretchr/testify) | v1.11.1 | 测试断言 |
|
| [testify](https://github.com/stretchr/testify) | v1.11.1 | 测试断言 |
|
||||||
| [golang.org/x/crypto](https://golang.org/x/crypto) | v0.50.0 | 加密工具 |
|
| [golang.org/x/crypto](https://golang.org/x/crypto) | v0.50.0 | 加密工具 |
|
||||||
| [golang.org/x/net](https://golang.org/x/net) | v0.53.0 | 网络扩展 |
|
| [golang.org/x/net](https://golang.org/x/net) | v0.53.0 | 网络扩展 |
|
||||||
| [yaml.v3](https://gopkg.in/yaml.v3) | v3.0.1 | YAML 配置解析 |
|
| [yaml.v3](https://gopkg.in/yaml.v3) | v3.0.1 | YAML 配置解析 |
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
@ -1381,3 +1000,4 @@ MIT License
|
|||||||
## 作者
|
## 作者
|
||||||
|
|
||||||
xfy
|
xfy
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user