[译] nginScript 入门

栏目: 服务器 · 发布时间: 6年前

内容简介:编者的话 – 这是关于 nignScript 这个系列的博文的第一篇。本文中讨论了 NGINX 公司选择自己实现 JavaScript 的原因,并且提供了一个简单的使用案例。探索更多的使用案例,请阅读其他的博文:自从 nginScript2015 年 9 月上线以来,作为一个实验性的模块,持续有新功能和语言的核心支持被加入。随着 NGINX Plus R12 的推出,我们很荣幸的宣布 nginScript 现在已经是一个在 NGINX 和 NGINX Plus 中可被广泛使用的稳定版模块了。nignScr
[译] nginScript 入门

在 HTTP 请求中发挥出 JavaScript 的强大力量和便捷优势

编者的话 – 这是关于 nignScript 这个系列的博文的第一篇。本文中讨论了 NGINX 公司选择自己实现 JavaScript 的原因,并且提供了一个简单的使用案例。探索更多的使用案例,请阅读其他的博文:

自从 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 解决方案的例子。

我们会很乐意了解你们想到的 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 模块:

  1. 跟随这些操作说明,使用开源 repository构建 nginScript 模块。
  2. 将这个模块的二进制文件( ngx_http_js_module.so )拷贝到 NGINX 根目录(通常是 /etc/nginx/modules )下的 modules 子目录下。
  3. 完成给开源 NGINX 装载 nginScript的第二步和第三步。

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划


以上所述就是小编给大家介绍的《[译] nginScript 入门》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PWA实战

PWA实战

[美]Dean Alan Hume / 郑丰彧 / 电子工业出版社 / 2018-6 / 69

Progressive Web App(PWA)是由谷歌提出的一整套技术解决方案,它致力于为 Web 提供出色的用户体验,并完美体现了渐进增强原则。作为为数不多的实战入门用书,《PWA 实战:面向下一代的Progressive Web App》旨在通过大量清晰示例来介绍 PWA 的主要特性。全书一共由五个部分组成:第一部分介绍 PWA 的概念及解锁 PWA 应用的关键—Service Worker......一起来看看 《PWA实战》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具