博客 / 詳情

返回

架構設計--openresty生命週期

openresty的生命週期,各個階段都做了那些事情:

  1. 初始化階段 init_by_lua Worker 進程啓動時執行一次。加載全局配置、初始化共享字典
  2. SSL 握手階段 ssl_certificate_by_lua SSL/TLS 握手時執行。動態加載 SSL 證書
  3. 請求重寫階段 rewrite_by_lua 修改請求 URI、重定向。URL 重寫、強制 HTTPS
  4. 訪問控制階段 access_by_lua 請求訪問權限校驗。IP 黑名單、JWT 鑑權、限流
  5. 內容生成階段 content_by_lua 生成響應內容(若需代理到後端,通常在此階段發起子請求) 動態響應、直接返回 Lua 計算結果
  6. 響應頭過濾階段 header_filter_by_lua 修改響應頭。添加 CORS 頭、刪除敏感頭
  7. 響應體過濾階段 body_filter_by_lua 修改響應體(可能被多次調用,因響應體分塊傳輸) 響應內容脱敏、壓縮
  8. 日誌記錄階段 log_by_lua 請求結束時記錄日誌

該過程圖示:
image.png


代碼案例
鑑權:檢查請求頭中的 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)
        }
    }
}
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.