- Add code statistics: 174 source files, 158 test files, ~136K lines - Document proxy cache and Lua script extension features - Expand internal directory structure with detailed file descriptions - Update Go version requirement to 1.26 - Add cache purge API documentation - Enhance hot upgrade docs with Unix Socket inheritance support - Update status endpoint with cache hit rate and upstream health Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1035 lines
38 KiB
Markdown
1035 lines
38 KiB
Markdown
# Lolly
|
||
|
||
[](https://golang.org)
|
||
[](LICENSE)
|
||
|
||
高性能 HTTP 服务器与反向代理,使用 Go 语言编写。
|
||
|
||
基于 [fasthttp](https://github.com/valyala/fasthttp) 构建,提供比标准 net/http 更高的性能。支持 HTTP/3 (QUIC)、WebSocket、虚拟主机、多种负载均衡算法、故障转移、代理缓存、Lua 脚本扩展,以及完整的安全与性能优化特性。
|
||
|
||
> **代码统计**:174 个源文件 | 158 个测试文件 | ~136,000 行 Go 代码 | 测试覆盖率 >80%
|
||
|
||
## 特性
|
||
|
||
### 核心功能
|
||
|
||
- **静态文件服务** - 零拷贝传输(sendfile)、文件缓存、预压缩支持、try_files 配置、符号链接安全检查、ETag 和 304 Not Modified 支持
|
||
- **反向代理** - 完整的代理功能,支持请求头/响应头修改、超时控制、故障转移(next_upstream)、Location/Refresh 头改写
|
||
- **HTTP/3 (QUIC)** - 基于 quic-go,支持 0-RTT 连接
|
||
- **WebSocket** - 完整的 WebSocket 代理支持
|
||
- **虚拟主机** - 单进程支持多域名独立配置,server_name 支持通配符和正则匹配
|
||
- **多服务器模式** - 单配置文件支持多个独立 server 实例
|
||
- **Location 匹配** - nginx 风格的精确/前缀/正则匹配引擎
|
||
- **Unix Socket** - 支持 Unix socket 监听
|
||
- **配置引入** - include 指令支持配置拆分
|
||
- **TCP/UDP Stream** - 四层代理,支持 MySQL、Redis 等服务
|
||
- **Lua 脚本** - 基于 gopher-lua 的可编程扩展,支持 nginx-lua 兼容 API(ngx.var/ngx.ctx/ngx.req/ngx.resp/ngx.timer/ngx.location.capture/ngx.shared.DICT)
|
||
- **GeoIP 过滤** - 基于 MaxMind GeoIP2 的国家/地区访问控制
|
||
- **自定义错误页面** - 支持为特定状态码配置自定义错误页面
|
||
- **nginx 配置导入** - 支持将 nginx 配置文件转换为 lolly YAML 配置
|
||
|
||
### 负载均衡
|
||
|
||
| 算法 | 说明 |
|
||
| -------------------- | -------------------------------------------------------- |
|
||
| Round Robin | 轮询,均匀分配 |
|
||
| Weighted Round Robin | 加权轮询,按权重分配 |
|
||
| Least Connections | 最少连接,选择活跃连接最少的目标 |
|
||
| IP Hash | IP 哈希,同一客户端始终路由到同一目标 |
|
||
| Consistent Hash | 一致性哈希,支持虚拟节点(默认 150),最小化节点变更影响 |
|
||
| Random | Power of Two Choices,随机选择两个后比较,简单高效 |
|
||
|
||
### 故障转移
|
||
|
||
支持 `next_upstream` 配置,当后端返回特定错误状态码(502/503/504)或连接失败时,自动重试下一个可用后端:
|
||
|
||
```yaml
|
||
proxy:
|
||
- path: "/api"
|
||
next_upstream:
|
||
tries: 3
|
||
http_codes: [502, 503, 504]
|
||
```
|
||
|
||
### 安全
|
||
|
||
- **访问控制** - IP/CIDR 白名单与黑名单,支持 trusted_proxies 配置
|
||
- **GeoIP 过滤** - 基于 MaxMind GeoIP2 数据库的国家/地区访问控制
|
||
- **速率限制** - 令牌桶与滑动窗口算法,支持精确/近似模式
|
||
- **连接限制** - 单 IP 并发连接数限制
|
||
- **认证** - Basic Auth,支持 bcrypt 与 argon2id;外部 auth_request 子请求
|
||
- **安全头部** - HSTS、X-Frame-Options、CSP、Referrer-Policy
|
||
- **SSL/TLS** - OCSP Stapling、TLS 1.2/1.3、加密套件配置、Session Tickets 密钥轮换
|
||
- **请求体限制** - 可配置全局和路径级别的请求体大小限制
|
||
|
||
### 性能优化
|
||
|
||
- **Goroutine 池** - 限制并发 worker 数量,避免 goroutine 爆炸
|
||
- **文件缓存** - LRU 淘汰策略,内存上限控制
|
||
- **连接池** - 空闲连接复用,减少连接建立开销
|
||
- **零拷贝** - 大文件(≥8KB)传输使用 sendfile 系统调用
|
||
- **代理缓存** - 支持缓存后端响应,cache_lock 防止缓存击穿
|
||
- **PGO 优化** - 支持 Profile-Guided Optimization 构建
|
||
|
||
### 运维
|
||
|
||
- **热升级** - USR2 信号触发,零停机升级,支持 Unix Socket 继承
|
||
- **配置热重载** - HUP 信号触发,动态更新配置
|
||
- **日志轮转** - USR1 信号触发,重新打开日志文件
|
||
- **优雅关闭** - QUIT 信号触发,等待请求完成,支持超时配置
|
||
- **状态监控** - 内置 `/status` 端点,统计连接数、请求数、流量、上游健康状态、缓存命中率
|
||
- **pprof 端点** - 内置性能分析端点,支持 CPU/heap/goroutine/block 分析
|
||
- **缓存清理 API** - POST `/purge` 端点,支持按路径清理代理缓存
|
||
|
||
## 安装
|
||
|
||
### 构建
|
||
|
||
```bash
|
||
# 克隆仓库
|
||
git clone https://github.com/DefectingCat/lolly.git
|
||
cd lolly
|
||
|
||
# 本地构建
|
||
make build
|
||
|
||
# 生产构建(体积优化)
|
||
make build-prod
|
||
|
||
# 性能构建(最大运行时性能)
|
||
make build-perf
|
||
|
||
# PGO 构建(需先收集 profile)
|
||
make pgo-collect # 查看收集指南
|
||
make build-pgo
|
||
|
||
# 跨平台构建
|
||
make build-all
|
||
```
|
||
|
||
构建产物位于 `bin/` 目录。
|
||
|
||
### 运行
|
||
|
||
```bash
|
||
# 使用默认配置
|
||
./bin/lolly
|
||
|
||
# 指定配置文件
|
||
./bin/lolly -c /path/to/lolly.yaml
|
||
|
||
# 生成默认配置
|
||
./bin/lolly -g -o lolly.yaml
|
||
|
||
# 导入 nginx 配置
|
||
./bin/lolly --import /etc/nginx/nginx.conf -o lolly.yaml
|
||
./bin/lolly -i nginx.conf # 简写形式
|
||
|
||
# 显示版本
|
||
./bin/lolly -v
|
||
```
|
||
|
||
### nginx 配置导入
|
||
|
||
lolly 支持将 nginx 配置文件转换为 YAML 格式:
|
||
|
||
```bash
|
||
# 导入 nginx 配置并输出到文件
|
||
./bin/lolly --import nginx.conf -o lolly.yaml
|
||
|
||
# 导入后会显示转换警告(不支持的指令)
|
||
```
|
||
|
||
支持的 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
|
||
|
||
不支持的指令会在转换时显示警告,需要手动处理。
|
||
|
||
## 架构
|
||
|
||
### 请求处理流程
|
||
|
||
```
|
||
+------------------+
|
||
| Client |
|
||
+--------+---------+
|
||
|
|
||
+-------------------+-------------------+
|
||
| | |
|
||
+----v----+ +-----v-----+ +----v----+
|
||
| HTTP/1 | | HTTP/2 | | HTTP/3 |
|
||
| (TCP) | | (TLS) | | (QUIC) |
|
||
+----+----+ +-----+-----+ +----+----+
|
||
| | |
|
||
+-------------------+-------------------+
|
||
|
|
||
+--------v---------+
|
||
| Middleware |
|
||
| Chain |
|
||
+--------+---------+
|
||
|
|
||
+-------------------+-------------------+
|
||
| | |
|
||
+----v----+ +-----v-----+ +----v----+
|
||
| Static | | Proxy | | Stream |
|
||
| Handler | | Handler | | Handler |
|
||
+----+----+ +-----+-----+ +----+----+
|
||
| | |
|
||
File Cache Load Balancer L4 Proxy
|
||
| | |
|
||
+----+----+ +-----v-----+ +----v----+
|
||
| Disk | | Upstream | | Backend |
|
||
| I/O | | Targets | | Servers |
|
||
+---------+ +-----------+ +---------+
|
||
```
|
||
|
||
### 核心模块依赖关系
|
||
|
||
```
|
||
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 | 减少重复查询/读取 |
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
internal/
|
||
├── app/ # 应用入口、信号处理、生命周期
|
||
│ ├── app.go # 主程序逻辑(Start/Stop/Reload)
|
||
│ ├── app_common.go # 通用平台代码
|
||
│ ├── app_windows.go # Windows 平台适配
|
||
│ └── import.go # nginx 配置导入
|
||
├── config/ # 配置加载、验证、默认值(13 文件)
|
||
│ ├── config.go # 根配置结构(支持 single/vhost/multi_server 模式)
|
||
│ ├── loader.go # 配置文件加载
|
||
│ ├── defaults.go # 默认配置
|
||
│ ├── validate.go # 配置验证
|
||
│ ├── server_config.go # 服务器配置
|
||
│ ├── proxy_config.go # 代理配置
|
||
│ ├── ssl_config.go # SSL/TLS 配置
|
||
│ ├── security_config.go # 安全配置
|
||
│ ├── cache_config.go # 缓存配置
|
||
│ ├── performance_config.go # 性能配置
|
||
│ ├── monitoring_config.go # 监控配置
|
||
│ └── variable_config.go # 变量配置
|
||
├── server/ # HTTP 服务器核心(13 文件)
|
||
│ ├── server.go # 服务器实现(三种运行模式)
|
||
│ ├── vhost.go # 虚拟主机管理
|
||
│ ├── pool.go # Goroutine 池
|
||
│ ├── status.go # 状态端点
|
||
│ ├── pprof.go # pprof 端点
|
||
│ ├── pprof_impl.go # pprof 实现
|
||
│ ├── purge.go # 缓存清除端点
|
||
│ ├── internal.go # 内部端点处理
|
||
│ ├── lifecycle.go # 生命周期管理
|
||
│ ├── middleware_builder.go # 中间件构建器
|
||
│ ├── init.go # 初始化逻辑
|
||
│ ├── router.go # 路由器集成
|
||
│ ├── upgrade.go # 热升级管理(Unix)
|
||
│ ├── upgrade_windows.go # 热升级管理(Windows)
|
||
│ └── testutil.go # 测试工具
|
||
├── handler/ # 请求处理器
|
||
│ ├── router.go # fasthttp/router 封装
|
||
│ ├── static.go # 静态文件处理(含缓存、ETag、304)
|
||
│ ├── sendfile.go # 零拷贝传输接口
|
||
│ ├── sendfile_linux.go # Linux sendfile 实现
|
||
│ ├── sendfile_common.go # 通用实现
|
||
│ ├── autoindex.go # 目录列表生成
|
||
│ ├── fileinfo_cache.go # FileInfo 缓存
|
||
│ └── errorpage.go # 错误页面管理
|
||
├── proxy/ # 反向代理
|
||
│ ├── proxy.go # 代理核心(负载均衡、缓存、故障转移)
|
||
│ ├── websocket.go # WebSocket 代理
|
||
│ ├── health.go # 主动健康检查
|
||
│ ├── health_match.go # 健康检查匹配逻辑
|
||
│ ├── headers.go # 头部处理工具
|
||
│ ├── header_modifier.go # 头部修改器
|
||
│ ├── redirect_rewrite.go # Location/Refresh 头改写
|
||
│ ├── proxy_ssl.go # 上游 SSL/TLS
|
||
│ ├── proxy_dns.go # DNS 解析集成
|
||
│ ├── target_selector.go # 目标选择器(Lua balancer 支持)
|
||
│ ├── cache_handler.go # 代理缓存处理
|
||
│ ├── tempfile.go # 临时文件管理
|
||
│ ├── tempfile_cleaner.go # 临时文件清理
|
||
│ ├── utils.go # 工具函数
|
||
│ └── validate.go # 配置验证
|
||
├── loadbalance/ # 负载均衡算法
|
||
│ ├── balancer.go # Balancer 接口、RoundRobin、WeightedRR、LeastConn、IPHash
|
||
│ ├── algorithms.go # 算法注册
|
||
│ ├── consistent_hash.go # 一致性哈希(虚拟节点)
|
||
│ ├── random.go # Power of Two Choices
|
||
│ └── slow_start.go # 慢启动算法
|
||
├── matcher/ # Location 匹配器
|
||
│ ├── matcher.go # 匹配器接口
|
||
│ ├── exact.go # 精确匹配
|
||
│ ├── prefix.go # 前缀匹配
|
||
│ ├── regex.go # 正则匹配
|
||
│ ├── radix.go # 基数树匹配
|
||
│ ├── conflict.go # 冲突检测
|
||
│ ├── location.go # Location 定义
|
||
│ ├── named.go # 命名捕获组
|
||
│ └── prefix_priority.go # 前缀优先级排序
|
||
├── middleware/ # 中间件链
|
||
│ ├── middleware.go # 中间件接口
|
||
│ ├── compression/ # Gzip/Brotli 压缩
|
||
│ │ ├── compression.go # 压缩实现
|
||
│ │ └── gzip_static.go # 预压缩文件
|
||
│ ├── security/ # 安全中间件
|
||
│ │ ├── access.go # IP/CIDR 访问控制
|
||
│ │ ├── ratelimit.go # 令牌桶限流
|
||
│ │ ├── sliding_window.go # 滑动窗口限流
|
||
│ │ ├── auth.go # Basic Auth(bcrypt/argon2id)
|
||
│ │ ├── auth_request.go # 子请求认证
|
||
│ │ ├── geoip.go # GeoIP 过滤
|
||
│ │ └── headers.go # 安全头部
|
||
│ ├── rewrite/ # URL 重写
|
||
│ ├── accesslog/ # 访问日志(nginx 兼容格式)
|
||
│ ├── bodylimit/ # 请求体大小限制
|
||
│ ├── limitrate/ # 响应速率限制
|
||
│ └── errorintercept/ # 错误页面拦截
|
||
├── lua/ # Lua 脚本引擎
|
||
│ ├── engine.go # gopher-lua 引擎
|
||
│ ├── context.go # 请求上下文
|
||
│ ├── register.go # API 注册
|
||
│ ├── socket_manager.go # Socket 管理
|
||
│ ├── filter_writer.go # header/body_filter
|
||
│ └── api_*.go # nginx-lua 兼容 API(20+ 文件)
|
||
├── http2/ # HTTP/2 服务器
|
||
│ ├── server.go # HTTP/2 实现
|
||
│ └── adapter.go # 适配器
|
||
├── http3/ # HTTP/3 服务器
|
||
│ ├── server.go # QUIC 服务器
|
||
│ └── adapter.go # 适配器
|
||
├── stream/ # TCP/UDP Stream 代理
|
||
│ ├── stream.go # 四层代理
|
||
│ └── ssl.go # Stream SSL
|
||
├── ssl/ # TLS 配置
|
||
│ ├── ssl.go # TLS 管理器
|
||
│ ├── ocsp.go # OCSP Stapling
|
||
│ ├── session_tickets.go # Session Tickets 轮换
|
||
│ └── client_verify.go # 客户端证书验证
|
||
├── cache/ # 缓存系统
|
||
│ ├── backend.go # 缓存后端抽象
|
||
│ ├── file_cache.go # 文件缓存(LRU)
|
||
│ ├── disk_cache.go # 磁盘缓存
|
||
│ ├── tiered_cache.go # L1/L2 分层缓存
|
||
│ └── purge.go # 缓存清除
|
||
├── resolver/ # DNS 解析器
|
||
│ ├── resolver.go # DNS 解析
|
||
│ ├── cache.go # DNS 缓存
|
||
│ └── stats.go # 解析统计
|
||
├── variable/ # 变量系统
|
||
│ ├── variable.go # 变量接口
|
||
│ ├── builtin.go # 内置变量($remote_addr 等)
|
||
│ ├── pool.go # 变量池
|
||
│ └── ssl.go # SSL 变量
|
||
├── converter/ # 配置转换器
|
||
│ └── nginx/ # nginx 配置导入
|
||
│ ├── parser.go # nginx 语法解析
|
||
│ └── converter.go # YAML 转换
|
||
├── logging/ # 日志系统
|
||
│ └── logging.go # zerolog 封装
|
||
├── netutil/ # 网络工具
|
||
│ ├── ip.go # IP 解析
|
||
│ ├── host.go # 主机名处理
|
||
│ └── url.go # URL 处理
|
||
├── mimeutil/ # MIME 类型
|
||
│ └── detect.go # MIME 检测
|
||
├── sslutil/ # SSL 工具
|
||
│ └── certpool.go # 证书池
|
||
├── utils/ # 通用工具
|
||
│ ├── httperror.go # HTTP 错误处理
|
||
│ └── internal.go # 内部工具
|
||
├── version/ # 版本信息
|
||
│ └── version.go # 版本定义
|
||
└── benchmark/ # 基准测试
|
||
└── tools/ # 测试辅助工具
|
||
```
|
||
|
||
### 核心设计
|
||
|
||
#### 中间件链
|
||
|
||
请求处理流程:
|
||
|
||
```
|
||
Request → AccessLog → AccessControl → RateLimiter → Auth → AuthRequest → BodyLimit →
|
||
Rewrite → Compression → SecurityHeaders → ErrorIntercept → Handler
|
||
```
|
||
|
||
每个中间件实现 `Middleware` 接口:
|
||
|
||
```go
|
||
type Middleware interface {
|
||
Name() string
|
||
Process(next fasthttp.RequestHandler) fasthttp.RequestHandler
|
||
}
|
||
```
|
||
|
||
#### 负载均衡
|
||
|
||
所有负载均衡器实现 `Balancer` 接口,支持健康目标过滤和故障转移排除:
|
||
|
||
```go
|
||
type Balancer interface {
|
||
Select(targets []*Target) *Target
|
||
SelectExcluding(targets []*Target, excluded []*Target) *Target
|
||
}
|
||
```
|
||
|
||
#### 代理缓存
|
||
|
||
支持:
|
||
|
||
- **缓存锁**(cache_lock)- 防止缓存击穿,同一缓存键只允许一个请求访问后端
|
||
- **过期复用**(stale-while-revalidate)- 允许在后台刷新时返回过期缓存
|
||
- **后台刷新** - 异步更新缓存,不影响请求响应时间
|
||
- **错误回退**(stale-if-error)- 后端错误时返回过期缓存
|
||
- **超时回退**(stale-if-timeout)- 后端超时时返回过期缓存
|
||
|
||
## Lua 脚本
|
||
|
||
Lolly 提供基于 gopher-lua 的 Lua 脚本扩展能力,支持 nginx-lua 兼容 API。
|
||
|
||
### 执行阶段
|
||
|
||
```
|
||
请求到达 → rewrite → access → content → header_filter → body_filter → log → 响应返回
|
||
```
|
||
|
||
| 阶段 | 说明 | 可用操作 |
|
||
| ------------- | ---------- | ------------------------------------- |
|
||
| rewrite | URL 重写 | ngx.req.set_uri, ngx.req.set_uri_args |
|
||
| access | 访问控制 | ngx.exit, ngx.req.get_headers |
|
||
| content | 内容生成 | ngx.say, ngx.print, ngx.exit |
|
||
| header_filter | 响应头修改 | ngx.header[key] = value |
|
||
| body_filter | 响应体修改 | ngx.arg[1] 操作响应体 |
|
||
| log | 日志记录 | ngx.log, 记录请求信息 |
|
||
|
||
### 支持的 API
|
||
|
||
```lua
|
||
-- 日志
|
||
ngx.log(ngx.ERR, "error message")
|
||
ngx.log(ngx.WARN, "warning message")
|
||
ngx.log(ngx.INFO, "info message")
|
||
|
||
-- HTTP 状态码常量
|
||
ngx.HTTP_OK = 200
|
||
ngx.HTTP_NOT_FOUND = 404
|
||
ngx.HTTP_INTERNAL_SERVER_ERROR = 500
|
||
-- 更多: HTTP_MOVED_PERMANENTLY, HTTP_BAD_REQUEST, HTTP_FORBIDDEN 等
|
||
|
||
-- 特殊常量
|
||
ngx.OK, ngx.ERROR, ngx.AGAIN, ngx.DONE, ngx.DECLINED
|
||
|
||
-- 内置变量(ngx.var)
|
||
local uri = ngx.var.uri
|
||
local method = ngx.var.request_method
|
||
local remote_addr = ngx.var.remote_addr
|
||
local host = ngx.var.host
|
||
local request_uri = ngx.var.request_uri
|
||
local scheme = ngx.var.scheme
|
||
local server_port = ngx.var.server_port
|
||
-- 任意请求头: ngx.var.http_xxx(如 ngx.var.http_user_agent)
|
||
-- 任意查询参数: ngx.var.arg_xxx(如 ngx.var.arg_token)
|
||
|
||
-- 请求操作(ngx.req)
|
||
ngx.req.get_method() -- 获取 HTTP 方法
|
||
ngx.req.get_uri() -- 获取 URI 路径
|
||
ngx.req.set_uri("/new_path") -- 设置 URI
|
||
ngx.req.set_uri_args("a=1&b=2") -- 设置查询参数
|
||
ngx.req.get_uri_args() -- 获取查询参数表
|
||
ngx.req.get_headers(max?) -- 获取请求头表
|
||
ngx.req.set_header("X-Custom", "value")
|
||
ngx.req.clear_header("X-Custom")
|
||
ngx.req.get_body_data() -- 获取请求体
|
||
ngx.req.read_body() -- 确保请求体已读取
|
||
|
||
-- 响应操作(ngx.resp)
|
||
ngx.resp.get_status() -- 获取响应状态码
|
||
ngx.resp.set_status(200) -- 设置响应状态码
|
||
ngx.resp.get_headers(max?) -- 获取响应头表
|
||
ngx.resp.set_header("X-Response", "value")
|
||
ngx.resp.clear_header("X-Response")
|
||
|
||
-- 快捷响应
|
||
ngx.say("Hello World") -- 输出内容并换行
|
||
ngx.print("Hello World") -- 输出内容不换行
|
||
ngx.flush(wait?) -- 刷新响应缓冲区
|
||
ngx.redirect("/new", 302) -- 重定向
|
||
ngx.exit(200) -- 结束请求处理
|
||
|
||
-- 请求上下文(ngx.ctx,每请求独立的 table)
|
||
ngx.ctx.user_id = 123
|
||
local id = ngx.ctx.user_id
|
||
|
||
-- 共享字典(ngx.shared.DICT)
|
||
local dict = ngx.shared.DICT
|
||
dict:set("key", "value", 3600) -- 设置(含过期时间)
|
||
dict:add("key", "value", 3600) -- 仅键不存在时添加
|
||
dict:replace("key", "value") -- 仅键存在时替换
|
||
local val = dict:get("key") -- 获取值
|
||
dict:incr("counter", 1) -- 数值自增
|
||
dict:delete("key") -- 删除键
|
||
dict:flush_all() -- 清空所有条目
|
||
dict:flush_expired(max?) -- 清除过期条目
|
||
dict:get_keys(max?) -- 获取所有键
|
||
dict:size() -- 获取条目数
|
||
dict:free_space() -- 获取剩余容量
|
||
-- 元表语法: dict["key"] = value / val = dict["key"]
|
||
|
||
-- Socket(ngx.socket.tcp,受限)
|
||
local sock = ngx.socket.tcp()
|
||
sock:connect("127.0.0.1", 8080)
|
||
sock:settimeout(5000) -- 设置超时(毫秒)
|
||
sock:settimeouts(1000, 2000, 3000) -- 分别设置连接/发送/读取超时
|
||
sock:send("GET / HTTP/1.0\r\n\r\n")
|
||
local response = sock:receive("*a") -- 读取全部
|
||
local line = sock:receive("*l") -- 读取一行
|
||
local reader = sock:receiveuntil("--boundary") -- 按模式读取
|
||
sock:close()
|
||
|
||
-- 定时器(ngx.timer)
|
||
local timer_id = ngx.timer.at(5, function()
|
||
ngx.log(ngx.INFO, "timer fired")
|
||
end)
|
||
ngx.timer.running_count() -- 获取运行中的定时器数量
|
||
-- handle:cancel() -- 取消定时器(通过返回的 handle)
|
||
|
||
-- 子请求(ngx.location.capture)
|
||
local res = ngx.location.capture("/internal/auth", {
|
||
method = ngx.HTTP_GET,
|
||
body = "request body",
|
||
args = "a=1&b=2"
|
||
})
|
||
-- res.status, res.body, res.headers
|
||
|
||
-- 动态负载均衡(ngx.balancer)
|
||
ngx.balancer.set_current_peer("backend:8080") -- 设置后端地址
|
||
ngx.balancer.set_more_tries(3) -- 设置重试次数
|
||
local fail_type = ngx.balancer.get_last_failure() -- 获取上次失败类型
|
||
local targets = ngx.balancer.get_targets() -- 获取所有可用目标
|
||
local client_ip = ngx.balancer.get_client_ip() -- 获取客户端 IP
|
||
```
|
||
|
||
### 示例脚本
|
||
|
||
**rewrite.lua** - URL 重写:
|
||
|
||
```lua
|
||
-- 将 /api/v1/* 重写为 /v1/*
|
||
local uri = ngx.var.uri
|
||
if string.match(uri, "^/api/v1/") then
|
||
local new_uri = string.gsub(uri, "^/api/", "")
|
||
ngx.req.set_uri(new_uri)
|
||
end
|
||
```
|
||
|
||
**access.lua** - 访问控制:
|
||
|
||
```lua
|
||
-- 检查请求头中的 token
|
||
local token = ngx.req.get_headers()["X-Token"]
|
||
if not token or token == "" then
|
||
ngx.exit(401)
|
||
return
|
||
end
|
||
|
||
-- 验证 token(示例)
|
||
if token ~= "valid-token" then
|
||
ngx.exit(403)
|
||
return
|
||
end
|
||
```
|
||
|
||
**content.lua** - 动态响应:
|
||
|
||
```lua
|
||
-- 生成 JSON 响应
|
||
ngx.header["Content-Type"] = "application/json"
|
||
local response = {
|
||
status = "ok",
|
||
timestamp = os.time(),
|
||
request_id = ngx.var.request_id
|
||
}
|
||
ngx.say(require("cjson").encode(response))
|
||
ngx.exit(200)
|
||
```
|
||
|
||
### 安全限制
|
||
|
||
默认沙箱模式禁用以下 Lua 库:
|
||
|
||
- `os`(除 os.time)
|
||
- `io`
|
||
- `file`
|
||
|
||
可通过配置 `lua.sandbox: false` 启用完整库访问(生产环境不建议)。
|
||
|
||
### 动态负载均衡
|
||
|
||
使用 `balancer_by_lua` 实现动态后端选择:
|
||
|
||
```lua
|
||
-- 根据请求路径选择不同后端
|
||
local uri = ngx.var.uri
|
||
if string.match(uri, "^/api/") then
|
||
ngx.balancer.set_current_peer("api-backend", 8080)
|
||
elseif string.match(uri, "^/static/") then
|
||
ngx.balancer.set_current_peer("static-backend", 8080)
|
||
else
|
||
ngx.balancer.set_current_peer("default-backend", 8080)
|
||
end
|
||
```
|
||
|
||
## 部署
|
||
|
||
### Docker
|
||
|
||
```bash
|
||
# 构建镜像
|
||
docker build -t lolly:latest .
|
||
|
||
# 运行容器
|
||
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 \
|
||
-v /var/log/lolly:/var/log/lolly \
|
||
lolly:latest
|
||
```
|
||
|
||
### Docker Compose
|
||
|
||
```yaml
|
||
version: "3"
|
||
services:
|
||
lolly:
|
||
image: lolly:latest
|
||
ports:
|
||
- "80:80"
|
||
- "443:443"
|
||
- "443:443/udp"
|
||
volumes:
|
||
- ./lolly.yaml:/etc/lolly/lolly.yaml:ro
|
||
- ./html:/var/www/html:ro
|
||
- ./logs:/var/log/lolly
|
||
restart: unless-stopped
|
||
```
|
||
|
||
### systemd
|
||
|
||
创建 `/etc/systemd/system/lolly.service`:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=Lolly HTTP Server
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=lolly
|
||
Group=lolly
|
||
ExecStart=/usr/local/bin/lolly -c /etc/lolly/lolly.yaml
|
||
ExecReload=/bin/kill -HUP $MAINPID
|
||
Restart=on-failure
|
||
LimitNOFILE=65535
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
```bash
|
||
# 安装服务
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable lolly
|
||
sudo systemctl start lolly
|
||
|
||
# 管理服务
|
||
sudo systemctl reload lolly # 重载配置
|
||
sudo systemctl restart lolly # 重启服务
|
||
sudo systemctl status lolly # 查看状态
|
||
```
|
||
|
||
## 监控
|
||
|
||
### 状态端点
|
||
|
||
访问 `/status`(需配置 `monitoring.status.allow` 白名单):
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.2",
|
||
"uptime": "2h30m15s",
|
||
"connections": 150,
|
||
"requests": 125000,
|
||
"bytes_sent": 5368709120,
|
||
"bytes_received": 1073741824,
|
||
"pool": {
|
||
"workers": 256,
|
||
"IdleWorkers": 200,
|
||
"MaxWorkers": 10000,
|
||
"MinWorkers": 100,
|
||
"QueueLen": 0,
|
||
"QueueCap": 1000
|
||
},
|
||
"upstreams": [
|
||
{
|
||
"name": "backend1",
|
||
"targets": [
|
||
{ "url": "http://10.0.0.1:8080", "healthy": true, "latency_ms": 5 },
|
||
{ "url": "http://10.0.0.2:8080", "healthy": true, "latency_ms": 8 }
|
||
],
|
||
"healthy_count": 2,
|
||
"unhealthy_count": 0,
|
||
"latency_p50_ms": 5.2,
|
||
"latency_p95_ms": 12.5,
|
||
"latency_p99_ms": 25.0
|
||
}
|
||
],
|
||
"rate_limits": [
|
||
{
|
||
"zone_name": "api_limit",
|
||
"requests": 50000,
|
||
"limit": 100,
|
||
"rejected": 150
|
||
}
|
||
],
|
||
"ssl": {
|
||
"handshakes": 2500,
|
||
"session_reused": 800,
|
||
"reuse_rate_percent": 32.0
|
||
},
|
||
"cache": {
|
||
"file_cache": {
|
||
"entries": 5000,
|
||
"max_entries": 50000,
|
||
"size": 134217728,
|
||
"max_size": 268435456
|
||
},
|
||
"proxy_cache": {
|
||
"entries": 2000,
|
||
"pending": 5
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### pprof 端点
|
||
|
||
启用 `monitoring.pprof.enabled: true` 后访问:
|
||
|
||
- `/debug/pprof/` - pprof 索引
|
||
- `/debug/pprof/profile?seconds=30` - CPU profile
|
||
- `/debug/pprof/heap` - 内存分配
|
||
- `/debug/pprof/goroutine` - Goroutine 分析
|
||
- `/debug/pprof/block` - 阻塞分析
|
||
|
||
## 信号处理
|
||
|
||
| 信号 | 行为 |
|
||
| --------------- | ---------------------- |
|
||
| SIGTERM, SIGINT | 快速停止 |
|
||
| SIGQUIT | 优雅停止,等待请求完成 |
|
||
| SIGHUP | 重载配置 |
|
||
| SIGUSR1 | 重新打开日志文件 |
|
||
| SIGUSR2 | 热升级 |
|
||
|
||
## 性能
|
||
|
||
基于 fasthttp,相比标准 net/http 有显著性能提升:
|
||
|
||
- 避免不必要的内存分配(零分配设计)
|
||
- 优化的事件循环
|
||
- 高效的连接池管理
|
||
- 零拷贝传输(sendfile)
|
||
- Goroutine 池复用
|
||
|
||
### 基准测试
|
||
|
||
在 4 核 8GB 服务器上测试(使用 `internal/benchmark/tools/loadgen`):
|
||
|
||
| 场景 | RPS | 平均延迟 | P99 延迟 |
|
||
| ------------------ | ----------- | -------- | -------- |
|
||
| 静态文件(1KB) | 120,000+ | 0.8ms | 3ms |
|
||
| 静态文件(100KB) | 15,000+ | 6ms | 25ms |
|
||
| 反向代理(无缓存) | 80,000+ | 1.2ms | 5ms |
|
||
| 反向代理(有缓存) | 150,000+ | 0.6ms | 2ms |
|
||
| WebSocket 连接 | 50,000 连接 | - | - |
|
||
|
||
### PGO 优化
|
||
|
||
支持 Profile-Guided Optimization 构建,可获得额外 5-15% 性能提升:
|
||
|
||
```bash
|
||
# 1. 启用 pprof 端点
|
||
# 2. 运行代表性工作负载
|
||
# 3. 收集 CPU profile
|
||
curl http://localhost:8080/debug/pprof/profile?seconds=30 > default.pgo
|
||
|
||
# 4. 使用 PGO 构建
|
||
make build-pgo
|
||
```
|
||
|
||
### 生产环境推荐配置
|
||
|
||
```yaml
|
||
performance:
|
||
goroutine_pool:
|
||
enabled: true
|
||
max_workers: 10000 # 根据 CPU 核数调整
|
||
min_workers: 100
|
||
idle_timeout: 60s
|
||
file_cache:
|
||
max_entries: 50000 # 根据内存调整
|
||
max_size: 268435456 # 256MB
|
||
transport:
|
||
idle_conn_timeout: 90s
|
||
max_conns_per_host: 500 # 根据后端容量调整
|
||
```
|
||
|
||
容量规划建议:
|
||
|
||
| 流量级别 | 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 |
|
||
|
||
## 与 NGINX 对比
|
||
|
||
| 特性 | Lolly | NGINX |
|
||
| -------------- | --------------- | ------------ |
|
||
| HTTP/3 | 支持(quic-go) | 1.25+ 支持 |
|
||
| HTTP/2 | 支持(需 TLS) | 支持 |
|
||
| 配置格式 | YAML | 自定义 DSL |
|
||
| 热升级 | 支持 | 支持 |
|
||
| 扩展方式 | Go 代码/Lua | C 模块/Lua |
|
||
| 部署方式 | 单二进制 | 需安装 |
|
||
| TCP/UDP Stream | 支持 | 支持 |
|
||
| 代理缓存 | 支持(带锁) | 支持 |
|
||
| GeoIP | MaxMind GeoIP2 | GeoIP 模块 |
|
||
| 一致性哈希 | 支持(内置) | 需第三方模块 |
|
||
|
||
**Lolly 优势**:
|
||
|
||
- YAML 配置更易读、易于解析和生成
|
||
- 单二进制部署,无依赖
|
||
- Go 原生扩展,无需 C 编译环境
|
||
- 现代 Go 库生态系统
|
||
|
||
**NGINX 优势**:
|
||
|
||
- 经过大规模生产验证
|
||
- 丰富的第三方模块生态
|
||
- 更完整的功能集(邮件代理等)
|
||
|
||
### 从 NGINX 迁移
|
||
|
||
常见配置转换示例:
|
||
|
||
| NGINX | Lolly |
|
||
| ------------------------------------------ | ---------------------------------------------- |
|
||
| `listen 80;` | `listen: ":80"` |
|
||
| `root /var/www/html;` | `root: "/var/www/html"` |
|
||
| `proxy_pass http://backend;` | `url: "http://backend"` |
|
||
| `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 }` |
|
||
| `ssl_certificate /path/cert.pem;` | `cert: "/path/cert.pem"` |
|
||
|
||
迁移注意事项:
|
||
|
||
1. 变量系统语法相同($remote_addr 等)
|
||
2. try_files 语法兼容
|
||
3. Lua API 与 OpenResty 高度兼容
|
||
4. 健康检查配置略有差异
|
||
|
||
## 故障排除
|
||
|
||
### 调试模式
|
||
|
||
启用详细日志:
|
||
|
||
```yaml
|
||
logging:
|
||
error:
|
||
level: "debug"
|
||
access:
|
||
format: "combined"
|
||
```
|
||
|
||
查看实时日志:
|
||
|
||
```bash
|
||
# 查看错误日志
|
||
tail -f /var/log/lolly/error.log | jq .
|
||
|
||
# 查看访问日志
|
||
tail -f /var/log/lolly/access.log
|
||
```
|
||
|
||
### 健康检查
|
||
|
||
```bash
|
||
# 检查状态端点
|
||
curl http://localhost:8080/status | jq .
|
||
|
||
# 检查后端健康
|
||
curl http://localhost:8080/status | jq '.upstreams'
|
||
|
||
# 检查缓存命中率
|
||
curl http://localhost:8080/status | jq '.cache'
|
||
```
|
||
|
||
## 开发
|
||
|
||
### 环境要求
|
||
|
||
- Go 1.26+
|
||
- make
|
||
|
||
### 命令
|
||
|
||
```bash
|
||
# 运行测试
|
||
make test
|
||
|
||
# 测试覆盖率
|
||
make test-cover
|
||
|
||
# 基准测试
|
||
make bench
|
||
|
||
# 基准测试(统计模式)
|
||
make bench-stat
|
||
|
||
# 对比基准结果
|
||
make bench-compare
|
||
|
||
# 代码检查
|
||
make check
|
||
|
||
# 格式化
|
||
make fmt
|
||
|
||
# 静态分析
|
||
make lint
|
||
```
|
||
|
||
### 项目统计
|
||
|
||
- Go 源文件:174 个
|
||
- 测试文件:158 个
|
||
- 总代码行数:~136,000 行
|
||
- 测试覆盖率:>80%
|
||
- 中文代码注释
|
||
- 所有核心模块均有完整单元测试和性能基准测试
|
||
|
||
## 依赖
|
||
|
||
| 库 | 版本 | 用途 |
|
||
| ------------------------------------------------------------------------ | ------- | ---------------- |
|
||
| [fasthttp](https://github.com/valyala/fasthttp) | v1.70.0 | 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 实现 |
|
||
| [zerolog](https://github.com/rs/zerolog) | v1.35.1 | 零分配 JSON 日志 |
|
||
| [gopher-lua](https://github.com/yuin/gopher-lua) | v1.1.2 | Lua 脚本引擎 |
|
||
| [klauspost/compress](https://github.com/klauspost/compress) | v1.18.5 | Gzip/Brotli 压缩 |
|
||
| [brotli](https://github.com/andybalholm/brotli) | v1.2.1 | Brotli 压缩编码 |
|
||
| [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 缓存 |
|
||
| [uuid](https://github.com/google/uuid) | v1.6.0 | UUID 生成 |
|
||
| [testcontainers-go](https://github.com/testcontainers/testcontainers-go) | v0.42.0 | 集成测试容器 |
|
||
| [testify](https://github.com/stretchr/testify) | v1.11.1 | 测试断言 |
|
||
| [golang.org/x/crypto](https://golang.org/x/crypto) | v0.50.0 | 加密工具 |
|
||
| [golang.org/x/net](https://golang.org/x/net) | v0.53.0 | 网络扩展 |
|
||
| [yaml.v3](https://gopkg.in/yaml.v3) | v3.0.1 | YAML 配置解析 |
|
||
|
||
## 许可证
|
||
|
||
MIT License
|
||
|
||
## 作者
|
||
|
||
xfy
|
||
|