openresty的生命週期,各個階段都做了那些事情:
- 初始化階段 init_by_lua Worker 進程啓動時執行一次。加載全局配置、初始化共享字典
- SSL 握手階段 ssl_certificate_by_lua SSL/TLS 握手時執行。動態加載 SSL 證書
- 請求重寫階段 rewrite_by_lua 修改請求 URI、重定向。URL 重寫、強制 HTTPS
- 訪問控制階段 access_by_lua 請求訪問權限校驗。IP 黑名單、JWT 鑑權、限流
- 內容生成階段 content_by_lua 生成響應內容(若需代理到後端,通常在此階段發起子請求) 動態響應、直接返回 Lua 計算結果
- 響應頭過濾階段 header_filter_by_lua 修改響應頭。添加 CORS 頭、刪除敏感頭
- 響應體過濾階段 body_filter_by_lua 修改響應體(可能被多次調用,因響應體分塊傳輸) 響應內容脱敏、壓縮
- 日誌記錄階段 log_by_lua 請求結束時記錄日誌
該過程圖示:
代碼案例
鑑權:檢查請求頭中的 Authorization 是否有效。
動態路由:根據請求路徑 /api/<service> 轉發到不同的 Go 服務。
統計:記錄請求耗時和狀態碼。
http {
# 初始化共享內存和全局配置
lua_shared_dict auth_cache 10m;
init_by_lua_block {
-- 加載全局依賴
local cjson = require "cjson"
-- 定義路由表(實際可從數據庫讀取)
ngx.shared.auth_cache:set("valid_tokens", cjson.encode({
["token1"] = true,
["token2"] = true
}))
}
server {
listen 80;
# 1. SSL 階段(略,需配置證書)
# 2. 請求重寫階段:規範化路徑
rewrite_by_lua_block {
-- 移除末尾斜槓
if ngx.var.uri:sub(-1) == "/" then
ngx.req.set_uri(ngx.var.uri:sub(1, -2))
end
}
# 3. 訪問控制階段:鑑權
access_by_lua_block {
local token = ngx.req.get_headers()["Authorization"]
if not token then
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- 檢查 Token 有效性(從共享內存讀取)
local valid_tokens = ngx.shared.auth_cache:get("valid_tokens")
if not valid_tokens then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
local tokens = require("cjson").decode(valid_tokens)
if not tokens[token] then
ngx.log(ngx.WARN, "Invalid token: ", token)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
# 4. 內容生成階段:動態路由到 Go 後端
location ~ ^/api/(\w+) {
content_by_lua_block {
local service = ngx.var[1] -- 捕獲路由中的服務名
local go_services = {
user = "http://go-user-service:8080",
order = "http://go-order-service:8080"
}
local backend = go_services[service]
if not backend then
ngx.exit(ngx.HTTP_NOT_FOUND)
end
-- 發起子請求到 Go 後端
local res = ngx.location.capture("/proxy", {
method = ngx.HTTP_GET,
args = ngx.req.get_uri_args(),
body = ngx.req.get_body_data(),
vars = { backend = backend }
})
-- 返回後端響應
ngx.status = res.status
ngx.print(res.body)
}
}
# 隱藏的內部 location,用於實際代理
location /proxy {
internal; # 禁止外部直接訪問
proxy_pass $backend;
proxy_set_header Host $host;
}
# 5. 響應頭過濾階段:添加 CORS
header_filter_by_lua_block {
ngx.header["Access-Control-Allow-Origin"] = "*"
ngx.header["X-Request-ID"] = ngx.var.request_id
}
# 6. 日誌記錄階段:統計請求
log_by_lua_block {
local request_time = tonumber(ngx.var.request_time)
local status = ngx.var.status
ngx.log(ngx.INFO, "Request completed: status=", status, " time=", request_time)
}
}
}