NGINX 内嵌 JavaScript`ngx_http_js_module`(njs)

一、模块简介

ngx_http_js_module(njs)将 JavaScript(子集)内嵌到 NGINX 配置中,可用于:

  • 变量赋值js_setjs_var
  • 内容生成js_content
  • 头部/主体过滤js_header_filterjs_body_filter
  • Fetch 调用:内置异步 HTTP 客户端
  • 定时任务js_periodic
  • 共享内存字典js_shared_dict_zone
  • Crypto:浏览器级别加密操作

它支持 njs 自身引擎,也可切换至 QuickJS(js_engine qjs),并允许灵活配置 SSL、超时、协议等 Fetch 细节。

二、安装与启用

  1. 获取 NGINX 商业版 或自行编译带 --with-http_js_module

  2. njs 包:若自编译,需 --add-dynamic-module=path/to/nginx-module-njs

  3. 验证加载

    nginx -V 2>&1 | grep js_module
    # 应包含 --with-http_js_module
    

三、基础示例:动态变量与内容

3.1 导入与变量

http {
  js_import   http.js;                  # 导入模块
  js_set      $foo   http.foo;          # 设置变量 $foo
  js_set      $hash  http.hash;         # 设置 $hash

  server {
    listen 8000;
    location /var {
      return 200 $foo;                  # 输出 foo()
    }
    location /crypto {
      add_header X-Hash $hash;          # 在头部输出 hash()
      return 200;
    }
  }
}

3.2 http.js 内容

// http.js
export default {
  foo: function(r) {
    return "Hello from njs!";
  },
  hash: async function(r) {
    let digest = await crypto.subtle.digest("SHA-256", r.uri);
    return Buffer.from(digest).toString("hex");
  }
};

四、响应生成:js_content

location 中用 JavaScript 完全接管响应体:

location = /hello {
  js_content   http.hello;
}
// http.js
export function hello(r) {
  r.return(200, `Hello, ${r.args.name||"world"}!`);
}

五、动态 Fetch:服务器间调用

location = /fetch {
  js_content                 http.fetch;
  js_fetch_trusted_certificate /etc/ssl/certs/ca.pem;
  js_fetch_timeout           5s;
}
// http.js
export async function fetch(r) {
  let res = await Promise.all([
    ngx.fetch("https://api1/"),
    ngx.fetch("https://api2/")
  ]);
  let bodies = await Promise.all(res.map(r => r.text()));
  r.return(200, bodies.join("\n---\n"));
}

六、过滤器:修改头部与主体

6.1 清除 Content-Length

location /proxy {
  proxy_pass http://backend;
  js_header_filter   main.clearContentLength;
  js_body_filter     main.toUpper;   # 示例:主体转大写
}
// main.js
export function clearContentLength(r) {
  delete r.headersOut["Content-Length"];
}

export function toUpper(r, data, flags) {
  r.sendBuffer(data.toString().toUpperCase(), flags);
}

注意:修改长度后务必清除 Content-Length,否则客户端会等待错误长度。

七、定时任务:js_periodic

适合健康检查、数据预热、日志采集等后台任务:

location @tasks {
  js_periodic tasks.refreshCache interval=60s jitter=5000 worker_affinity=0101;
}
// tasks.js
export async function refreshCache(s) {
  let data = await ngx.fetch("https://config.service/latest");
  let json = await data.json();
  ngx.shared.config.set("latest", JSON.stringify(json), 3600);
}

八、共享字典:js_shared_dict_zone

在多个 worker 之间共享状态或缓存:

http {
  js_shared_dict_zone zone=config:10m timeout=180s evict type=string;
  js_import tasks.js;

  server {
    location = /config {
      js_content tasks.getConfig;
    }
    location = /update {
      js_content tasks.setConfig;
    }
  }
}
// tasks.js
export function getConfig(r) {
  let val = ngx.shared.config.get("latest") || "{}";
  r.return(200, val);
}
export function setConfig(r) {
  ngx.shared.config.set("latest", r.args.value, 3600);
  r.return(200, "OK");
}

九、性能调优与并发

  • 上下文复用js_context_reuse 256,减少 JS 上下文创建销毁开销
  • 引擎选择js_engine qjs(QuickJS 性能更高)
  • Fetch 缓冲区:根据响应大小调整 js_fetch_max_response_buffer_size
  • SSL 参数js_fetch_ciphersjs_fetch_protocolsjs_fetch_verify_depth 精细控制

十、最佳实践与安全

  1. 避免阻塞:过滤器仅支持同步操作;长耗时逻辑请使用 js_periodic 或后端服务。
  2. 严格验证:Fetch 默认验证证书,确保 js_fetch_trusted_certificate 指向可信根。
  3. 限流保护:配合 limit_reqlimit_conn 保护 JS 路由不被滥用。
  4. 错误捕获:在 JS 中用 try/catch 捕获异常,并在 NGINX 端统一返回 500 或日志记录。

通过以上功能,你可以让 NGINX 不仅是高效的反向代理和静态分发,还能变身轻量级应用服务器,直接用 JavaScript 实现路由、加工、调用与缓存逻辑,真正达成“配置即代码”的极简运维体验。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello.Reader

请我喝杯咖啡吧😊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值