From 972eab4267f50bc7e4893cb781c0d8306c6c4a37 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 16 Apr 2026 10:48:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor(docs):=20=E9=87=8D=E6=9E=84=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84=EF=BC=8Cnginx=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=A7=BB=E8=87=B3=E5=AD=90=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 docs/ 根目录下的 nginx 相关文档统一移动到 docs/nginx/ 子目录, 提高文档组织性和可维护性。 Co-Authored-By: Claude Opus 4.6 --- docs/config-reference.md | 233 ----- docs/lua-embed-analysis.md | 835 ------------------ docs/{ => nginx}/01-nginx-overview.md | 0 docs/{ => nginx}/02-nginx-installation.md | 0 docs/{ => nginx}/03-nginx-http-core.md | 0 .../04-nginx-proxy-loadbalancing.md | 0 docs/{ => nginx}/05-nginx-ssl-https.md | 0 docs/{ => nginx}/06-nginx-rewrite.md | 0 .../07-nginx-compression-caching.md | 0 .../08-nginx-logging-monitoring.md | 0 docs/{ => nginx}/09-nginx-security.md | 0 docs/{ => nginx}/10-nginx-stream-tcp-udp.md | 0 docs/{ => nginx}/11-nginx-mail-proxy.md | 0 .../12-nginx-performance-tuning.md | 0 docs/{ => nginx}/13-git-commit-guide.md | 0 docs/{ => nginx}/14-nginx-grpc-uwsgi.md | 0 .../{ => nginx}/15-nginx-advanced-features.md | 0 .../{ => nginx}/16-nginx-internal-redirect.md | 0 docs/{ => nginx}/17-nginx-mirror-slice.md | 0 docs/{ => nginx}/18-nginx-memcached.md | 0 .../19-nginx-http-modules-detail.md | 0 docs/{ => nginx}/20-nginx-rate-limiting.md | 0 docs/{ => nginx}/21-nginx-http2-http3.md | 0 .../22-nginx-third-party-modules.md | 0 docs/{ => nginx}/23-nginx-special-modules.md | 0 docs/{ => nginx}/24-nginx-core-events.md | 0 .../25-nginx-variables-reference.md | 0 docs/{ => nginx}/26-nginx-lua-guide.md | 0 .../27-nginx-security-deep-dive.md | 0 docs/{ => nginx}/28-nginx-api-gateway.md | 0 docs/{ => nginx}/29-nginx-dynamic-config.md | 0 docs/{ => nginx}/30-nginx-njs-guide.md | 0 docs/{ => nginx}/31-nginx-observability.md | 0 docs/{ => nginx}/32-nginx-acme-ssl.md | 0 docs/{ => nginx}/33-nginx-mqtt-module.md | 0 docs/{ => nginx}/34-nginx-oidc-module.md | 0 docs/{ => nginx}/35-nginx-keyval-module.md | 0 docs/{ => nginx}/36-nginx-streaming-media.md | 0 docs/{ => nginx}/37-nginx-dav-module.md | 0 docs/{ => nginx}/38-nginx-zone-sync-module.md | 0 docs/{ => nginx}/39-nginx-tunnel-module.md | 0 41 files changed, 1068 deletions(-) delete mode 100644 docs/config-reference.md delete mode 100644 docs/lua-embed-analysis.md rename docs/{ => nginx}/01-nginx-overview.md (100%) rename docs/{ => nginx}/02-nginx-installation.md (100%) rename docs/{ => nginx}/03-nginx-http-core.md (100%) rename docs/{ => nginx}/04-nginx-proxy-loadbalancing.md (100%) rename docs/{ => nginx}/05-nginx-ssl-https.md (100%) rename docs/{ => nginx}/06-nginx-rewrite.md (100%) rename docs/{ => nginx}/07-nginx-compression-caching.md (100%) rename docs/{ => nginx}/08-nginx-logging-monitoring.md (100%) rename docs/{ => nginx}/09-nginx-security.md (100%) rename docs/{ => nginx}/10-nginx-stream-tcp-udp.md (100%) rename docs/{ => nginx}/11-nginx-mail-proxy.md (100%) rename docs/{ => nginx}/12-nginx-performance-tuning.md (100%) rename docs/{ => nginx}/13-git-commit-guide.md (100%) rename docs/{ => nginx}/14-nginx-grpc-uwsgi.md (100%) rename docs/{ => nginx}/15-nginx-advanced-features.md (100%) rename docs/{ => nginx}/16-nginx-internal-redirect.md (100%) rename docs/{ => nginx}/17-nginx-mirror-slice.md (100%) rename docs/{ => nginx}/18-nginx-memcached.md (100%) rename docs/{ => nginx}/19-nginx-http-modules-detail.md (100%) rename docs/{ => nginx}/20-nginx-rate-limiting.md (100%) rename docs/{ => nginx}/21-nginx-http2-http3.md (100%) rename docs/{ => nginx}/22-nginx-third-party-modules.md (100%) rename docs/{ => nginx}/23-nginx-special-modules.md (100%) rename docs/{ => nginx}/24-nginx-core-events.md (100%) rename docs/{ => nginx}/25-nginx-variables-reference.md (100%) rename docs/{ => nginx}/26-nginx-lua-guide.md (100%) rename docs/{ => nginx}/27-nginx-security-deep-dive.md (100%) rename docs/{ => nginx}/28-nginx-api-gateway.md (100%) rename docs/{ => nginx}/29-nginx-dynamic-config.md (100%) rename docs/{ => nginx}/30-nginx-njs-guide.md (100%) rename docs/{ => nginx}/31-nginx-observability.md (100%) rename docs/{ => nginx}/32-nginx-acme-ssl.md (100%) rename docs/{ => nginx}/33-nginx-mqtt-module.md (100%) rename docs/{ => nginx}/34-nginx-oidc-module.md (100%) rename docs/{ => nginx}/35-nginx-keyval-module.md (100%) rename docs/{ => nginx}/36-nginx-streaming-media.md (100%) rename docs/{ => nginx}/37-nginx-dav-module.md (100%) rename docs/{ => nginx}/38-nginx-zone-sync-module.md (100%) rename docs/{ => nginx}/39-nginx-tunnel-module.md (100%) diff --git a/docs/config-reference.md b/docs/config-reference.md deleted file mode 100644 index 9d47a18..0000000 --- a/docs/config-reference.md +++ /dev/null @@ -1,233 +0,0 @@ -# 配置参考文档 - -## 目录 - -- [变量系统](#变量系统) -- [DNS 解析器](#dns-解析器) -- [访问日志格式](#访问日志格式) - ---- - -## 变量系统 - -Lolly 支持 nginx 风格的变量系统,可用于访问日志格式、代理请求头和 URL 重写规则。 - -### 内置变量 - -| 变量名 | 说明 | 示例值 | -|--------|------|--------| -| `$host` | 请求的主机名(Host 头) | `example.com` | -| `$remote_addr` | 客户端 IP 地址 | `192.168.1.1` | -| `$remote_port` | 客户端端口 | `54321` | -| `$request_uri` | 原始请求 URI(包含查询参数) | `/api/users?page=1` | -| `$uri` | 解码后的 URI 路径 | `/api/users` | -| `$args` | 查询参数字符串 | `page=1&limit=10` | -| `$request_method` | HTTP 请求方法 | `GET`, `POST` | -| `$scheme` | 协议 | `http`, `https` | -| `$server_name` | 服务器名称 | `localhost` | -| `$server_port` | 服务器端口 | `8080` | -| `$status` | HTTP 响应状态码 | `200`, `404` | -| `$body_bytes_sent` | 发送的响应体字节数 | `1024` | -| `$request_time` | 请求处理时间(秒) | `0.050` | -| `$time_local` | 本地时间 | `08/Apr/2026:11:04:58 +0800` | -| `$time_iso8601` | ISO8601 格式时间 | `2026-04-08T11:04:58+08:00` | -| `$request_id` | 唯一请求标识符 | `uuid` | - -### 动态 HTTP 头变量 - -以 `$http_` 开头的变量用于获取 HTTP 请求头值: - -- `$http_user_agent` - User-Agent 头 -- `$http_referer` - Referer 头 -- `$http_x_forwarded_for` - X-Forwarded-For 头 -- 其他任意请求头:`$http_header_name` - -### 变量格式 - -支持两种格式: - -1. **简单格式**: `$var` - ``` - $host $uri - ``` - -2. **花括号格式**: `${var}` - ``` - ${host}:8080 - ${scheme}://${host}${uri} - ``` - -### 在代理请求头中使用变量 - -```yaml -proxy: - - path: /api - targets: - - url: http://backend:8080 - headers: - set_request: - X-Real-IP: "$remote_addr" - X-Forwarded-Host: "$host" - X-Request-ID: "$request_id" -``` - -### 在访问日志中使用变量 - -```yaml -logging: - access: - format: '$remote_addr - $remote_user [$time_local] "$request_method $uri $scheme" $status $body_bytes_sent' -``` - -### 自定义变量 - -```yaml -variables: - set: - app_name: "lolly" - version: "1.0.0" -``` - -**注意**: -- `$request_id` 为内置变量,自动为每个请求生成唯一 UUID,无需配置 -- 自定义变量名不能与内置变量冲突 -- 变量名只允许字母、数字、下划线 - ---- - -## DNS 解析器 - -Lolly 内置 DNS 解析器,支持动态解析后端服务域名。 - -### 配置选项 - -```yaml -resolver: - enabled: true # 是否启用 - addresses: # DNS 服务器地址列表 - - "8.8.8.8:53" - - "8.8.4.4:53" - valid: 30s # 缓存有效期(TTL) - timeout: 5s # DNS 查询超时 - ipv4: true # 查询 IPv4 地址 - ipv6: false # 查询 IPv6 地址 - cache_size: 1024 # 缓存最大条目数 -``` - -### 功能特性 - -- **DNS 缓存**: 按 TTL 缓存解析结果,减少 DNS 查询延迟 -- **后台刷新**: 自动在 TTL/2 时刷新缓存,避免过期 -- **故障转移**: 解析失败时使用缓存 IP 继续服务 -- **健康检查**: 首次解析失败标记目标不健康 - -### 使用场景 - -当后端目标使用域名时,DNS 解析器自动生效: - -```yaml -proxy: - - path: /api - targets: - - url: http://backend.example.com:8080 # 使用域名 - weight: 1 -``` - -### 监控指标 - -通过状态端点获取 DNS 解析统计: - -- `CacheHits` - 缓存命中次数 -- `CacheMisses` - 缓存未命中次数 -- `CacheEntries` - 当前缓存条目数 -- `ResolveErrors` - 解析错误次数 -- `AverageLatency` - 平均解析延迟 - ---- - -## 访问日志格式 - -### nginx 兼容格式 - -Lolly 默认提供 nginx 兼容的访问日志格式: - -```yaml -logging: - access: - format: '$remote_addr - $remote_user [$time] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"' -``` - -示例输出: -``` -192.168.1.1 - - [08/Apr/2026:11:04:58 +0800] "GET /api/users HTTP/1.1" 200 1024 "-" "Mozilla/5.0" -``` - -### JSON 格式 - -设置格式为 `json` 输出结构化日志: - -```yaml -logging: - access: - format: 'json' -``` - -示例输出: -```json -{ - "remote_addr": "192.168.1.1", - "request": "GET /api/users HTTP/1.1", - "status": 200, - "body_bytes_sent": 1024, - "http_user_agent": "Mozilla/5.0" -} -``` - -### 自定义格式 - -使用变量创建自定义格式: - -```yaml -logging: - access: - format: '$remote_addr $request_method $uri $status $request_time' -``` - ---- - -## 完整配置示例 - -```yaml -server: - listen: ":8080" - name: "localhost" - - proxy: - - path: /api - targets: - - url: http://backend.example.com:8080 - headers: - set_request: - X-Real-IP: "$remote_addr" - X-Forwarded-Host: "$host" - X-Request-ID: "$request_id" - -resolver: - enabled: true - addresses: - - "8.8.8.8:53" - valid: 30s - timeout: 5s - -variables: - set: - app_name: "lolly" - -logging: - access: - format: '$remote_addr - $remote_user [$time_local] "$request_method $uri $scheme" $status $body_bytes_sent' - path: "/var/log/lolly/access.log" - error: - level: "info" - path: "/var/log/lolly/error.log" -``` diff --git a/docs/lua-embed-analysis.md b/docs/lua-embed-analysis.md deleted file mode 100644 index 089ca01..0000000 --- a/docs/lua-embed-analysis.md +++ /dev/null @@ -1,835 +0,0 @@ -# Golang Lua 运行时嵌入分析 - -本文档分析 Go 语言嵌入 Lua 运行时的方案,为 lolly 项目实现类似 lua-nginx-module 功能提供技术参考。 - ---- - -## 一、主流 Lua 运行时方案对比 - -### 1.1 可选方案 - -| 方案 | 语言 | 性能 | Lua版本 | 特点 | -|------|------|------|---------|------| -| **gopher-lua** | 纯 Go | ~Python3 | Lua 5.1 + goto | 原生 Go 实现,goroutine/channel 集成 | -| **go-lua** | Go + C | ~原生Lua | Lua 5.1 | CGO 调用 C Lua,性能接近原生 | -| **luaJIT (CGO)** | C | 极高 | LuaJIT 2.1 | 最快,FFI 强大,但 CGO 开销 | -| **glua** (Shopify) | Go + C | 高 | Lua 5.2/5.3 | Shopify 废弃,不推荐 | - -### 1.2 详细对比 - -#### gopher-lua - -**优势**: -- **纯 Go 实现**: 无 CGO 依赖,交叉编译友好 -- **原生并发**: `LChannel` 类型直接操作 Go channel -- **Context 支持**: `SetContext(ctx)` 实现超时取消 -- **字节码复用**: `FunctionProto` 跨 LState 共享 -- **安全沙箱**: `SkipOpenLibs` 精细控制标准库加载 - -**劣势**: -- 性能约 Python3 级别,低于原生 Lua/LuaJIT -- 不支持 Lua 5.2+ 特性(bit32, utf8 等) -- GC 压力较大(大量 LValue 对象) - -**适用场景**: -- 需要纯 Go、交叉编译 -- 性能要求中等 -- 需要与 Go goroutine/channel 深度集成 - -#### go-lua (CGO binding) - -**优势**: -- 性能接近原生 Lua(通过 CGO 直接调用 C API) -- 支持 Lua 5.1 标准库完整功能 - -**劣势**: -- CGO 依赖,交叉编译复杂 -- Go-C 边界开销(每次调用 ~50ns) -- 协程与 goroutine 交互困难(C 栈问题) - -**适用场景**: -- 性能关键场景 -- 已有 C Lua 生态依赖 -- 可接受 CGO 复杂度 - -#### LuaJIT via CGO - -**优势**: -- **极致性能**: JIT 编译,接近 C 速度 -- **FFI 强大**: 直接调用 C 函数无开销 -- **内存高效**: 更小的内存占用 - -**劣势**: -- LuaJIT 2.1 开发停滞 -- CGO 集成复杂 -- JIT 在某些环境受限(容器、安全限制) -- Go-LuaJIT 协程映射困难 - -**适用场景**: -- 性能极致要求 -- 已有 OpenResty/LuaJIT 生态 -- 运行环境可控 - -### 1.3 推荐选择 - -**对于 lolly 项目,推荐 gopher-lua**: - -1. **纯 Go**: 与项目技术栈一致,交叉编译无障碍 -2. **并发集成**: 天然支持 goroutine/channel,契合 Go HTTP 服务器架构 -3. **性能足够**: Python3 级性能对脚本处理场景已够用 -4. **成熟稳定**: yuin/gopher-lua 维护活跃,社区成熟 - ---- - -## 二、gopher-lua 核心 API - -### 2.1 LState 状态机 - -```go -import "github.com/yuin/gopher-lua" - -// 创建 VM -L := lua.NewState(lua.Options{ - SkipOpenLibs: true, // 安全:禁用默认库 - IncludeGoStackTrace: true, // Panic 时输出 Go 调用栈 -}) -defer L.Close() - -// 执行脚本 -L.DoString("print('hello')") -L.DoFile("script.lua") - -// Context 控制(超时) -ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) -L.SetContext(ctx) -L.DoString("while true do end") // 5秒后自动取消 -``` - -### 2.2 栈操作 - -```go -// 基本栈操作 -L.Push(lua.LNumber(42)) // 压入数字 -L.Push(lua.LString("hello")) // 压入字符串 -v := L.Get(-1) // 获取栈顶 -L.Pop(2) // 弹出 2 个 - -// 全局变量 -L.SetGlobal("myvar", lua.LNumber(100)) -val := L.GetGlobal("myvar") - -// Table 操作 -tbl := L.NewTable() -L.SetField(tbl, "name", lua.LString("lolly")) -L.SetField(tbl, "version", lua.LString("0.2.0")) -L.SetGlobal("config", tbl) -``` - -### 2.3 函数注册 - -```go -// Go 函数签名: func(L *lua.LState) int (返回压栈结果数) -func Double(L *lua.LState) int { - n := L.CheckInt(1) // 获取第1个参数 - L.Push(lua.LNumber(n * 2)) // 压入结果 - return 1 // 返回结果数 -} - -// 注册到全局 -L.SetGlobal("double", L.NewFunction(Double)) - -// 批量注册到模块 -mod := L.NewTable() -L.SetFuncs(mod, map[string]lua.LGFunction{ - "double": Double, - "add": Add, - "sub": Sub, -}) -L.SetGlobal("mathx", mod) - -// 闭包(带 upvalue) -counter := 0 -L.SetGlobal("counter", L.NewClosure(func(L *lua.LState) int { - counter++ - L.Push(lua.LNumber(counter)) - return 1 -})) -``` - -### 2.4 调用 Lua 函数 - -```go -// 受保护调用(推荐) -err := L.CallByParam(lua.P{ - Fn: L.GetGlobal("myFunc"), // 函数 - NRet: 1, // 期望返回值 - Protect: true, // 拦截 panic -}, lua.LNumber(10), lua.LString("arg")) - -if err != nil { - // 错误处理 -} -ret := L.Get(-1) // 获取返回值 -L.Pop(1) // 清理栈 -``` - -### 2.5 模块加载 - -```go -// 预加载自定义模块 -L.PreloadModule("lolly", func(L *lua.LState) int { - mod := L.NewTable() - L.SetFuncs(mod, map[string]lua.LGFunction{ - "say": SayHello, - "log": LogMessage, - "sleep": Sleep, - }) - L.Push(mod) - return 1 // 返回模块表 -}) - -// Lua 中使用 -L.DoString(` -local lolly = require("lolly") -lolly.say("hello") -`) -``` - ---- - -## 三、协程支持 - -### 3.1 协程 API - -```go -// 创建协程线程 -co, cancel := L.NewThread() // 共享全局状态 - -// 获取 Lua 协程函数 -fn := L.GetGlobal("coroutine_func").(*lua.LFunction) - -// Resume(恢复执行) -state, err, values := L.Resume(co, fn, lua.LNumber(10)) -// state: lua.ResumeOK / lua.ResumeYield / lua.ResumeError -// values: yield 返回的值列表 - -// 检查状态 -status := L.Status(co) // "suspended" / "running" / "normal" / "dead" -``` - -### 3.2 Yield 模式 - -Lua 侧: -```lua -function async_task() - print("start") - coroutine.yield("waiting") -- 挂起,返回值 - print("continue") - return "done" -end -``` - -Go 侧: -```go -co, _ := L.NewThread() -fn := L.GetGlobal("async_task").(*lua.LFunction) - -// 第一次 resume -st, err, vals := L.Resume(co, fn) -if st == lua.ResumeYield { - fmt.Println("yielded:", vals[0]) // "waiting" -} - -// 第二次 resume(恢复) -st, err, vals = L.Resume(co) -if st == lua.ResumeOK { - fmt.Println("done:", vals[0]) // "done" -} -``` - -### 3.3 与 Go Channel 集成 - -gopher-lua 提供 `LChannel` 类型,让 Lua 操作 Go channel: - -```go -// 创建 Go channel -ch := make(chan string, 10) - -// 传递给 Lua -luaCh := lua.LChannel{Channel: ch} -L.SetGlobal("mychannel", luaCh) - -// Lua 中操作 -L.DoString(` --- 发送 -mychannel:send("hello") - --- 接收(阻塞) -local msg = mychannel:receive() -print(msg) -`) -``` - ---- - -## 四、错误处理 - -### 4.1 ApiError 结构 - -```go -type ApiError struct { - Type ApiErrorType // Run/Syntax/Panic/Memory/File - Object LValue // Lua 错误对象 - StackTrace string // 调用栈 - Cause error // 底层错误 -} -``` - -### 4.2 Panic Handler - -```go -// 注册 panic handler(类似 lua-nginx-module) -L.SetPanic(func(L *lua.LState) { - // 捕获 panic,记录日志 - log.Error("Lua panic: ", L.Get(-1)) - // 可以选择重建 VM 或返回错误 -}) - -// 使用 PCall 保护调用 -err := L.PCall(0, 0, nil) -if err != nil { - apiErr := err.(*lua.ApiError) - log.Error("Lua error: ", apiErr.StackTrace) -} -``` - ---- - -## 五、字节码缓存与复用 - -### 5.1 编译与复用 - -```go -// 编译脚本为字节码(可跨 VM 复用) -proto, err := lua.CompileString("function foo() return 42 end", "foo.lua") - -// 多个 LState 共享字节码 -L1 := lua.NewState() -fn1 := L1.NewFunctionFromProto(proto) - -L2 := lua.NewState() -fn2 := L2.NewFunctionFromProto(proto) // 无需重新编译 -``` - -### 5.2 缓存设计(类似 lua-nginx-module) - -```go -type CodeCache struct { - mu sync.RWMutex - protos map[string]*lua.FunctionProto // MD5(key) -> proto -} - -func (c *CodeCache) GetOrCompile(src string) (*lua.FunctionProto, error) { - key := md5Key(src) - - c.mu.RLock() - proto, ok := c.protos[key] - c.mu.RUnlock() - - if ok { - return proto, nil - } - - // 编译并缓存 - proto, err := lua.CompileString(src, key) - if err != nil { - return nil, err - } - - c.mu.Lock() - c.protos[key] = proto - c.mu.Unlock() - - return proto, nil -} -``` - ---- - -## 六、与 lolly 项目集成设计 - -### 6.1 架构映射 - -| lua-nginx-module | lolly (Go) 实现 | -|------------------|----------------| -| `ngx_http_lua_main_conf_t` | `LuaWorker` 结构,持有 VM | -| `ngx_http_lua_ctx_t` | `LuaRequestCtx`,请求上下文 | -| `ngx_http_lua_co_ctx_t` | `LuaCoroutine`,协程状态 | -| Phase Handlers | Middleware 集成点 | -| Filter Chain | Response Filter 中间件 | - -### 6.2 核心结构设计 - -```go -// internal/lua/engine.go - -package lua - -import ( - "context" - "sync" - - glua "github.com/yuin/gopher-lua" -) - -// LuaWorker - worker 级单 VM(对应 ngx_http_lua_main_conf_t) -type LuaWorker struct { - L *glua.LState // 主 VM - codeCache *CodeCache // 字节码缓存 - coroPool *CoroutinePool // 协程池 - modules map[string]glua.LGFunction // 已加载模块 - mu sync.RWMutex -} - -// LuaRequestCtx - 请求上下文(对应 ngx_http_lua_ctx_t) -type LuaRequestCtx struct { - Worker *LuaWorker - Request *fasthttp.RequestCtx // fasthttp 请求 - Coroutine *LuaCoroutine // 当前协程 - Variables map[string]string // ngx.var - Output []byte // ngx.say 输出缓冲 - Phase Phase // 当前阶段 - Ctx context.Context // Go context -} - -// LuaCoroutine - 协程状态(对应 ngx_http_lua_co_ctx_t) -type LuaCoroutine struct { - Thread *glua.LState // 协程线程 - Status CoroutineStatus // running/suspended/dead - Parent *LuaCoroutine // 父协程 - ResumeFunc func() // 恢复回调(类似 resume_handler) -} - -// Phase - 处理阶段 -type Phase int - -const ( - PhaseInit Phase = iota - PhaseAccess - PhaseContent - PhaseLog - PhaseHeaderFilter - PhaseBodyFilter -) -``` - -### 6.3 Worker 级单 VM 初始化 - -```go -// internal/lua/worker.go - -func NewLuaWorker() *LuaWorker { - // 创建 VM(安全模式) - L := glua.NewState(glua.Options{ - SkipOpenLibs: true, - }) - - worker := &LuaWorker{ - L: L, - codeCache: NewCodeCache(), - coroPool: NewCoroutinePool(L), - modules: make(map[string]glua.LGFunction), - } - - // 加载必要标准库 - worker.loadSafeLibs() - - // 注册 lolly.* API - worker.registerLollyAPI() - - return worker -} - -func (w *LuaWorker) loadSafeLibs() { - // 只加载安全的库(禁用 os, io 等危险库) - w.L.CallByParam(glua.P{ - Fn: w.L.NewFunction(glua.OpenBase), - Protect: true, - }) - w.L.CallByParam(glua.P{ - Fn: w.L.NewFunction(glua.OpenTable), - Protect: true, - }) - w.L.CallByParam(glua.P{ - Fn: w.L.NewFunction(glua.OpenString), - Protect: true, - }) - w.L.CallByParam(glua.P{ - Fn: w.L.NewFunction(glua.OpenMath), - Protect: true, - }) -} -``` - -### 6.4 lolly.* API 注册 - -```go -// internal/lua/api.go - -func (w *LuaWorker) registerLollyAPI() { - // 创建 lolly 模块表 - lollyMod := w.L.NewTable() - - // 注册核心 API - w.L.SetFuncs(lollyMod, map[string]glua.LGFunction{ - // 输出 - "say": w.apiSay, - "print": w.apiPrint, - - // 请求 - "req": w.apiRequest, - "get_uri": w.apiGetURI, - "get_arg": w.apiGetArg, - "get_header": w.apiGetHeader, - - // 响应 - "resp": w.apiResponse, - "set_header": w.apiSetHeader, - "set_status": w.apiSetStatus, - - // 变量 - "var": w.apiVar, - "set_var": w.apiSetVar, - - // 控制流 - "exit": w.apiExit, - "sleep": w.apiSleep, // 异步 sleep - "throw": w.apiThrow, - - // 日志 - "log": w.apiLog, - "err": w.apiLogErr, - "warn": w.apiLogWarn, - "info": w.apiLogInfo, - }) - - w.L.SetGlobal("lolly", lollyMod) - - // 兼容 nginx 命名(可选) - w.L.SetGlobal("ngx", lollyMod) -} - -// apiSay - 输出内容 -func (w *LuaWorker) apiSay(L *glua.LState) int { - ctx := getRequestCtx(L) // 从 LState 获取请求上下文 - str := L.CheckString(1) - ctx.Output = append(ctx.Output, str...) - return 0 -} - -// apiGetURI - 获取请求 URI -func (w *LuaWorker) apiGetURI(L *glua.LState) int { - ctx := getRequestCtx(L) - uri := string(ctx.Request.URI().Path()) - L.Push(glua.LString(uri)) - return 1 -} - -// apiSleep - 异步睡眠(yield 实现) -func (w *LuaWorker) apiSleep(L *glua.LState) int { - ctx := getRequestCtx(L) - ms := L.CheckInt(1) - - // 创建定时器,yield 当前协程 - ctx.Coroutine.ResumeFunc = func() { - // 定时器到期后 resume - ctx.Worker.ResumeCoroutine(ctx.Coroutine) - } - - // 注册定时器 - go func() { - time.Sleep(time.Duration(ms) * time.Millisecond) - ctx.Coroutine.ResumeFunc() - }() - - // Yield - L.Yield(glua.LNumber(ms)) - return 0 -} -``` - -### 6.5 请求上下文绑定 - -```go -// internal/lua/context.go - -// 请求开始时绑定上下文 -func (w *LuaWorker) NewRequestCtx(req *fasthttp.RequestCtx) *LuaRequestCtx { - ctx := &LuaRequestCtx{ - Worker: w, - Request: req, - Variables: make(map[string]string), - Phase: PhaseAccess, - Ctx: req, - } - - // 创建请求协程 - ctx.Coroutine = w.coroPool.Acquire() - ctx.Coroutine.Parent = nil - - // 绑定到 LState(使用 exdata 模式) - // gopher-lua 不支持 exdata,使用全局变量 - ctx.Coroutine.Thread.SetGlobal("__lolly_req", glua.LUserData{ - Value: ctx, - Metatable: w.L.NewTable(), // 可设置 __index 方法 - }) - - return ctx -} - -// 从 LState 获取请求上下文 -func getRequestCtx(L *glua.LState) *LuaRequestCtx { - ud := L.GetGlobal("__lolly_req") - if ud == glua.LNil { - return nil - } - return ud.(*glua.LUserData).Value.(*LuaRequestCtx) -} -``` - -### 6.6 Yield/Resume 与 Go 异步集成 - -**关键设计**: Lua yield → Go channel → 恢复执行 - -```go -// internal/lua/coroutine.go - -type CoroutinePool struct { - L *glua.LState - free chan *LuaCoroutine - resume chan *LuaCoroutine // 恢复队列 -} - -func (p *CoroutinePool) Acquire() *LuaCoroutine { - select { - case co := <-p.free: - return co - default: - // 创建新协程 - thread, _ := p.L.NewThread() - return &LuaCoroutine{ - Thread: thread, - Status: StatusSuspended, - } - } -} - -// 执行脚本(支持 yield) -func (ctx *LuaRequestCtx) RunScript(script string) error { - // 获取或编译字节码 - proto, err := ctx.Worker.codeCache.GetOrCompile(script) - if err != nil { - return err - } - - fn := ctx.Coroutine.Thread.NewFunctionFromProto(proto) - - // 开始执行 - state, err, _ := ctx.Worker.L.Resume(ctx.Coroutine.Thread, fn) - - for state == glua.ResumeYield { - // 协程 yield,等待恢复信号 - ctx.Coroutine.Status = StatusSuspended - - // 等待 ResumeFunc 触发 - select { - case <-ctx.Worker.coroPool.resume: - // 恢复执行 - state, err, _ = ctx.Worker.L.Resume(ctx.Coroutine.Thread) - case <-ctx.Ctx.Done(): - // 请求超时/取消 - return ctx.Ctx.Err() - } - } - - if state == glua.ResumeError { - return err - } - - return nil -} -``` - -### 6.7 中间件集成 - -```go -// internal/middleware/lua_middleware.go - -package middleware - -import ( - "rua.plus/lolly/internal/lua" - "github.com/valyala/fasthttp" -) - -type LuaMiddleware struct { - worker *lua.LuaWorker - config LuaConfig -} - -func (m *LuaMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler { - return func(ctx *fasthttp.RequestCtx) { - // 创建 Lua 请求上下文 - luaCtx := m.worker.NewRequestCtx(ctx) - - // 执行 access_by_lua - if m.config.AccessScript != "" { - if err := luaCtx.RunScript(m.config.AccessScript); err != nil { - ctx.Error("Lua error: "+err.Error(), 500) - return - } - } - - // 执行 content_by_lua(如果有) - if m.config.ContentScript != "" { - luaCtx.Phase = lua.PhaseContent - if err := luaCtx.RunScript(m.config.ContentScript); err != nil { - ctx.Error("Lua error: "+err.Error(), 500) - return - } - // 输出 Lua 内容 - ctx.Write(luaCtx.Output) - return - } - - // 继续下一个 handler - next(ctx) - - // 执行 log_by_lua - if m.config.LogScript != "" { - luaCtx.Phase = lua.PhaseLog - luaCtx.RunScript(m.config.LogScript) - } - } -} -``` - ---- - -## 七、实现路线图 - -### 7.1 阶段一:基础嵌入(Week 1-2) - -| 任务 | 文件 | 说明 | -|------|------|------| -| 添加 gopher-lua 依赖 | `go.mod` | `github.com/yuin/gopher-lua` | -| 创建 LuaWorker | `internal/lua/worker.go` | 单 VM 管理 | -| 代码缓存 | `internal/lua/cache.go` | 字节码缓存 | -| 基础 API 注入 | `internal/lua/api.go` | say/print/get_uri 等 | - -### 7.2 阶段二:请求集成(Week 3-4) - -| 任务 | 文件 | 说明 | -|------|------|------| -| LuaRequestCtx | `internal/lua/context.go` | 请求上下文绑定 | -| LuaMiddleware | `internal/middleware/lua_middleware.go` | 中间件集成 | -| 配置支持 | `internal/config/lua.go` | Lua 指令解析 | - -### 7.3 阶段三:异步支持(Week 5-6) - -| 任务 | 文件 | 说明 | -|------|------|------| -| CoroutinePool | `internal/lua/coroutine.go` | 协程池 | -| Yield/Resume | `internal/lua/coroutine.go` | 异步 sleep 等 | -| LChannel 集成 | `internal/lua/channel.go` | Go channel 操作 | - -### 7.4 阶段四:高级功能(Week 7-10) - -| 任务 | 说明 | -|------|------| -| 共享内存 (shdict) | Go sync.Map + Lua Table API | -| Cosocket | 非阻塞 TCP socket | -| 子请求 | 内部 location capture | -| Timer | ngx.timer.at 实现 | -| Balancer | 动态负载均衡 | - ---- - -## 八、性能考量 - -### 8.1 性能优化点 - -| 优化 | 方法 | -|------|------| -| **字节码缓存** | 预编译脚本,跨请求复用 | -| **协程池** | 预创建协程,避免频繁创建销毁 | -| **减少 GC** | 使用 LTable 而非大量小对象 | -| **并行执行** | 多 worker 独立 VM,无锁竞争 | - -### 8.2 性能基准 - -```go -// 建议基准测试 -func BenchmarkLuaSimple(b *testing.B) { - L := lua.NewState() - defer L.Close() - L.DoString("function test() return 1 + 1 end") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - L.CallByParam(lua.P{Fn: L.GetGlobal("test")}) - } -} - -func BenchmarkLuaWithCtx(b *testing.B) { - // 带请求上下文的基准 -} -``` - ---- - -## 九、安全考虑 - -### 9.1 安全沙箱 - -```go -// 禁用危险库 -L := lua.NewState(lua.Options{SkipOpenLibs: true}) - -// 只加载安全库 -safeLibs := []glua.LGFunction{ - glua.OpenBase, // 基础 - glua.OpenTable, // 表操作 - glua.OpenString, // 字符串 - glua.OpenMath, // 数学 - // 禁用: OpenOS, OpenIO, OpenPackage (部分) -} -``` - -### 9.2 资源限制 - -```go -// Context 超时 -ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) -L.SetContext(ctx) - -// 内存限制(通过 Options) -L := lua.NewState(lua.Options{ - RegistryMaxSize: 1024 * 1024, // 限制注册表大小 -}) - -// CPU 限制(通过 goroutine 监控) -go func() { - time.Sleep(5 * time.Second) - cancel() // 强制取消 -}() -``` - ---- - -## 十、参考资源 - -- [gopher-lua GitHub](https://github.com/yuin/gopher-lua) - 主仓库 -- [gopher-lua API 文档](https://pkg.go.dev/github.com/yuin/gopher-lua) - GoDoc -- [Lua 5.1 手册](https://www.lua.org/manual/5.1/) - 语言参考 -- [lua-nginx-module 文档](../lua-nginx-module/) - 架构参考(已生成) \ No newline at end of file diff --git a/docs/01-nginx-overview.md b/docs/nginx/01-nginx-overview.md similarity index 100% rename from docs/01-nginx-overview.md rename to docs/nginx/01-nginx-overview.md diff --git a/docs/02-nginx-installation.md b/docs/nginx/02-nginx-installation.md similarity index 100% rename from docs/02-nginx-installation.md rename to docs/nginx/02-nginx-installation.md diff --git a/docs/03-nginx-http-core.md b/docs/nginx/03-nginx-http-core.md similarity index 100% rename from docs/03-nginx-http-core.md rename to docs/nginx/03-nginx-http-core.md diff --git a/docs/04-nginx-proxy-loadbalancing.md b/docs/nginx/04-nginx-proxy-loadbalancing.md similarity index 100% rename from docs/04-nginx-proxy-loadbalancing.md rename to docs/nginx/04-nginx-proxy-loadbalancing.md diff --git a/docs/05-nginx-ssl-https.md b/docs/nginx/05-nginx-ssl-https.md similarity index 100% rename from docs/05-nginx-ssl-https.md rename to docs/nginx/05-nginx-ssl-https.md diff --git a/docs/06-nginx-rewrite.md b/docs/nginx/06-nginx-rewrite.md similarity index 100% rename from docs/06-nginx-rewrite.md rename to docs/nginx/06-nginx-rewrite.md diff --git a/docs/07-nginx-compression-caching.md b/docs/nginx/07-nginx-compression-caching.md similarity index 100% rename from docs/07-nginx-compression-caching.md rename to docs/nginx/07-nginx-compression-caching.md diff --git a/docs/08-nginx-logging-monitoring.md b/docs/nginx/08-nginx-logging-monitoring.md similarity index 100% rename from docs/08-nginx-logging-monitoring.md rename to docs/nginx/08-nginx-logging-monitoring.md diff --git a/docs/09-nginx-security.md b/docs/nginx/09-nginx-security.md similarity index 100% rename from docs/09-nginx-security.md rename to docs/nginx/09-nginx-security.md diff --git a/docs/10-nginx-stream-tcp-udp.md b/docs/nginx/10-nginx-stream-tcp-udp.md similarity index 100% rename from docs/10-nginx-stream-tcp-udp.md rename to docs/nginx/10-nginx-stream-tcp-udp.md diff --git a/docs/11-nginx-mail-proxy.md b/docs/nginx/11-nginx-mail-proxy.md similarity index 100% rename from docs/11-nginx-mail-proxy.md rename to docs/nginx/11-nginx-mail-proxy.md diff --git a/docs/12-nginx-performance-tuning.md b/docs/nginx/12-nginx-performance-tuning.md similarity index 100% rename from docs/12-nginx-performance-tuning.md rename to docs/nginx/12-nginx-performance-tuning.md diff --git a/docs/13-git-commit-guide.md b/docs/nginx/13-git-commit-guide.md similarity index 100% rename from docs/13-git-commit-guide.md rename to docs/nginx/13-git-commit-guide.md diff --git a/docs/14-nginx-grpc-uwsgi.md b/docs/nginx/14-nginx-grpc-uwsgi.md similarity index 100% rename from docs/14-nginx-grpc-uwsgi.md rename to docs/nginx/14-nginx-grpc-uwsgi.md diff --git a/docs/15-nginx-advanced-features.md b/docs/nginx/15-nginx-advanced-features.md similarity index 100% rename from docs/15-nginx-advanced-features.md rename to docs/nginx/15-nginx-advanced-features.md diff --git a/docs/16-nginx-internal-redirect.md b/docs/nginx/16-nginx-internal-redirect.md similarity index 100% rename from docs/16-nginx-internal-redirect.md rename to docs/nginx/16-nginx-internal-redirect.md diff --git a/docs/17-nginx-mirror-slice.md b/docs/nginx/17-nginx-mirror-slice.md similarity index 100% rename from docs/17-nginx-mirror-slice.md rename to docs/nginx/17-nginx-mirror-slice.md diff --git a/docs/18-nginx-memcached.md b/docs/nginx/18-nginx-memcached.md similarity index 100% rename from docs/18-nginx-memcached.md rename to docs/nginx/18-nginx-memcached.md diff --git a/docs/19-nginx-http-modules-detail.md b/docs/nginx/19-nginx-http-modules-detail.md similarity index 100% rename from docs/19-nginx-http-modules-detail.md rename to docs/nginx/19-nginx-http-modules-detail.md diff --git a/docs/20-nginx-rate-limiting.md b/docs/nginx/20-nginx-rate-limiting.md similarity index 100% rename from docs/20-nginx-rate-limiting.md rename to docs/nginx/20-nginx-rate-limiting.md diff --git a/docs/21-nginx-http2-http3.md b/docs/nginx/21-nginx-http2-http3.md similarity index 100% rename from docs/21-nginx-http2-http3.md rename to docs/nginx/21-nginx-http2-http3.md diff --git a/docs/22-nginx-third-party-modules.md b/docs/nginx/22-nginx-third-party-modules.md similarity index 100% rename from docs/22-nginx-third-party-modules.md rename to docs/nginx/22-nginx-third-party-modules.md diff --git a/docs/23-nginx-special-modules.md b/docs/nginx/23-nginx-special-modules.md similarity index 100% rename from docs/23-nginx-special-modules.md rename to docs/nginx/23-nginx-special-modules.md diff --git a/docs/24-nginx-core-events.md b/docs/nginx/24-nginx-core-events.md similarity index 100% rename from docs/24-nginx-core-events.md rename to docs/nginx/24-nginx-core-events.md diff --git a/docs/25-nginx-variables-reference.md b/docs/nginx/25-nginx-variables-reference.md similarity index 100% rename from docs/25-nginx-variables-reference.md rename to docs/nginx/25-nginx-variables-reference.md diff --git a/docs/26-nginx-lua-guide.md b/docs/nginx/26-nginx-lua-guide.md similarity index 100% rename from docs/26-nginx-lua-guide.md rename to docs/nginx/26-nginx-lua-guide.md diff --git a/docs/27-nginx-security-deep-dive.md b/docs/nginx/27-nginx-security-deep-dive.md similarity index 100% rename from docs/27-nginx-security-deep-dive.md rename to docs/nginx/27-nginx-security-deep-dive.md diff --git a/docs/28-nginx-api-gateway.md b/docs/nginx/28-nginx-api-gateway.md similarity index 100% rename from docs/28-nginx-api-gateway.md rename to docs/nginx/28-nginx-api-gateway.md diff --git a/docs/29-nginx-dynamic-config.md b/docs/nginx/29-nginx-dynamic-config.md similarity index 100% rename from docs/29-nginx-dynamic-config.md rename to docs/nginx/29-nginx-dynamic-config.md diff --git a/docs/30-nginx-njs-guide.md b/docs/nginx/30-nginx-njs-guide.md similarity index 100% rename from docs/30-nginx-njs-guide.md rename to docs/nginx/30-nginx-njs-guide.md diff --git a/docs/31-nginx-observability.md b/docs/nginx/31-nginx-observability.md similarity index 100% rename from docs/31-nginx-observability.md rename to docs/nginx/31-nginx-observability.md diff --git a/docs/32-nginx-acme-ssl.md b/docs/nginx/32-nginx-acme-ssl.md similarity index 100% rename from docs/32-nginx-acme-ssl.md rename to docs/nginx/32-nginx-acme-ssl.md diff --git a/docs/33-nginx-mqtt-module.md b/docs/nginx/33-nginx-mqtt-module.md similarity index 100% rename from docs/33-nginx-mqtt-module.md rename to docs/nginx/33-nginx-mqtt-module.md diff --git a/docs/34-nginx-oidc-module.md b/docs/nginx/34-nginx-oidc-module.md similarity index 100% rename from docs/34-nginx-oidc-module.md rename to docs/nginx/34-nginx-oidc-module.md diff --git a/docs/35-nginx-keyval-module.md b/docs/nginx/35-nginx-keyval-module.md similarity index 100% rename from docs/35-nginx-keyval-module.md rename to docs/nginx/35-nginx-keyval-module.md diff --git a/docs/36-nginx-streaming-media.md b/docs/nginx/36-nginx-streaming-media.md similarity index 100% rename from docs/36-nginx-streaming-media.md rename to docs/nginx/36-nginx-streaming-media.md diff --git a/docs/37-nginx-dav-module.md b/docs/nginx/37-nginx-dav-module.md similarity index 100% rename from docs/37-nginx-dav-module.md rename to docs/nginx/37-nginx-dav-module.md diff --git a/docs/38-nginx-zone-sync-module.md b/docs/nginx/38-nginx-zone-sync-module.md similarity index 100% rename from docs/38-nginx-zone-sync-module.md rename to docs/nginx/38-nginx-zone-sync-module.md diff --git a/docs/39-nginx-tunnel-module.md b/docs/nginx/39-nginx-tunnel-module.md similarity index 100% rename from docs/39-nginx-tunnel-module.md rename to docs/nginx/39-nginx-tunnel-module.md