文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

可以在 Nginx 中运行 JavaScript,厉害了!

2024-12-02 21:32

关注

Web 服务中灰度方案的实现,很多会采用 Nginx + Lua + Redis 方案。Lua 是一个轻量级的脚本语言,体积小、启动速度快、性能高。通过 lua-nginx-module 模块将 Lua 语言嵌入到 Nginx 中,可以使用 Lua 脚本扩展 Nginx 功能,并可以访问 MySQL、Redis 等数据库。

图片

Lua 虽然是个强大的脚本语言,但过于小众。Nginx 团队选择非常流行的 JavaScript 研发 NGINX JavaScript 模块 (njs),让更多工程师可以使用 JavaScript 来扩展 Nginx 功能,从而更好的发展 Nginx 社区生态。

图片

NGINX JavaScript 简介

NGINX JavaScript 简称 njs,是 JavaScript 语言的子集,实现了部分 ECMAScript 5.1(strict mode)规范和 ECMAScript 6 规范,可以使用 njs 来扩展 Nginx 功能。

njs 与 Node.js、JavaScript 的区别

一、运行时不同

Node.js 使用 V8 引擎,njs 是专门为 Nginx 定制设计的运行时。Node.js 使用 V8 引擎在内存中有一个持久化的 JavaScript 虚拟机 (VM) 并执行垃圾收集以进行内存管理;而 njs 是专门为 Nginx 设计,非常轻量,会为每个请求初始化一个新的 JavaScript VM 和必要的内存,并在请求完成时释放内存。

二、语言规范差异

JavaScript 的规范是由 ECMAScript 标准定义,随着标准版本的更新迭代,会支持更多的语言功能;njs 自研的服务端运行时,更多的优先支撑服务于 Nginx,只实现了 ECMAScript 5.1 和部分 ECMAScript 6,实现更多标准规范的同时,更多会考虑是否是 Nginx 所需要的。

njs 安装&配置

安装 nginx-module-njs 动态模块,需要 Nginx 版本为 1.9.11 之后支持动态模块的载入。 

  1. yum install nginx-module-njs 

安装后,在配置文件 nginx.conf 中需要使用 load_module 指令加载 njs 动态模块。 

  1. load_module modules/ngx_http_js_module.so; 

njs 基本使用

Hello World

nginx.conf: 

  1. http {  
  2.     js_import http.js;  
  3.     # or js_import http from http.js;  
  4.     server {  
  5.         listen 8000;  
  6.         location / {  
  7.             js_content http.hello;  
  8.         }  
  9.     }  

http.js: 

  1. function hello(r) {  
  2.     r.return(200, "Hello world!");  
  3.  
  4. export default { hello }; 

js_import : 导入一个 njs 模块,没有指定模块名称则默认为文件名称。

js_content : 使用 njs 模块里导出的方法处理这个请求。

HTTP Proxying

使用 njs 模块处理 HTTP 请求,并使用 subrequest 发起子请求。

nginx.conf: 

  1. js_import http.js;  
  2. location /start {  
  3.     js_content http.content;  
  4.  
  5. location /foo {  
  6.     proxy_pass <http://backend1> 
  7.  
  8. location /bar {  
  9.     proxy_pass <http://backend2> 

http.js: 

  1. function content(r) {  
  2.     r.subrequest('/api/5/foo', {  
  3.           method: 'POST',  
  4.           body: JSON.stringify({ foo: 'foo', bar: "bar" })  
  5.     }, function(res) {  
  6.             if (res.status != 200) {  
  7.                 r.return(res.status, res.responseBody);  
  8.                 return;  
  9.             }  
  10.             var json = JSON.parse(res.responseBody);  
  11.             r.return(200, json.content);  
  12.     });  
  13.  
  14. export default { content }; 

r.subrequest : 可以去请求内部的其他 API ,headers 和该请求相同,并且可以在 location 块里使用 proxy_set_header 来设置或覆盖原来的 header。

自定义日志输出格式

使用 njs 定制 Nginx 日志的输出格式。

nginx.js: 

  1. js_import  logging.js;  
  2. js_set     $access_log_headers logging.kvAccess;  
  3. log_format kvpairs $access_log_headers;  
  4. server {  
  5.     listen 80;  
  6.     root /usr/share/nginx/html;  
  7.     access_log /var/log/nginx/access.log kvpairs;  

logging.js: 

  1. function kvAccess(r) {  
  2.     var log = `${r.variables.time_iso8601} client=${r.remoteAddress} method=${r.method} uri=${r.uri} status=${r.status}`;  
  3.     r.rawHeadersIn.forEach(h => log += ` in.${h[0]}=${h[1]}`);  
  4.     r.rawHeadersOut.forEach(h => log += ` out.${h[0]}=${h[1]}`);  
  5.     return log;  
  6.  
  7. export default { kvAccess } 

js_set : 将 njs 模块里的 kvAccess 方法执行后,执行结果放到 $access_log_headers 变量中。但如果只被引用在 log_format 中,则只会在日志记录阶段被执行。

r : HTTP request 对象。属性列表:http://nginx.org/en/docs/njs/reference.html#http

访问数据库

一、访问 Redis

使用 redis2-nginx-module 动态模块,结合 subrequest 来访问 Redis 数据。

nginx.conf: 

  1. 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: 

  1. function serialize(obj) {    var str = [];    for (var p in obj) {        if (obj.hasOwnProperty(p)) {            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));        }    }    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 编码。

redis2_query : 执行的 Redis 命令。

redis2_pass : Redis 后端服务。

redis2_pass 返回值为类似 redis-cli 执行后的返回值,需要有一个 parser 来解析是否执行成。

二、访问 MySQL

使用 drizzle-nginx-module 动态模块,结合 subrequest 来访问 MySQL 数据。

nginx.conf: 

  1. upstream backend {  
  2.     drizzle_server 127.0.0.1:3306 dbname=test  
  3.         password=some_pass user=monty protocol=mysql 
  4.  
  5. server {  
  6.     js_import http.js;  
  7.     location /mysql {  
  8.          set_unescape_uri $name $arg_name;  
  9.          # 为防止 SQL 注入攻击,使用 set_quote_sql_str 来设置 sql 语句中的变量  
  10.          set_quote_sql_str $quoted_name $name;  
  11.          drizzle_query "select * from cats where name = $quoted_name";  
  12.          drizzle_pass backend;  
  13.          drizzle_connect_timeout    500ms; # default 60s  
  14.          drizzle_send_query_timeout 2s;    # default 60s  
  15.          drizzle_recv_cols_timeout  1s;    # default 60s  
  16.          drizzle_recv_rows_timeout  1s;    # default 60s  
  17.     }  
  18.     # GET /get_mysql_data?name=cat_name  
  19.     location /get_mysql_data {  
  20.         js_content http.get_mysql_data;  
  21.     }  

http.js: 

  1. function serialize(obj) {  
  2.     var str = [];  
  3.     for (var p in obj) {  
  4.         if (obj.hasOwnProperty(p)) {  
  5.             str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));  
  6.         }  
  7.     }  
  8.     return str.join("&");  
  9. };  
  10. function get_mysql_data(r) {  
  11.     r.subrequest('/mysql', {  
  12.           args: serialize(r.args),  
  13.           method: 'GET'  
  14.     }, function(res) {  
  15.             if (res.status != 200) {  
  16.                 r.return(res.status, res.responseBody);  
  17.                 return;  
  18.             }  
  19.             r.return(200, res.responseBody);  
  20.     });  
  21.     return log; 
  22.   
  23. export default { get_mysql_data } 

set_quote_sql_str : 为防止 SQL 注入攻击,来设置 sql 语句中的变量。

drizzle_query : 执行的 SQL 语句。

drizzle_pass : Drizzle 或 MySQL 服务的 upstream。

结语

在 njs 之前,Nginx+Lua 生态虽然已日趋成熟,但 Nginx 毕竟是一个 Web 服务器,JavaScript 作为 Web 开发的最流行的语言,可以使用 JavaScript 生态来扩展 Nginx 的功能,可能会更加的有一些想象力做更多的事情。 

 

来源:前端大全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯