下面给你一份 超详尽的 njs(NGINX JavaScript 引擎)入门 + 进阶教程,从背景、原理、安装、核心 API、实战示例、注意事项等全面覆盖。你可以把它当作自己的备忘手册。
(注:本文参考官方文档与社区资料)
目录
- 背景与定位
- 支持环境与版本兼容
- 安装与启用
- 配置简介
- 核心模型与执行流程
- njs API & 对象模型
- 示例:从简单到复杂
- 高级特性
- 性能与资源注意点
- 调试与测试
- 社区资源与示例项目
- 小结 + 使用建议
1. 背景与定位
- 什么是 njs?
njs 是 NGINX 提供的一个 JavaScript 引擎模块,允许在 nginx 配置中嵌入 JavaScript 脚本,对 HTTP / Stream 流程进行扩展和 “编程式控制”。(nginx.org)
换句话说,njs 就像一个轻量级的 JS 运行时,内嵌在 nginx 的 worker 里,用来处理请求、响应、过滤、hook 等逻辑。(GitHub) - 它能做什么?为什么要用它?
- 在请求达到后端之前做安全校验、访问控制
- 动态修改响应头、响应体
- 实现自定义 content handler(响应生成)
- 在 stream / TCP / UDP 流中插入 JS 逻辑
- 执行异步请求(fetch / HTTP 调用)
- 实现过滤、路由、度量、缓存等
- 与 NGINX 自身指令结合:可以更灵活地做控制,而不用修改 C 模块
- 与传统方式的区别
通常要做这些功能,你要写 nginx 模块(用 C)、用 Lua(OpenResty)、或在上层业务里做。njs 能让很多逻辑直接在 nginx 层完成、减少网络往返、加快处理。
2. 支持环境与版本兼容
- njs 是作为 nginx 的 动态模块 提供,可插入到 nginx Open Source 或 NGINX Plus 中。(GitHub)
- 自 v0.8.6 起,支持选择不同的 JavaScript 引擎(默认 njs 引擎,也支持 QuickJS)(nginx.org)
- 兼容性注意事项:
3. 安装与启用
安装
不同系统下安装方式略有不同。以 Linux 为例:
- Ubuntu / Debian:
sudo apt update
sudo apt install nginx-module-njs
- RHEL / CentOS / RedHat:
sudo yum install nginx-module-njs
或在某些发行版上使用 dnf
。(GitHub)
- 在 Alpine 或较轻量系统也有对应包。(docs.nginx.com)
- 也可以从源代码编译,作为 nginx 的动态模块加入,或编译为 CLI 使用。(GitHub)
启用 / 加载模块
在 nginx 配置文件的最顶级(main)上下文,加载模块:
load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;
然后重载 nginx。(docs.nginx.com)
确认配置:
nginx -t
nginx -s reload
4. 配置简介 + 指令
njs 在 nginx 配置中通过一些指令来连接 JS 逻辑与请求处理流程。以下是一些核心指令。
指令 | 上下文 | 说明 / 用法 | 版本 / 备注 |
---|---|---|---|
js_import <module> | http / server / location | 导入 JS 模块文件以供使用 | 基础用法 |
js_content module.function | location | 将某个 JS 函数作为内容处理 handler | 使用在 HTTP 模式 |
js_set | location | 用 JS 表达式设置一个 nginx 变量 | — |
js_body_filter / js_header_filter | location | 使用 JS 逻辑来过滤响应体 / 响应头 | — |
js_preread | stream / TCP 上下文 | 在接收到数据前执行 JS 处理 | — |
`js_engine <njs | qjs>` | http / server / location | 选择 JS 引擎:njs 或 QuickJS |
js_context_reuse <number> | http / server / location | 设置 JS 上下文重用数量(针对 QuickJS 引擎) | v0.8.6+ (nginx.org) |
js_fetch_* 系列指令 | http / server / location | 控制 njs 中 fetch 功能的 buffer 大小、超时、协议等 | (nginx.org) |
js_path <path> | http / server / location | 设置额外的 JS 模块路径 | (nginx.org) |
js_periodic module.func [interval=..] [jitter=..] [worker_affinity=..] | location | 定期在 worker 进程中执行某个 JS 函数 | v0.8.1+ (nginx.org) |
示例(简化):
http {
load_module modules/ngx_http_js_module.so;
load_module modules/ngx_stream_js_module.so;
js_import mymod from /etc/nginx/njs/mymod.js;
server {
listen 80;
location /hello {
js_content mymod.hello;
}
}
}
5. 核心模型与执行流程
要真正用好 njs,理解其执行模型与请求生命周期中的挂钩点很关键。
请求流程中的挂钩点
以 HTTP 为例(stream 模式类似):
- 客户端发起请求
- nginx 核心处理请求,按 location 匹配
- 如果该 location 配置了
js_content
,就进入 njs 逻辑,由 JS 函数生成响应 - JS 函数可以访问
r
(request/session)对象,读取请求参数、头、body 等 - JS 可以决定直接返回(
r.return()
),也可继续委托 upstream - 在响应过程中,还可以在
js_header_filter
或js_body_filter
中对响应头/体做处理 - 完成后,返回响应给客户端
在 stream / TCP / UDP 模式下,类似流程也可在 js_preread
阶段插入逻辑。
上下文与状态
- 每个请求由一个 JS 上下文(VM 实例或环境)处理
- 为了性能,nginx 会重用上下文(context reuse),减少创建销毁开销(特别在 QuickJS 引擎下)(nginx.org)
- JS 模块可以导出多个函数,nginx 配置中引用它们
- 在 JS 里,对
r
(request/session 对象)、r.variables
(nginx 变量)、r.args
(GET 参数对象)等有访问能力
6. njs API & 对象模型
下面列出一些核心 API、对象、属性和方法(不可能穷尽,建议结合官方 Reference 查文档)(nginx.org)
r 对象(HTTP Request / Session 对象)
这是 JS 脚本中最常用的对象,代表当前请求会话。
常见属性 / 方法:
属性 / 方法 | 说明 |
---|---|
r.args | 请求的 query 参数对象,重复键以数组形式表示 |
r.variables | nginx 变量映射($uri, $args 等) |
r.headersIn | 请求头映射对象 |
r.headersOut | 响应头映射,设置响应头 |
r.method | HTTP 方法,如 “GET”, “POST” |
r.requestBody | 请求体(如果已读取) |
r.return(status, body?) | 终止当前请求,立即返回响应 |
r.error(msg) | 在日志系统里写错误 |
r.done() | 在 body_filter 中,中断后续过滤流程 |
r.subrequest() / r.internalRedirect() | 发起子请求或内部重定向 |
r.log() / r.warn() / r.debug() | 日志相关函数 |
其他模块 / API
- Fetch API / HTTP:njs 支持类似 fetch 的网络请求能力,可以在 JS 中发起 HTTP 调用(对 upstream 或第三方服务)
- Shared Dictionary(共享字典):多个请求之间共享数据存储(键/值)
- 定时 / 周期任务:
js_periodic
指令 - 模块 / import:可以把 JS 代码拆分模块,通过
js_import
导入 - 模块路径 & 模块查找:通过
js_path
指定额外路径 - QuickJS 支持特性:BigInt、Proxy、异步生成器等(视版本支持)(nginx.org)
- njs 内部 / 辅助 API:如
njs.dump()
,console.dump()
等(部分版本)(nginx.org)
举例:读取 GET 参数并返回
export default {
hello: function (r) {
let name = r.args.name || "world";
r.return(200, "Hello " + name);
}
}
然后 nginx 配置:
js_import mymod from /etc/nginx/njs/mymod.js;
server {
location /hello {
js_content mymod.hello;
}
}
7. 示例:从简单到复杂
下面按层级给你几个示例,帮助加深理解。
示例 1:Hello World
JS 脚本 hello.js
:
export default {
hello: function(r) {
r.return(200, "Hello, njs!");
}
}
nginx 配置:
load_module modules/ngx_http_js_module.so;
http {
js_import hello from /etc/nginx/njs/hello.js;
server {
listen 8080;
location /hello {
js_content hello.hello;
}
}
}
访问 http://your-server:8080/hello
→ 显示 “Hello, njs!”
示例 2:参数处理 + JSON 返回
export default {
greet: function(r) {
let name = r.args.name || "Guest";
let obj = { message: "Hello " + name, time: Date.now() };
r.headersOut['Content-Type'] = 'application/json';
r.return(200, JSON.stringify(obj));
}
}
配置中:
location /greet {
js_content greet.greet;
}
请求如 /greet?name=Alice
→ 返回:
{"message":"Hello Alice","time":167...}
示例 3:响应头 / 响应体过滤
假设你希望在返回内容中插入一个 footer 或做修改,可以用 js_body_filter
:
export default {
filter: function(r, data, flags) {
// data 是 Buffer / 字符串片段
// 最终返回修改后的数据
let content = data.toString();
content = content + "\n<!-- Served by njs -->";
return content;
}
}
nginx 配置:
location /static {
js_header_filter filter.filter;
js_body_filter filter.filter;
proxy_pass http://backend;
}
这样,所有经过这个 location 的响应体都会被 append 一个注释。
示例 4:子请求(subrequest)
一种强大用法是在 JS 里发起子请求:
export default {
doSomething: function(r) {
r.subrequest('/api/data', function(res) {
if (res.status != 200) {
r.return(500, 'error upstream');
} else {
r.return(200, "Got data: " + res.responseBody);
}
});
}
}
在 nginx:
location /do {
js_content mymod.doSomething;
}
location /api/data {
proxy_pass http://backend/api/data;
}
这样你可以在一个请求里整合后端多个子服务数据。
8. 高级特性
下面这些特性适合中高级用法:
- 周期定时任务(js_periodic)
可以在后台定时执行 JS 任务(无请求触发)(nginx.org) - 共享字典 / 缓存
多个请求可以共享一个字典存储(key-value),用于计数、缓存、状态保持 - 引擎切换 / QuickJS 支持
通过js_engine qjs
切换到 QuickJS 模式,可以获取更现代 JS 特性(如 BigInt、Proxy 等)(nginx.org) - 上下文重用
在 QuickJS 模式下,可以通过js_context_reuse
设置 JS 上下文对象池数量,减少创建开销。(nginx.org) - 动态模块导入 / 多模块组织
使用js_import
+ ES 模块方式组织 JS 代码,使逻辑清晰可维护 - 异步 / Promise 支持(取决版本)
部分版本支持异步 JS 特性,例如异步函数或 Promise(视 njs 版本支持情况) - 安全 / 沙箱机制
njs 默认限制了一些全局 API,只暴露给 JS 与 nginx 接口。不能像 Node 那样任意访问文件系统等。
9. 性能与资源注意点
njs 虽然灵活,但也有一些资源与性能方面的考量:
- 每个请求创建 / 销毁 JS 上下文代价大 → 使用上下文重用
- JS 执行时间要短,避免阻塞 nginx worker
- JS 逻辑复杂时,可能影响吞吐与延迟
- 注意内存泄漏、闭包、全局变量滥用
- 在高并发场景下,尽量减少 JS 调用的次数
- 避免在 JS 里做大量 CPU 密集型计算
- 使用 js_periodic、共享字典等功能时,要控制资源消耗
10. 调试与测试
- njs 提供一个命令行工具
njs
(CLI),可以在本地执行 JS 代码、调试语法错误(不含 nginx 对象)(GitHub) - 在 JS 里使用
r.log()
,r.error()
,r.debug()
输出到 nginx 日志,便于调试 - 在 nginx 配置里开启错误日志级别为 debug 或 info
- 使用示例项目 + Docker 方式快速试验
- 单元测试 / 模块测试:把 JS 模块抽出,单独在 node 或 CLI 环境测试逻辑
11. 社区资源与示例项目
- 官方 njs 文档(Reference)(nginx.org)
- njs GitHub 仓库(源码 + 文档)(GitHub)
- njs-examples 仓库,包含很多实用样例:HTTP、stream、授权、加密等(GitHub)
- 社区博客 / 入门教程(如 “Create Nginx extensions in JavaScript”)(DEV Community)
- 视频教程(官方 + 社区)(YouTube)
12. 小结 + 使用建议
- njs 是一个非常强大的工具,适合在 nginx 层处理一些轻量逻辑,但不适合用作完整应用逻辑
- 从简单的
js_content
入手,一步步扩展到过滤、子请求、共享字典等 - 在性能敏感场景下,要格外注意 JS 执行成本
- 推荐把复杂逻辑保留给后端服务,把 njs 用作 “辅助 / 边缘处理”
- 开发时多用日志 + CLI 工具 + 示例项目辅助调试
发表回复