内容简介:编者的话 – 这是关于 nignScript 这个系列的博文的第一篇。本文中讨论了 NGINX 公司选择自己实现 JavaScript 的原因,并且提供了一个简单的使用案例。探索更多的使用案例,请阅读其他的博文:自从 nginScript2015 年 9 月上线以来,作为一个实验性的模块,持续有新功能和语言的核心支持被加入。随着 NGINX Plus R12 的推出,我们很荣幸的宣布 nginScript 现在已经是一个在 NGINX 和 NGINX Plus 中可被广泛使用的稳定版模块了。nignScr
- 原文地址: Introduction to nginScript
- 原文作者:Liam Crilly
- 译文出自: 掘金翻译计划
- 译者: 1992chenlu
- 校对者: mnikn 、 imink
在 HTTP 请求中发挥出 JavaScript 的强大力量和便捷优势
编者的话 – 这是关于 nignScript 这个系列的博文的第一篇。本文中讨论了 NGINX 公司选择自己实现 JavaScript 的原因,并且提供了一个简单的使用案例。探索更多的使用案例,请阅读其他的博文:
- nginScript 入门
- 使用 nginScript 逐步迁移客户端到新的服务器
- 在“Galera 集群负载均衡过程中 SQL 方法的日志记录”中的 nginScript 日志记录进阶
- 使用 nginScript 实现基于数据遮蔽的用户隐私保护
自从 nginScript2015 年 9 月上线以来,作为一个实验性的模块,持续有新功能和语言的核心支持被加入。随着 NGINX Plus R12 的推出,我们很荣幸的宣布 nginScript 现在已经是一个在 NGINX 和 NGINX Plus 中可被广泛使用的稳定版模块了。
nignScript 是一个只适用于 NGINX 和 NGINX Plus 的 JavaScript 实现,它是专为服务端用例和每次请求处理而设计的。它通过 JavaScript 代码扩展了 NGINX 的配置语法,为复杂配置提供了解决方案。
nignScript 可供 HTTP 和 TCP/UDP 两种协议使用,用例的种类广泛,例如:
- 根据正常情况下 NGINX 变量无法使用的数值,生成自定义的日志格式
- 实现新的负载均衡算法
- 为应用层粘滞会话(sticky sessons)解析 TCP/UDP 协议
当然,nignScript 可以做更多,也有更多可能性有待实现。虽然我们已经宣布 nignScript 能被广泛地应用,并且已经推荐在生产环境使用 nignScript,但我们还有一些在计划中的改良,用来支持更多的用例:
- 查看并修改 HTTP 请求/响应的 body(现已支持 TCP/UDP)
- 在 nginScript 代码中发出 HTTP 子请求(subrequests)
- 给 HTTP 请求写 authentication handlers(现已支持 TCP/UDP)
- 文件读写
在深入讨论 nginScript 之前,我们先澄清一下两个普遍存在的误解。
nginScript 不是 Lua
多年来,NIGINX 社区创建了一些程序化扩展。目前,Lua 是其中最流行的;使用时,它是一个 NGINX 模块 ,对于 NGINX Plus 来说,它是一个经认证的第三方模块。Lua 模块及其插件库提供了与 NGINX 内核的深度整合和一系列丰富的功能,包括一个 Redis 的驱动程序。
Lua 是一个强大的脚本语言。但是,就采用率来看,它仍是有一定缺陷的。并且,它也不算一个前端工程师或者开发运维工程师必备技能。
nginScript 没有企图取代 Lua,并且 nginScript 还有很长的路要走才能与 Lua 相提并论。nignScript 的目标是给广大 NIGINX 社区的人民群众,提供一个可以基于一种流行的编程语言的、程序化配置的解决方案。
nginScript 不是 Node.js
nginScript 的目标并不是将 NGINX 或者 NGINX Plus 变成一个应用服务器。简言之,nginScript 的功能相当于中间件,因为脚本的执行是发生于客户端与内容之间的。技术上讲,Node.js 与 nginScript 和 NGINX(或 NGINX Plus)的结合体有两个共同点,那就是事件驱动的架构,以及,都将 JavaScript 作为编程语言,仅此而已。
Node.js 使用 Google V8 JavaScript 引擎,而 nginScript 则完全是 ECMAScript 标准的实现,专为 NGINX 和 NGINX Plus 设计。Node.js 内置 JavaScript 虚拟机,用来执行垃圾回收和内存管理的操作,而 nginScript 则会对每一个请求都初始化一个 JavaScript 虚拟机和相应的内存空间,并在请求被完成后释放内存空间。
作为服务端语言的 JavaScript
如上所述,nginScript 是 JavaScript 语言的标准实现。而目前,所有其他的 JavaScript 运行引擎,都是以运行在网络浏览器为目的而设计的。客户端代码运行与服务端的代码运行有许多本质上的不同 —— 从系统资源的可利用性,到可能存在的并发运行的数量。
我们决定实现自己的 JavaScript runtime,一方面来满足服务端运行的需要,另一方面这种方式可以与 NGINX 请求处理的架构进行优雅适配。以下是我们的设计原则:
- 运行环境与请求有相同的生命周期
nginScript 使用单线程的字节码执行,这么设计是为了快速的初始化和垃圾清理。对每个请求,都有对应的运行环境被初始化。初始启动是很迅速的,因为初始化没有用到复杂的状态或者帮助类。内存池的消耗在运行的期间逐渐累积,在运行完成的时候被释放。这种内存管理的设计无需为单个对象跟踪和释放内存,或使用垃圾收集器。
- 非阻塞式代码执行
NGINX 和 NGINX Plus 的事件驱动模式会调度每个 nginScript 运行环境的运行。当一个 nginScript 规则执行一个阻塞操作时(比如读取网络数据,或者发起外部的子请求),NGINX 和 NGINX Plus 会将那个 JavaScript 虚拟机挂起,并在那个操作结束时,重新安排它的运行。这意味着,你可以将规则写的简单、线性,而 NGINX 和 NGINX Plus 在调度它们的时候也不会被阻塞。
- 按照我们的需要实现语言
JavaScript 的规范是按ECMAScript 标准定义的。nginScript 使用ECMAScript 5.1,和一部分ECMAScript 6 以实现数学相关的功能。实现自己的 JavaScript runtime 让我们能够更自由的调整服务端用例的语言支持的优先级,并忽视掉我们不需要的部分。我们有一个 已经提供支持和尚未提供支持的语言要素的列表 。
- 与请求处理阶段的紧密结合
NGINX 和 NGINX Plus 的请求处理分为不同的阶段。配置指令通常在一个特定的阶段被执行,原生的 NGINX 模块通常会在某个特定阶段,查看或者修改一个请求。nginScript 会将一些处理阶段暴露出去,通过配置指令,将控制权交给运行时的 JavaScript 代码。这种整合配置规则的方式,同时保证了原生 NGINX 模块的功能性和灵活性,并让其 JavaScript 实现代码变得简单。
下面的表格指出了目前可被 nginScript 利用的处理阶段,还有相应的配置指令。
处理阶段 | HTTP 模块 | 流 (TCP/UDP) 模块 |
---|---|---|
访问 – 网络连接访问控制 | :x: | :white_check_mark:js_access |
预读(Pre-read) – 读/写 body | :x: | :white_check_mark:js_preread |
过滤器 – 在代理中读/写 body | :x: | :white_check_mark:js_filter |
内容 – 向客户端发送响应 | :white_check_mark:js_content | :x: |
日志/变量 – 应需评估 | :white_check_mark:js_set | :white_check_mark:js_set |
nginScript 入门 —— 一个真实的例子
nginScript 可以作为一个模块,可以被编译到一个开源的 NGINX 二进制文件里,或者动态地载入 NGINX 或 NGINX Plus。本文的结尾处,有在 NGINX 和 NGINX Plus 中的说明。
在这个例子中,我们使用 NGINX 或 NGINX Plus 作为简单的反向代理,并使用 nginScript 以一种特定的格式构建访问日志记录。
- 包括客户端发来的请求文件头(request headers)
- 包括后端返回的响应文件头(response headers)
- 使用键值对,以便让日志文件处理工具(例如现在被称作 Elastic Stack 的 ELK Stack)高效的搜索和摄入日志记录
这个例子的 NGINX 配置十分简单:
Failed loading gist https://gist.github.com/49e2f6f6b0a36ed9846dd63cddbd742a.json: timeout
如你所见,nginScript 代码与配置规则并不一样。我们用 js_include
指令来指定包含我们所有的 JavaScript 代码的文件。 js_set
指令定义了一个新的 NGINX 变量 $access_log_with_headers
,还有填充这个变量所需的 JavaScript 函数。 log_format
指令定义了一种名为 键值对(kvpairs) 的新格式,它使用 $access_log_with_headers
变量的值输出每一行日志。
server
指令定义了一个简单的 HTTP 反向代理,这个反向代理可以将所有的请求转发给一个新地址,例如 http://www.example.com 。 access_log
指令可以用来指定所有以 键值对(kvpairs) 格式被录入日志的请求。
我们现在来看一下用来准备每一行日志格式的 JavaScript 代码。我们有两个函数:
-
kvHeaders
- 一个将headers
对象转换为键值对的帮助函数。所有的帮助函数,必须在调用他们的函数前面被声明。 -
kvAccessLog
- 这个函数定义了 NGINX 配置中的js_set
指令。它接收两个对象参数(arguments),它们分别代表了客户端请求(req
),与后端服务器的响应(res
)。像它们这样的内置对象,也可以被传递到所有 HTTP 的 nginScript 函数中。
正如在 kvAccessLog
函数中看到的那样,返回值才会被传递到 js_set
配置指令。要记住, NGINX 变量是应需评估的,这也就意味着被 js_set
定义的 JavaScript 函数只有在变量的值被需要的时候才会执行。在这个例子中, $access_log_with_headers
被 log_format
指令使用,因此 kvAccessLog()
是在输出日志的时候被执行的。而在 map
指令或者 rewrite
指令中被用到的变量,会在更早的处理阶段出发对应的 JavaScript 代码的执行。
我们通过传递一个请求通过我们的反向代理的方式,来观察这种增强版的 nginScript 日志记录解决方案,和它最终产生的日志文件记录。
$ curl http://127.0.0.1/ $ tail --lines=1 /var/log/nginx/access_headers.log 2017-03-14T14:36:53+00:00 client=127.0.0.1 method=GET uri=/ status=200 req.Host=127.0.0.1 req.User-Agent=curl/7.47.0 req.Accept=*/* res.Cache-Control=max-age=604800 res.Etag=\x22359670651+ident\x22 res.Expires='Tue, 21 Mar 2017 14:36:53 GMT' res.Last-Modified='Fri, 09 Aug 2013 23:54:35 GMT' res.Vary=Accept-Encoding res.X-Cache=HIT
nginScript 的许多功能都来自它访问 NGINX 内部的能力。这个例子使用了一些请求与响应对象的属性。nginScript 针对 TCP 和 UDP 的流模块使用了一个 session 对象和它的属性集 。查看我们的博客可以得到更多 nginScript 解决方案的例子。
- HTTP - 使用 nginScript 逐步迁移客户端到新的服务器
- 流(Stream) – Galera 集群负载均衡过程中 SQL 方法的日志记录
我们会很乐意了解你们想到的 nginScript 用例 - 请在评论里告诉我们。
在 NGINX 和 NGINX Plus 中开始使用 nginScript
- 给 NGINX Plus 装载 nginScript
- 给开源 NGINX 装载 nginScript
- 给开源 NGINX 编译动态 nginScript 模块
给 NGINX Plus 装载 nginScript
nginScript 是 NGINX Plus 订阅者可以免费使用的动态模块(关于开源 NGINX,请参考下面给开源 NGINX 装载 nginScript的部分。)
1 . 从 NGINX Plus repository 获取并安装 nginScript 模块
- Ubuntu 和 Debian 系统使用下面的命令:
$ sudo apt‑get install nginx-plus-module-njs
- RedHat、CentOS 和 Oracle Linux 系统使用下面的命令:
$ sudo yum install nginx-plus-module-njs
2 . 我们可以在配置文件 nginx.conf 的顶级 context 下("main")加入一条配置指令 load_module
,用来给 HTTP 流量加载 nginScript 模块(注意不是在 http 或者 stream 的 context 下):
load_module modules/ngx_http_js_module.so;
3 . 重新加载 NGINX Plus,将 nginScript 模块载入到正在运行的实例中。
$ sudo nginx -s reload
给开源 NGINX 装载 nginScript
如果你的系统配置了官方的 开源 NGINX 预建包(pre‑built packages) ,并且你安装的版本在 1.9.11 或以上,你可以直接将 nginScript 安装为平台的预建包(pre‑built packages)。
1 . 安装预建包(pre‑built packages)
- Ubuntu 和 Debian 系统使用下面的命令:
$ sudo apt-get install nginx-module-njs
- RedHat、CentOS 和 Oracle Linux 系统使用下面的命令:
$ sudo yum install nginx-module-njs
2 . 我们可以在配置文件 nginx.conf 的顶级 context 下("main")加入一条配置指令 load_module
,用来给 HTTP 流量加载 nginScript 模块(注意不是在 http 或者 stream 的 context 下):
load_module modules/ngx_http_js_module.so;
3 . 重新加载 NGINX Plus,将 nginScript 模块载入到正在运行的实例中。
$ sudo nginx -s reload
给开源 NGINX 编译动态 nginScript 模块
如果你更喜欢直接从源代码编译出一个 NGINX 模块:
- 跟随这些操作说明,使用开源 repository构建 nginScript 模块。
- 将这个模块的二进制文件( ngx_http_js_module.so )拷贝到 NGINX 根目录(通常是 /etc/nginx/modules )下的 modules 子目录下。
- 完成给开源 NGINX 装载 nginScript的第二步和第三步。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为掘金 上的英文分享文章。内容覆盖 Android 、 iOS 、 React 、 前端 、 后端 、 产品 、 设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划 。
以上所述就是小编给大家介绍的《[译] nginScript 入门》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- TiDB入门(四):从入门到“跑路”
- MyBatis从入门到精通(一):MyBatis入门
- MyBatis从入门到精通(一):MyBatis入门
- Docker入门(一)用hello world入门docker
- 赵童鞋带你入门PHP(六) ThinkPHP框架入门
- 初学者入门 Golang 的学习型项目,go入门项目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。