OpenResty之指令與經常使用API

1. 指令

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

lua_capture_error_log

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

啓用一個指定大小的緩衝區來捕獲全部的 Nginx 錯誤日誌,而不是保存到文件或磁盤中。nginx

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

lua_capture_error_log 100k;

一個經驗:4KB 緩存能夠容納大約 20 個典型的錯誤日誌消息。github

注意事項:json

  • 這個緩存不會增加,若是滿了,則新的錯誤日誌消息將會覆蓋緩存中舊的消息。
  • 這個緩存的大小必定要比單個的錯誤日誌消息的大小更大。
  • 能夠經過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 相似。c#

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 發行版中引入的。api

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 的輸出過濾中。
相關文章
相關標籤/搜索