openresty-minifiers
名稱
openresty-minifiers - 由 OpenResty Inc. 開發的高效能壓縮庫,支援壓縮 HTML、CSS 和 JavaScript 檔案。
本庫依賴私有庫 replace-filter-plus 模組。
目錄
說明
OpenResty Minifiers 包含三個壓縮器,分別用於最佳化不同型別的網頁內容:
- JS 壓縮器:用於壓縮 JavaScript 檔案。通過移除不必要的空白字元、換行符和註釋,減小檔案體積,加快網頁載入速度。
- CSS 壓縮器:與 JS 壓縮器類似,用於壓縮 CSS 檔案。通過移除不必要的字元和空格來最佳化樣式表,加快頁面渲染速度。
- HTML 壓縮器:用於壓縮 HTML 檔案。通過移除冗餘的 HTML 標籤、空白字元和註釋,減少頻寬佔用,加快頁面載入速度。
這些壓縮器是支援流式處理的專有 Nginx 輸出過濾器模組。其時間複雜度嚴格為 O(n),其中 n 為響應體資料流的長度;空間複雜度嚴格為 O(1),即無論輸入大小如何,記憶體用量都保持恆定。
基準測試
這些壓縮器使用專有的正規表示式編譯器,該編譯器基於我們自主研發的 DFA 最佳化演算法。
在我們的基準測試中,JS-minifier 模組在 Core i9-13900K 的單個 CPU 核心上可達到 120 MB/s 以上的掃描速度。它支援使用固定大小的緩衝區(例如 8KB)進行流式處理。
基準測試結果見:https://openresty.org/misc/re/bench/。
使用示例
在 OpenResty 配置檔案的 http 塊中載入 replace-filter-plus 模組,然後在 init_by_lua_block 塊中載入 openresty-minifiers 模組。
http {
...
load_module /usr/local/openresty/nginx/modules/ngx_http_replace_filter_module.so;
init_by_lua_block { require "resty.replace" }
...
}
可以根據需要載入 min-js.so、min-html.so 和 min-css.so,分別用於壓縮 JavaScript、HTML 和 CSS 檔案:
js-minifier
http {
replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so
/usr/local/openresty-minifiers/tpls/min-js.tpl;
server {
...
location ~ \.js$ {
replace_filter_types application/javascript;
replace_filter_max_buffered_size 8k;
access_by_lua_block {
local ok, err = require "resty.replace".pick("min-js")
if not ok then
error("failed to pick replace prog: " .. err)
end
}
}
}
}
html-minifier
http {
replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so
/usr/local/openresty-minifiers/tpls/min-html.tpl;
server {
...
location ~ \.html$ {
replace_filter_types text/html;
replace_filter_max_buffered_size 8k;
access_by_lua_block {
local ok, err = require "resty.replace".pick("min-html")
if not ok then
error("failed to pick replace prog: " .. err)
end
}
}
}
}
css-minifier
http {
replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so
/usr/local/openresty-minifiers/tpls/min-css.tpl;
server {
...
location ~ \.css$ {
replace_filter_types text/css;
replace_filter_max_buffered_size 8k;
access_by_lua_block {
local ok, err = require "resty.replace".pick("min-css")
if not ok then
error("failed to pick replace prog: " .. err)
end
}
}
}
}
安裝
配置軟體倉庫
首先,需要配置二進位制包倉庫。按照以下命令進行配置。(將命令中的 CLIENT_TOKEN 替換為訂閱郵件中的有效令牌)
curl -o get-xray-priv-lib-repo.sh https://pkg2.openresty.com/scripts/get-xray-priv-lib-repo.sh
sudo bash get-xray-priv-lib-repo.sh -l openresty-minifiers -t CLIENT_TOKEN
安裝軟體包
注意:以下安裝說明適用於 OpenResty 1.21.4.x。
對於使用 yum 包管理器的作業系統,執行以下命令安裝私有庫。
sudo yum install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4
對於使用 dnf 包管理器的作業系統,執行以下命令安裝私有庫。
sudo dnf install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4
對於使用 apt 包管理器的作業系統,執行以下命令安裝私有庫。
sudo apt-get install -y openresty-minifiers replace-filter-plus-nginx-module-1.21.4
指令
以下指令由 openresty-minifiers 依賴的 replace-filter-plus 模組提供。
replace_filter_preload
語法: replace_filter_preload <so_path> <templatefile>;
預設值: 無
上下文: http
載入由 or-regex 預先生成的 .so 檔案和模板檔案,並在初始化階段將其插入雜湊表。提取 so_path 中的檔名並去除副檔名,作為雜湊表的鍵。
示例如下:
http {
replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so /usr/local/openresty-minifiers/tpls/min-html.tpl;
replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so /usr/local/openresty-minifiers/tpls/min-css.tpl;
replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so /usr/local/openresty-minifiers/tpls/min-js.tpl;
}
例如,從 /usr/local/openresty-minifiers/lib/min-html.so 中提取 min-html 作為雜湊表的鍵。
模板檔案格式如下:
"$&" g
"$&" g
"" g
引號規則應遵循 Nginx 配置檔案的字串語法或類似規則。
replace_filter_types
語法: replace_filter_types <mime-type> …
預設值: replace_filter_types text/html
上下文: http, server, location, location if
階段: 響應體輸出過濾階段
指定需要處理的一種或多種 MIME 型別(由 Content-Type 響應頭給出)。
預設只處理 text/html 型別的響應。
Lua 介面顯式選擇的程式會繞過此檢查,呼叫者必須自行根據響應型別選擇合適的程式。
replace_filter_max_buffered_size
語法: replace_filter_max_buffered_size <size>
預設值: replace_filter_max_buffered_size 8k
上下文: http, server, location, location if
階段: 響應體輸出過濾階段
限制模組在執行時緩衝的資料總大小,預設為 8k。
replace_filter_last_modified
語法: replace_filter_last_modifiled keep | clear
預設值: replace_filter_last_modified clear
上下文: http, server, location, location if
階段: 響應體輸出過濾階段
控制如何處理已有的 Last-Modified 響應頭。
預設情況下,如果存在 Last-Modified 響應頭,模組會將其清除。可以通過以下配置始終保留原有的 Last-Modified 響應頭:
replace_filter_last_modified keep;
replace_filter_skip
語法: replace_filter_skip value
預設值: 無
上下文: http, server, location, location if
階段: 響應頭輸出過濾階段
控制是否跳過當前響應的替換。值為空字串或 "0" 時不跳過,其他值均跳過;支援包含 NGINX 變數的值。
Lua API
pick() 在請求的 access 階段提前選擇預載入的程式,隨後由模組的 header filter 判斷是否實際啟用。enable() 在 header_filter_by_lua_block 中根據已經取得的響應頭立即判斷並啟用,適合根據源站返回的 Content-Type 選擇壓縮器。
| 專案 | pick(name) | enable(name) |
|---|---|---|
| 呼叫階段 | access_by_lua_block | header_filter_by_lua_block |
| 適用場景 | 請求階段已能確定程式 | 需要根據響應頭選擇程式 |
| 檢查響應及調整響應頭 | 後續由模組的 header filter 執行 | 呼叫時立即執行 |
| 成功返回值 | true:僅表示選擇成功,最終仍可能跳過 | true:已啟用 |
| 跳過或失敗返回值 | nil, err:選擇失敗 | false, reason:跳過;nil, err:失敗 |
響應處理條件
- Lua 選擇程式時繞過
replace_filter_types。呼叫者必須保證程式適合響應的 MIME 型別,不能依賴該指令保護 JSON、圖片等響應。 - 非空
Content-Encoding(例如gzip)、Content-Length: 0或值為真的replace_filter_skip會使響應跳過替換。跳過時保留響應體及相關響應頭。 - 啟用時清除
Content-Length和Accept-Ranges;預設清除Last-Modified,配置replace_filter_last_modified keep時保留。 - 只處理主請求。子請求的響應頭、響應體直接透傳,配置
replace_filter的子請求也不執行替換。通過ngx.location.capture()捕獲原文後,若由主請求輸出,仍可在主請求上替換一次。 - 同一請求只能選擇一個程式。A 已啟用時,重複呼叫
enable(A)返回true。支援先pick(A)再enable(A);如果 A 尚未啟用,則檢查響應條件後再啟用。選擇不同程式仍返回nil, "current request already set prog"。
在 access 階段已能確定程式時,使用 pick();需要根據響應頭選擇程式時,在 header_filter_by_lua_block 中使用 enable(),由介面完成響應檢查和響應頭調整。
find_prog_id(name)
根據 replace_filter_preload 載入的 .so 檔名(去除副檔名)查詢程式 ID。例如 min-html.so 對應 min-html。name 必須為字串,未找到時返回 -1。
set_prog_by_id(r, prog_id)
在 access 階段選擇程式。r 是由 require("resty.core.base").get_request() 獲得的請求指標,prog_id 是 find_prog_id() 返回的 ID。
成功返回 nil,失敗返回錯誤字串。子請求返回 "subrequests are not supported",不設定程式。選擇成功並不表示響應最終一定會被改寫;仍需通過後續的響應檢查。
pick(name)
pick(name) 是先查詢程式 ID、再為當前請求呼叫 set_prog_by_id() 的便捷介面。只快取成功的查詢。
access_by_lua_block {
local ok, err = require("resty.replace").pick("min-js")
if not ok then
ngx.log(ngx.ERR, "failed to pick minifier: ", err)
end
}
成功返回 true,失敗返回 nil, err,不返回 false。未知名稱返回 nil, "replace program not found: <name>";為子請求選擇已知程式時返回 nil, "subrequests are not supported"。
enable(name)
僅在 header_filter_by_lua_block 中呼叫。根據當前響應檢查跳過條件,成功後立即啟用並調整響應頭,無需呼叫者手動刪除這些頭。
| 返回值 | 含義 |
|---|---|
true | 指定程式已啟用,包括呼叫前已啟用的情況。 |
false, "empty response body" | Content-Length 為 0。 |
false, "response body is content-encoded" | Content-Encoding 非空。 |
false, "replace_filter_skip is true" | replace_filter_skip 求值為真。 |
false, "subrequests are not supported" | 當前請求是子請求。 |
nil, err | 未知程式、程式衝突、非法呼叫階段或其他錯誤。 |
返回 false 時不選擇新程式,也不調整響應頭;之前通過 pick() 選擇的程式仍保留,但未啟用。未知程式返回 replace program not found: <name>;錯誤呼叫階段返回 API disabled in the current context。錯誤引數型別會丟擲 Lua 錯誤。
指定程式已啟用時,重複呼叫直接返回 true,不會再次檢查響應條件或調整響應頭。呼叫仍須位於 header-filter 階段,且響應頭尚未傳送。
如果此前尚未選擇程式,適用於當前響應的原生 replace_filter 規則優先於 enable(),包括從上層配置繼承的規則。無論模組順序如何,此時呼叫均返回 nil, "current request already set prog"。如果響應型別不匹配 replace_filter_types,原生規則不會阻止 Lua 啟用程式。對於跳過、帶內容編碼或空響應的情況,仍返回 false, reason。
enable_prog_by_id(r, prog_id)
enable() 的按 ID 呼叫形式。r 和 prog_id 的含義與 set_prog_by_id() 相同;呼叫階段、響應檢查以及 true / false, reason / nil, err 返回約定與 enable() 相同。非法 ID 返回 nil, "invalid prog id"。
根據響應頭選擇壓縮器
下面的示例要求所用版本支援 enable(),並假定過濾模組已載入且上游 backend 已配置。它接受帶引數的 MIME 型別(例如 text/css; charset=utf-8),跳過未列出的型別,並使用 enable() 根據最終響應頭啟用壓縮器。發往源站的 Accept-Encoding 被清空;如果源站仍返回編碼後的響應,模組會跳過它。
http {
replace_filter_preload /usr/local/openresty-minifiers/lib/min-html.so
/usr/local/openresty-minifiers/tpls/min-html.tpl;
replace_filter_preload /usr/local/openresty-minifiers/lib/min-css.so
/usr/local/openresty-minifiers/tpls/min-css.tpl;
replace_filter_preload /usr/local/openresty-minifiers/lib/min-js.so
/usr/local/openresty-minifiers/tpls/min-js.tpl;
server {
location / {
proxy_set_header Accept-Encoding "";
proxy_pass http://backend;
header_filter_by_lua_block {
local content_type = ngx.header.content_type or ""
local match = ngx.re.match(content_type, [[^\s*([^;\s]+)]], "jo")
if not match then
return
end
local mime = string.lower(match[1])
local name
if mime == "text/html" then
name = "min-html"
elseif mime == "text/css" then
name = "min-css"
elseif mime == "text/javascript"
or mime == "application/javascript"
then
name = "min-js"
else
return
end
local ok, err = require("resty.replace").enable(name)
if ok == nil then
ngx.log(ngx.ERR, "failed to enable minifier: ", err)
end
}
}
}
}
版權與許可
版權所有 (C) OpenResty Inc.,保留所有權利。
本軟體為專有軟體,嚴禁以任何形式再分發或共享。