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.,保留所有权利。
本软件为专有软件,严禁以任何形式再分发或共享。