OpenResty之指令與常用API


1. 指令

通過 Lua 編寫 Nginx 腳本的基本構建塊是指令。指令常用於指定 Lua 代碼是幾時執行的以及如何使用運行的結果。下圖展示了指令執行的順序。
image

lua_capture_error_log

語法:lua_capture_error_log size
默認:none
上下文:http

啟用一個指定大小的緩沖區來捕獲所有的 Nginx 錯誤日志,而不是保存到文件或磁盤中。

如下,可以使用 k 或 m 表示大小:

lua_capture_error_log 100k;

一個經驗:4KB 緩存可以容納大約 20 個典型的錯誤日志消息。

注意事項:

  • 這個緩存不會增長,如果滿了,則新的錯誤日志消息將會覆蓋緩存中舊的消息。
  • 這個緩存的大小一定要比單個的錯誤日志消息的大小更大。
  • 可以通過lua-resty-core 庫的 ngx.errlog 模塊的 get_logs 函數來讀取 Lua land 緩沖區的消息。這個 Lua API 將返回捕獲的錯誤日志消息,並且將讀取到的消息從全局捕獲緩存區中移除,為任何新的錯誤日志消息騰出空間。基於這個原因,如果用戶讀取緩沖去中的錯誤日志消息足夠快的話不宜將該緩沖區配置得太大。
  • error_log 指令指定的日志級別將會影響該緩沖區的捕獲,該緩沖區僅捕獲級別不低於 error_log 指令指定的日志級別的消息。用戶可以通過 Lua API 函數 errlog.set_filter_level 來動態設置更高的過濾日志級別,這比靜態的 error_log 指令更靈活。
  • 如果沒有使用 ./configure 選項 --with-debug 來編譯 OpenResty 或 Nginx,則無法捕獲調試日志。由於高昂的開銷,在生產版本中強烈建議禁用調試體質。

lua_code_cache

語法:lua_code_cache on | off
默認:lua_lua_cache on
上下文:http, server, location, location if
  • 在 *_by_lua_file 指令(類似 set_by_lua_file 和 content_by_lua_file)或 lua 模塊中使能或禁止 Lua 代碼的 lua 代碼緩存功能。
  • 當關閉時,通過 ngx_lua 提供的每個請求都將在一個單獨的 Lua VM 實例中運行。因此,由 set_by_lua_file,content_by_lua_file,access_by_lua_file 等中引用的 lua 文件都將不會被緩存,所有的 lua 模塊都將從頭開始加載。通過該指令,開發人員可以采用編輯並刷新的模式。
  • 注意,當在 nginx.conf 配置文件中編輯內聯 Lua 代碼時,由於僅有 Nginx 配置文件解析器,所以在 nginx.conf 中內聯寫入的 lua 代碼(如由 set_by_lua,content_by_lua,access_by_lua 和 rewrite_by_lua 指定的 lua 代碼)將不會被更新。僅有的方法是通過發送 HUP 信號來重新加載配置文件或重啟 Nginx。
  • 即使啟用了代碼緩存,也無法緩存由 *_by_lua_file 中的 dofile 或 loadfile 加載的 lua 文件(除非你自己緩存了結果)。通常可以使用 init_by_lua 或 init_by_lua_file 指令來加載所有這些文件,或者通過使這些 Lua 文件成功真正的 lua 模塊並通過 require 來加載它們。
  • ngx_lua 模塊不支持 Apache mod_lua 模塊可用的 stat 模式。
  • 強烈禁止在生產環境中禁用 Lua 代碼緩存,僅可以在開發期間使用。因為它會對整體性能產生負面的影響。例如,在禁用 lua 代碼緩存后,"hello world" Lua 示例的性能可能會下降一個數量級。

lua_package_path

語法:lua_package_path <lua-style-path-str>
默認:LUA_PATH 環境變量內容或者 lua 編譯的默認值
上下文:http
  • 設置由 set_by_lua,content_by_lua 等指定的腳本使用的 lua 模塊搜索路徑。這個路徑字符串是標准的 lua 路徑格式,";;" 常用於表示原始的搜索路徑。
  • 從發行版 v0.5.0rc29 開始,可以在搜索路徑中使用特殊符號 $prefix 或 ${prefix}來指示服務器前綴的路徑,通常在 Nginx 服務器啟動時通過 -p PATH 命令行選項來指定 server prefix。

lua_package_cpath

語法:lua_apckage_cpath <lua-style-cpath-str>
默認:LUA_CPATH 環境變量的內容或 lua 的默認編譯
上下文:http
  • 設置通過由 set_by_lua,content_by_lua 等指定的腳本的 Lua C-module 的搜索路徑。這個 cpath 字符串是標准的 lua cpath 格式,";;" 常用於表示原始的 cpath。
  • 從 v0.5.0rc29 發行版開始,可以在搜索路徑字符串中使用特殊字符 $prefix 或 ${prefix} 來指示 server prefix(該前綴通常在 Nginx 服務器啟動時通過 -p PATH 命令行選項來指定)。

init_by_lua

語法:init_by_lua <lua-script-str>
上下文:http
階段:加載配置
  • 注意在 v0.9.17 發行版以后不鼓勵使用該指令,應使用 init_by_lua_block 指令代替。
  • 當 Nginx master 進程(如果有)在加載 Nginx 配置文件的時候,在全局 Lua VM 級別上運行由參數<lua-script-str>指定的 lua 代碼。
  • 當 Nginx 接收到 HUP 信號並開始重新加載配置文件時,Lua VM 將會被重新創建,且 init_by_lua 也將在新的 VM 上再次運行。如果 lua_code_cache 指令是關閉的(默認打開),init_by_lua 處理程序將在每個請求上都運行一次,因此這這種特殊模式下,總是每個請求創建一個獨立的 Lua VM。
  • 通常,你可以通過該掛鈎在服務器啟動時預加載 Lua 模塊,並利用現代操作系統的寫時復制(COW)優化。這里是一個預加載 Lua 模塊的示例:
# this runs before forking out nginx worker processes:
init_by_lua_block { require "cjson" }

server {
    location = /api {
        content_by_lua_block {
            -- the following requrei() will just return
            -- the already loaded module from package.loaded:
            ngx.say(require "cjson".encode{dog = 5, cat = 6} -- {"dog":5,"cat":6}
        }
    }
}
  • 也可以在這個階段初始化 lua_shared_dict shm 存儲。如下示例:
lua_shared_dict dogs 1m;

init_by_lua_block {
    local dogs = ngx.shared.dogs;
    dogs:set("Tom", 56)
}

server {
    location = /api {
        content_by_lua_block {
            local dogs = ngx.shared.dogs;
            ngx.say(dogs:get("Tom"))
        }
    }
}
  • 但注意,lua_shared_dict shm 共享內存將不會通過重新加載配置(例如通過 HUP 信號)清除。在這種情況下,如果你不想在 init_by_lua 代碼中重新初始化 shm 內存,則需要在 shm 內存中設置一個自定義 flag,並在 init_by_lua 代碼中總是檢測該 flag。
  • 因為該 Lua 代碼在 Nginx fork 出 worker 子進程前運行,所以在這里加載的數據或代碼將享受許多操作系統在所有 worker 進程中提供的寫時復制功能,這將節省大量的內存。
  • 不要在此上下文中初始化你自己的 Lua 全局變量,因為使用的 Lua 全局變量有性能損失,並且可能導致全局命名空間污染(更多細節參閱Lua Variable Scope)。建議的方法是使用正確的 Lua 模塊文件(但不要使用標准 Lua 函數模塊去定義 Lua 模塊,因為它也會污染全局命名空間),同時在 init_by_lua 或其他上下文中調用 require 去加載你自己的模塊文件(require() 會將加載的模塊緩存在全局的 Lua 注冊表 package.loaded 中,因此你的模塊僅在整個 Lua VM 實例中加載一次)。
  • 在該上下文中,僅支持一小部分 Lua 的 Nginx API:
    • 日志 API:ngx.log 和 print
    • 共享字典 API:ngx.shared.DICT
  • 在未來用戶請求中,該上下文將支持更多的 Lua 的 Nginx API。
  • 基本上,你可以在此上下文中安全地使用阻塞 I/O 的 Lua 庫,因為在服務器啟動期間阻塞 master 進程完全是可以的。甚至 Nginx 內核也會在配置加載階段阻塞(至少在解析 upstream 域名時)。
  • 應該非常小心在此上下文中注冊的 Lua 代碼中潛在的安全漏洞,因為 Nginx master 進程通常在 root 用戶下運行。

init_by_lua_block

語法:Init_by_lua_block { lua-script }
上下文:http
階段:加載配置
  • 與 init_by_lua 指令類似,不同之處在於此指令直接在一對花括號({})內部而不是在 Nginx 字符串(需要特殊字符串轉義)中內聯 lua 代碼。如:
init_by_lua_block {
    print("I need no extra escaping here, for example: \r\nblah")
}

init_by_lua_file

語法:init_by_lua_file <path-to-lua-script-file>
上下文:http
階段:加載配置
  • 等同於 init_by_lua。

init_worker_by_lua

語法:init_worker_by_lua <lua-script-str>
上下文:http
階段:啟動 worker 時
  • 在 v0.9.17 版本后不鼓勵使用此指令,改用 init_worker_by_lua_block 指令代替。
  • 當 master 進程使能時,在每個 Nginx worker 進程啟動時運行指定的 Lua 代碼。當 master 進程被禁用時,這個鈎子將在 init_by_lua* 之后運行。
  • 這個鈎子常用於創建每個 worker 重復發生的計時器(通過 ngx.timer.at ),用於后端運行狀況檢查或其他定時工作。如下示例:
init_worker_by_lua '
    local delay = 3 -- in seconds
    local new_timer = ngx.timer.at
    local log = ngx.log
    local ERR = ngx.ERR
    local check
    
    check = function(premature)
        if not premature then
            -- do the health check or other routine work
            local ok, err = new_timer(delay, check)
            if not ok then
                log(ERR, "failed to create timer: ", err)
                return
            end
        end
    end
    
    local hdl, err = new_timer(delay, check)
    if not hdl then
        log(ERR, "failed to create timer: ", err)
        return
    end
';
  • 自 v0.10.12 版本以來,該鈎子不再在 cache manager 和 cache loader 進程中運行。

init_worker_by_lua_block

語法:init_worker_by_lua_block { lua-script }
上下文:http
階段:啟動 worker 時
  • 與 init_worker_by_lua 指令類似,不同之處在於此指令直接在一對花括號({})內部而不是在 NGINX 字符串文字(需要特殊字符轉義)中內聯 Lua 代碼。如下:
init_worker_by_lua_block {
    print("I need no extra escaping here, for example: \r\nblah")
}
  • 自 v0.10.12 版本以來,該鈎子不再在 cache manager 和 cache loader 進程中運行。

init_worker_by_lua_file

語法:init_worker_by_lua_file <lua-file-path>
上下文:http
階段:啟動 worker 時

與 init_worker_by_lua 類似。

content_by_lua

語法:content_by_lua <lua-script-str>
上下文:location,location if
階段:content
  • 注意在 v0.9.17 發行版之后不鼓勵使用此指令,改為使用 content_by_lua_block 指令代替。
  • 作為一個 "content handler" 為每個請求執行在 中指定的 lua 代碼字符串。該 lua 代碼可能會進行 API 調用,並且在獨立的全局環境(例如 sandbox)中作為一個新派生的協程執行。
  • 不要在同一個 location 中同時使用該指令和其他的 content handler 指令。如,該指令和 proxy_pass 指令不應該在通過一個 location 中出現。

content_by_lua_block

語法:content_by_lua_block { lua-script }
上下文:location,location if
階段:content
  • 與 content_by_lua 指令類似,不同之處在於該指令直接在一對花括號({})內聯 lua 源碼,而不是在 Nginx 字符串文件中(它需要特殊字符轉義)。

示例:

content_by_lua_block {
    ngx.say("I need no extra escaping here, for example: \r\nblah")
}

該指令最初是在 v0.9.17 發行版中引入的。

content_by_lua_file

語法:content_by_lua_file <path-to-lua-script-file>
上下文:location,location if
階段:content
  • 等同於 content_by_lua,不同之處在於 指定的文件包含 lua 代碼,從 v0.5.0rc32 發行版開始,將執行 Lua/LuaJIT 字節碼。
  • Nginx 變量可以用在 字符串中以提供靈活性,但是這會帶來一些風險,因此通常不建議這么做。
  • 當給定一個類似 foo/bar.lua 這樣的相對路徑時,會轉變為相對於 server prefix 的絕對路徑(server prefix 是在 Nginx 服務器啟動時,通過 -p PATH 命令行選項執行的)。
  • 當 lua 代碼緩存打開時(默認打開),用戶代碼在第一個請求時被加載一次並緩存,並且每次修改 lua 源碼文件時都必須重新加載 Nginx 配置。可以通過 在 nginx.conf 中使用 lua_code_cache off 在開發期間臨時禁用 lua 代碼緩存,來避免重新加載 Nginx。
  • 在動態分派的文件路徑中支持 Nginx 變量,如下:
# CAUTION: conntents in nginx var must be carefully filtered,
# otherwise there'll be great security risk!
location ~ ^/app/([-_a-zA-Z0-9/]+) {
    set $path $1;
    content_by_lua_file /path/to/lua/app/root/$path.lua;
}

ssl_certificate_by_lua_block

語法:ssl_certificate_by_lua_block { lua-script }
上下文:server
階段:在 SSL 握手之前
  • 當 Nginx 將要與下游 SSL(https)連接開始 SSL 握手時,運行該指令指定的用戶 Lua 代碼。
  • 該指令對於基於每個請求而設置的 SSL 證書鏈和相應的私鑰特別有用。從遠端(如 cosocket API)加載此類非阻塞的握手配置也很有用。此外,還可以在純 Lua 中按要求執行 OCSP stapling handling。
  • 另一個典型的使用情況是在 lua-resty-limit-traffic#readme 庫的幫助下執行非阻塞的 SSL 握手流量控制。
  • 也可以對來自客戶端的 SSL 握手請求做一些有趣的事情,比如拒絕使用 SSLv3 協議的舊的 SSL 客戶端,甚至是選擇性地拒絕。
  • 由 lua-resty-core 庫提供的 ngx.ssl 和 ngx.ocsp Lua 模塊在這種上下文中特別有用。可以使用這兩個 Lua 模塊提供的 Lua API 來操作 SSL 證書鏈和私鑰,以啟動當前 SSL 連接。
  • 但是,當 NGINX/OpenSSL 通過 SSL 會話 ID 或當前 SSL 連接的 TLS 會話 ticket 成功地恢復 SSL 會話時,這個 Lua 處理程序根據沒有運行。也就是說,這個 Lua 處理程序僅在 Nginx 開始完整 SSL 握手時運行。
  • 下面是一個使用 ngx.ssl 模塊同時進行的簡單的示例:
server {
    listen 443 ssl;
    server_name test.com;
    
    ssl_certificate_by_lua_block {
        print("About to initiate a new SSL handshake!")
    }
    
    location / {
        root html;
    }
}
  • 注意,即使你根本不使用此靜態證書和私鑰,你也需要配置 ssl_certificate 和 ssl_certificate_key 指令。這用於滿足 Nginx 配置的占位符,否則在啟動 Nginx 時會看到以下錯誤:
nginx: [emerg] no ssl configured for the server

ssl_certificate_by_lua_file

語法:ssl_certificate_by_lua_file <path-to-lua-script-file>
上下文:server
階段:在 SSL 握手之前
  • 等價於 ssl_certificate_by_lua_block,若給定一個類似 foo/bar.lua 的相對路徑,則該路徑將會被轉變為相對 server prefix(由 Nginx 啟動時通過 -p PATH 選項指定) 的絕對路徑。

2. Nginx API for Lua

ngx.arg

語法:val = ngx.arg[index]
上下文:set_by_lua*, body_filter_by_lua*
  • 當在 set_by_lua* 指令的上下文中使用它時,該表是只讀的,並且保存配置指令的輸入參數:
value = ngx.arg[n]

示例:

location /foo {
    set $a 32;
    set $b 56;
    
    set_by_lua $sum
        'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])'
        $a $b;
    
    echo $sum;
}

輸出為 88,即 32 和 56 的總和。

  • 當在 body_filter_by_lua* 的上下文中使用該表時,第一個元素將輸入數據塊保存到 output filter code 中,第二個參數保存指示整個輸出數據流結束的 "eof" 標志的布爾標志。
  • 傳遞給下游 Nginx 輸出過濾器的數據塊和 "eof" 標志也可以通過將值直接分配給相應的表元素來覆蓋。當將 ngx.arg[1] 設置為 nil 或空的 lua 字符串值時,數據塊將不會被傳遞到下游 Nginx 的輸出過濾中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM