Simple NginScript
某客户有定时关闭网站的需求,每天晚上10点后关闭,第二天6点开启,节假日全天关闭。
想到通过 nginx 的配置文件实现,但是自己对 nginx 的掌握一直停留在“够用”阶段,涉及逻辑判断的功能从来没用过,只是知道 nginx 一定具备这样的功能。
网上一搜,马上发现一篇关于https://blog.micblo.com/2017/03/12/nginx-Lua-%E8%AE%BE%E5%AE%9A%E7%BD%91%E7%AB%99%E5%AE%9A%E6%97%B6%E5%85%B3%E9%97%AD/[通过 lua 控制服务器开关的文章],与我的需求非常类似,只是其基于 lua 模块,而我服务器上用的是官方源,不带 lua 模块,而且跑了好多个网站,自己编译或者换用 openresty 有一定风险搞挂掉,慎重起见又是一番搜索,决定用 nginScript 来实现这个功能。
安装
如果你像我一样用了 nginx 的官方源,安装就比较简单了,nginx 官方以动态库的形式提供了 nginScript 模块,在 CentOS 中只要执行 yum install nginx-module-njs 就可以自动安装这个模块。
其他系统可以根据实际情况调整包管理器的命令,http://nginx.org/en/linux_packages.html#dynmodules[官方文档页面]。我博客中也有历史文章介绍如何安装 nginx 官方源。
载入模块
通过上述命令安装之后,nginx 并没有自动启用相关功能,需要我们修改配置文件以载入动态模块。
载入模块的命令是 load_module file ,其中 file 是模块所在路径,我们是通过包管理工具自动安装的模块,所以并不知道这个模块文件具体在什么位置。在 CentOS 中,我们可以通过 rpm -ql 命令找到我们所需的信息。
# rpm -ql nginx-module-njs /usr/bin/njs /usr/lib64/nginx/modules/ngx_http_js_module-debug.so /usr/lib64/nginx/modules/ngx_http_js_module.so /usr/lib64/nginx/modules/ngx_stream_js_module-debug.so /usr/lib64/nginx/modules/ngx_stream_js_module.so /usr/share/doc/nginx-module-njs /usr/share/doc/nginx-module-njs/CHANGES /usr/share/doc/nginx-module-njs/COPYRIGHT
可以看到 /usr/lib64/nginx/modules 下有 4 个 so 文件,这里我们只要用到 ngx_http_js_module.so 即可。因为我们需要用到两个指令 js_include 和 js_set 都包含在这个模块中。
因此最后的载入命令是:
load_module modules/ngx_http_js_module.so
使用 nginScript
这是http://nginx.org/en/docs/http/ngx_http_js_module.html#js_include[官方文档]对 js_include 和 js_set 的说明:
Syntax: js_include file; Default: — Context: http Specifies a file that implements location and variable handlers in njs. Syntax: js_set $variable function; Default: — Context: http Sets an njs function for the specified variable.
非常简洁,所以其实我还是靠网上搜到的内容才知道为何要使用他们,以及如何使用他们。
通过 js_include 引用一个 js 文件,该文件里定义的函数,可以在 js_set 指令中调用,并将执行结果赋值给一个变量
这两个命令的有效范围是 http ,也就是说应该放在 nginx 配置文件的这个位置
http {
...
js_include /data/block.js
js_set $nightOrHoliday nightOrHoliday
...
}一开始我没去理解官方文档中 Context: http 的意思,将 js_include 和 js_set 放在了 server 中,毕竟只有部分网站需要这个规则,但是配置文件无法生效,通过 nginx -t 检测提示 'js_include' is not allowed here 。最后才意识到 Context 可能是这个意思,然后把这两行配置放到 http 下解决了问题。
上面引用了 /data/block.js 这个文件,这个 js 文件会在 nginx 启动或重载配置时被载入内存,而不是像 PHP 那样,每次有用户访问的时候从硬盘读取文件,这保证了 nginx 整体的速度不会受到大的影响。
我在这个文件里定义了两个函数,一个用来计算当前日期和时间,一个用来判断这个时间是否需要屏蔽。
function litdate() { // 一个用来计算日期和时间的函数
...
}
function nightOrHoliday() {
var hour = litdate().G;
if (hour < 6 || hour >= 22) {
return true;
}
var date = litdate().format('md');
if (date == '0425' ||
date == '0430' ||
date == '0501' ||
date == '0618' ||
date == '0924' ||
date == '1001' ||
date == '1002' ||
date == '1003' ||
date == '1004' ||
date == '1005' ||
date == '1006' ||
date == '1007') {
return true;
}
return false;
}这个函数会在 js_set $nightOrHoliday nightOrHoliday 中被调用,其返回的 true 或 false 会被赋值给变量 $nightOrHoliday,最后在相关站点的 server 中通过 if 指令判断这个变量以决定是否返回网站内容。
server {
...
location / {
if ($nightOrHoliday = 'true') {
return 444;
}
...
}
...
}这里需要特别说明一下,nginx 配置文件中的变量只有一种类型,就是字符串,js 文件中返回的 true 或者 false ,到了这里就变成了相应的字符串,不能直接用 if 进行判断。
全部设置完毕后,重启或重载配置,使之生效。