Nginx 中运行 JavaScript
Wang.Lin
文章源自JAVA秀-https://www.javaxiu.com/41932.html
code秘密花园 文章源自JAVA秀-https://www.javaxiu.com/41932.html
1周前 文章源自JAVA秀-https://www.javaxiu.com/41932.html
引言文章源自JAVA秀-https://www.javaxiu.com/41932.html
Nginx 作为市场占有率最高的Web服务器,主打高性能、可扩展。自带了很多核心功能模块,并且也有大量的第三方模块。文章源自JAVA秀-https://www.javaxiu.com/41932.html
Web 服务中灰度方案的实现,很多会采用 Nginx + Lua + Redis 方案。Lua 是一个轻量级的脚本语言,体积小、启动速度快、性能高。通过 lua-nginx-module 模块将 Lua 语言嵌入到 Nginx 中,可以使用 Lua 脚本扩展 Nginx 功能,并可以访问 MySQL、Redis 等数据库。文章源自JAVA秀-https://www.javaxiu.com/41932.html
文章源自JAVA秀-https://www.javaxiu.com/41932.html
Lua 虽然是个强大的脚本语言,但过于小众。Nginx 团队选择非常流行的 JavaScript 研发 NGINX JavaScript 模块 (njs),让更多工程师可以使用 JavaScript 来扩展 Nginx 功能,从而更好的发展 Nginx 社区生态。文章源自JAVA秀-https://www.javaxiu.com/41932.html
文章源自JAVA秀-https://www.javaxiu.com/41932.html
NGINX JavaScript 简介
NGINX JavaScript 简称 njs,是 JavaScript 语言的子集,实现了部分 ECMAScript 5.1(strict mode)规范和 ECMAScript 6 规范,可以使用 njs 来扩展 Nginx 功能。文章源自JAVA秀-https://www.javaxiu.com/41932.html
njs 与 Node.js、JavaScript 的区别
一、运行时不同文章源自JAVA秀-https://www.javaxiu.com/41932.html
Node.js 使用 V8 引擎,njs 是专门为 Nginx 定制设计的运行时。Node.js 使用 V8 引擎在内存中有一个持久化的 JavaScript 虚拟机 (VM) 并执行垃圾收集以进行内存管理;而 njs 是专门为 Nginx 设计,非常轻量,会为每个请求初始化一个新的 JavaScript VM 和必要的内存,并在请求完成时释放内存。文章源自JAVA秀-https://www.javaxiu.com/41932.html
二、语言规范差异文章源自JAVA秀-https://www.javaxiu.com/41932.html
JavaScript 的规范是由 ECMAScript 标准定义,随着标准版本的更新迭代,会支持更多的语言功能;njs 自研的服务端运行时,更多的优先支撑服务于 Nginx,只实现了 ECMAScript 5.1 和部分 ECMAScript 6,实现更多标准规范的同时,更多会考虑是否是 Nginx 所需要的。文章源自JAVA秀-https://www.javaxiu.com/41932.html
njs 安装&配置
安装 nginx-module-njs 动态模块,需要 Nginx 版本为 1.9.11 之后支持动态模块的载入。文章源自JAVA秀-https://www.javaxiu.com/41932.html
yum install nginx-module-njs
安装后,在配置文件 nginx.conf 中需要使用 load_module 指令加载 njs 动态模块。文章源自JAVA秀-https://www.javaxiu.com/41932.html
load_module modules/ngx_http_js_module.so;
njs 基本使用
Hello World
nginx.conf:文章源自JAVA秀-https://www.javaxiu.com/41932.html
http { js_import http.js; # or js_import http from http.js; server { listen 8000; location / { js_content http.hello; } }}
http.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
function hello(r) { r.return(200, "Hello world!");}export default { hello };
js_import : 导入一个 njs 模块,没有指定模块名称则默认为文件名称。文章源自JAVA秀-https://www.javaxiu.com/41932.html
js_content : 使用 njs 模块里导出的方法处理这个请求。文章源自JAVA秀-https://www.javaxiu.com/41932.html
HTTP Proxying
使用 njs 模块处理 HTTP 请求,并使用 subrequest 发起子请求。文章源自JAVA秀-https://www.javaxiu.com/41932.html
nginx.conf:文章源自JAVA秀-https://www.javaxiu.com/41932.html
js_import http.js;location /start { js_content http.content;}location /foo { proxy_pass <http://backend1>;}location /bar { proxy_pass <http://backend2>;}
http.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
function content(r) { r.subrequest('/api/5/foo', { method: 'POST', body: JSON.stringify({ foo: 'foo', bar: "bar" }) }, function(res) { if (res.status != 200) { r.return(res.status, res.responseBody); return; } var json = JSON.parse(res.responseBody); r.return(200, json.content); });}export default { content };
r.subrequest : 可以去请求内部的其他 API ,headers 和该请求相同,并且可以在 location 块里使用 proxy_set_header 来设置或覆盖原来的 header。文章源自JAVA秀-https://www.javaxiu.com/41932.html
自定义日志输出格式
使用 njs 定制 Nginx 日志的输出格式。文章源自JAVA秀-https://www.javaxiu.com/41932.html
nginx.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
js_import logging.js;js_set $access_log_headers logging.kvAccess;log_format kvpairs $access_log_headers;server { listen 80; root /usr/share/nginx/html; access_log /var/log/nginx/access.log kvpairs;}
logging.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
function kvAccess(r) { var log = `${r.variables.time_iso8601} client=${r.remoteAddress} method=${r.method} uri=${r.uri} status=${r.status}`; r.rawHeadersIn.forEach(h => log += ` in.${h[0]}=${h[1]}`); r.rawHeadersOut.forEach(h => log += ` out.${h[0]}=${h[1]}`); return log;}export default { kvAccess }
js_set : 将 njs 模块里的 kvAccess 方法执行后,执行结果放到 $access_log_headers 变量中。但如果只被引用在 log_format 中,则只会在日志记录阶段被执行。文章源自JAVA秀-https://www.javaxiu.com/41932.html
r : HTTP request 对象。属性列表:http://nginx.org/en/docs/njs/reference.html#http文章源自JAVA秀-https://www.javaxiu.com/41932.html
访问数据库
一、访问 Redis文章源自JAVA秀-https://www.javaxiu.com/41932.html
使用 redis2-nginx-module 动态模块,结合 subrequest 来访问 Redis 数据。文章源自JAVA秀-https://www.javaxiu.com/41932.html
nginx.conf:文章源自JAVA秀-https://www.javaxiu.com/41932.html
js_import http.js;# GET /redis_get?key=some_keylocation = /redis_get { # 解码 uri 中的参数 key,赋值到变量 $key set_unescape_uri $key $arg_key; redis2_query get $key; redis2_pass 127.0.0.1:6379;}# GET /redis_set?key=one&val=first%20valuelocation = /redis_set { set_unescape_uri $key $arg_key; set_unescape_uri $val $arg_val; redis2_query set $key $val; redis2_pass 127.0.0.1:6379;}# GET /get_redis_data?key=some_keylocation /get_redis_data { js_content http.get_redis_data;}
http.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
function serialize(obj) { var str = []; for (var p in obj) { if (obj.hasOwnProperty(p)) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj)); } } return str.join("&");};function get_redis_data(r) { r.subrequest('/redis_get', { args: serialize(r.args), method: 'GET' }, function(res) { if (res.status != 200) { r.return(res.status, res.responseBody); return; } r.return(200, res.responseBody); }); return log;}export default { get_redis_data }
set_unescape_uri :解码 uri 中参数的 %XX 编码。文章源自JAVA秀-https://www.javaxiu.com/41932.html
redis2_query : 执行的 Redis 命令。文章源自JAVA秀-https://www.javaxiu.com/41932.html
redis2_pass : Redis 后端服务。文章源自JAVA秀-https://www.javaxiu.com/41932.html
redis2_pass 返回值为类似 redis-cli 执行后的返回值,需要有一个 parser 来解析是否执行成。文章源自JAVA秀-https://www.javaxiu.com/41932.html
二、访问 MySQL文章源自JAVA秀-https://www.javaxiu.com/41932.html
使用 drizzle-nginx-module 动态模块,结合 subrequest 来访问 MySQL 数据。文章源自JAVA秀-https://www.javaxiu.com/41932.html
nginx.conf:文章源自JAVA秀-https://www.javaxiu.com/41932.html
upstream backend { drizzle_server 127.0.0.1:3306 dbname=test password=some_pass user=monty protocol=mysql;}server { js_import http.js; location /mysql { set_unescape_uri $name $arg_name; # 为防止 SQL 注入攻击,使用 set_quote_sql_str 来设置 sql 语句中的变量 set_quote_sql_str $quoted_name $name; drizzle_query "select * from cats where name = $quoted_name"; drizzle_pass backend; drizzle_connect_timeout 500ms; # default 60s drizzle_send_query_timeout 2s; # default 60s drizzle_recv_cols_timeout 1s; # default 60s drizzle_recv_rows_timeout 1s; # default 60s } # GET /get_mysql_data?name=cat_name location /get_mysql_data { js_content http.get_mysql_data; }}
http.js:文章源自JAVA秀-https://www.javaxiu.com/41932.html
function serialize(obj) { var str = []; for (var p in obj) { if (obj.hasOwnProperty(p)) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj)); } } return str.join("&");};function get_mysql_data(r) { r.subrequest('/mysql', { args: serialize(r.args), method: 'GET' }, function(res) { if (res.status != 200) { r.return(res.status, res.responseBody); return; } r.return(200, res.responseBody); }); return log;}export default { get_mysql_data }
set_quote_sql_str : 为防止 SQL 注入攻击,来设置 sql 语句中的变量。文章源自JAVA秀-https://www.javaxiu.com/41932.html
drizzle_query : 执行的 SQL 语句。文章源自JAVA秀-https://www.javaxiu.com/41932.html
drizzle_pass : Drizzle 或 MySQL 服务的 upstream。文章源自JAVA秀-https://www.javaxiu.com/41932.html
结语
在 njs 之前,Nginx+Lua 生态虽然已日趋成熟,但 Nginx 毕竟是一个 Web 服务器,JavaScript 作为 Web 开发的最流行的语言,可以使用 JavaScript 生态来扩展 Nginx 的功能,可能会更加的有一些想象力做更多的事情。文章源自JAVA秀-https://www.javaxiu.com/41932.html
参考文献
2021年06月 Web 服务器排行榜 https://news.netcraft.com/archives/2021/06/29/june-2021-web-server-survey.html文章源自JAVA秀-https://www.javaxiu.com/41932.html
njs scripting language https://nginx.org/en/docs/njs/文章源自JAVA秀-https://www.javaxiu.com/41932.html
NJS Learning Materials https://github.com/soulteary/njs-learning-materials文章源自JAVA秀-https://www.javaxiu.com/41932.html
Harnessing the Power and Convenience of JavaScript for Each Request with the NGINX JavaScript Module https://www.nginx.com/blog/harnessing-power-convenience-of-javascript-for-each-request-with-nginx-javascript-module文章源自JAVA秀-https://www.javaxiu.com/41932.html
Introducing Nginx NJS https://www.mywaiting.com/weblogs/i文章源自JAVA秀-https://www.javaxiu.com/41932.html
关于本文作者:@whilefor原文:https://zhuanlan.zhihu.com/p/393788937文章源自JAVA秀-https://www.javaxiu.com/41932.html
文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html code秘密花园 文章源自JAVA秀-https://www.javaxiu.com/41932.html 一个优质的前端号,基础、框架、算法、项目、面试... 总有你想要的。文章源自JAVA秀-https://www.javaxiu.com/41932.html ·188篇原创内容 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 公众号文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html
阅读原文文章源自JAVA秀-https://www.javaxiu.com/41932.html
喜欢此内容的人还喜欢 文章源自JAVA秀-https://www.javaxiu.com/41932.html
文章源自JAVA秀-https://www.javaxiu.com/41932.html 书写一个好的JavaScript 变量命名 文章源自JAVA秀-https://www.javaxiu.com/41932.html 书写一个好的JavaScript 变量命名 文章源自JAVA秀-https://www.javaxiu.com/41932.html ... 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 不看的原因文章源自JAVA秀-https://www.javaxiu.com/41932.html JavaScript日常开发中常用的Object操作方法 文章源自JAVA秀-https://www.javaxiu.com/41932.html JavaScript日常开发中常用的Object操作方法 文章源自JAVA秀-https://www.javaxiu.com/41932.html ... 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 不看的原因文章源自JAVA秀-https://www.javaxiu.com/41932.html 如何理解单线程的JavaScript及其工作原理 文章源自JAVA秀-https://www.javaxiu.com/41932.html 如何理解单线程的JavaScript及其工作原理 文章源自JAVA秀-https://www.javaxiu.com/41932.html ... 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 文章源自JAVA秀-https://www.javaxiu.com/41932.html 不看的原因文章源自JAVA秀-https://www.javaxiu.com/41932.html

评论