十四、获取Nginx的环境变量

通过Lua API可以获取Nginx的环境变量,用来提升某些业务处理流程,比如有些定时任务只需要在一个worker进程上执行,不需要执行多次,因此可以获取环境变量中worker的ID,在指定的ID上执行任务即可;或者获取Nginx的worker进程是否正在shutdown,以决定是否对数据进行备份操作。

14.1 获取环境所在的模块
ngx.config.subsystem
语法:subsystem = ngx.config.subsystem
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:获取当前请求的Nginx子环境(http或stream)。如果在http模块下,就返回字符串http;如果在stream模块下,则返回字符串stream。

14.2 确认调试模式
ngx.config.debug
语法:debug = ngx.config.debug
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:判断请求是否在Debug模式下执行。例如,当需要在Debug模式下,打印某些数据或是执行某些代码时,可以通过这个判断,区分线下测试环境和线上环境。

14.3 获取prefix路径
ngx.config.prefix

语法:prefix = ngx.config.prefix()
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:获取编译Nginx时--prefix=的路径,如果启动Nginx时使用了参数-p,就以参数-p的值为准。

14.4 获取Nginx的版本号
ngx.config.nginx_version

语法:ver = ngx.config.nginx_version
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:获取Nginx的版本号,如本书使用的Nginx版本号是1.12.2。

14.5 获取configure信息
ngx.config.nginx_configure

语法:str = ngx.config.nginx_configure()
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua*

含义:获取编译Nginx时./configure命令的信息,返回的是一个字符串。

14.6 获取Ngx_Lua的版本号
ngx.config.ngx_lua_version
语法:ver = ngx.config.ngx_lua_version
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua*

含义:获取Ngx_Lua模块的版本号。可以用来检查Ngx_Lua的版本。例如,当开发某个功能需要使用指定的版本时,可以在代码中进行判断,如果不是指定的版本,可以输出警告信息。

14.7 判断worker进程是否退出
ngx.worker.exiting
语法:exiting = ngx.worker.exiting()
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:判断Nginx的worker进程是否退出。

14.8 获取worker进程的ID
ngx.worker.id
语法:count = ngx.worker.id()
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua*

含义:获取当前执行的worker进程的ID。worker进程的ID从0开始,依次递增,最大值是worker总数的值减1。

14.9 获取worker进程数量
ngx.worker.count
语法:count = ngx.worker.count()
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,init_by_lua,init_worker_by_lua

含义:获取当前Nginx worker进程的数量,即Nginx配置中worker_processes的值。

十五、定时任务

可以使用Nginx执行定时任务,例如,定期获取MySQL数据库中的数据并存放到共享内存中,定时监听某个配置是否发生改变(如果发生改变就重载Nginx),定时将日志远程传输到集中存储上等。
在Lua-0.10.9版本之前,常使用ngx.timer.at来启动定时任务。Ngx_Lua 0.10.9新增了ngx.timer.every,启动定时任务更加方便了。本章中的定时任务都使用ngx.timer.every来创建,后续介绍也会以此命令为主。

15.1 创建定时任务

ngx.timer.every
语法:hdl, err = ngx.timer.every(delay, callback, user_arg1, user_arg2, ...)
配置环境:init_worker_by_lua,set_by_lua,rewrite_by_lua,access_by_lua,content_ by_lua,header_filter_by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancerby lua,ssl_certificate_by_lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua*
含义:创建一个定时任务,delay指的是延迟时间,表示每隔多少秒执行一次,支持配置0.001s,不支持配置0s;callback是需要执行的Lua函数,在Nginx退出时,定时任务会被关闭。

init_worker_by_lua_block {
    local delay = 3;
    local ngx = require "ngx";
    local check
    check = function(premature)
        if not premature then
         --输出当前worker进程的PID和ID。
             ngx.log(ngx.ERR, ' ngx.worker.pid: ',ngx.worker.pid(),' ngx.worker.id: ',ngx.worker.id(),"------test nginx !!!")
        end
    end
    --每隔3s执行一次check函数
    local ok, err = ngx.timer.every(delay, check)
    if not ok then
         ngx.log(ngx.ERR, "failed to create timer: ", err)
         return
    end
}

重载Nginx配置后,定时任务会在启动worker进程时就被触发执行,请观察图7-1所示的定时任务输出的日志。
Lua-Nginx-Module常用指令(下)
图7-1 定时任务输出的日志
从图7-1可以发现如下规则。

1.每个worker进程都在执行输出操作。
2.都是每3s执行一次。
3.如果没有从Nginx外部进行访问的请求,定时任务会继续执行下去。
参数user_arg1、user_arg2用来给定时任务传递参数,示例如下:

init_worker_by_lua_block {
    local delay = 3;
    local ngx = require "ngx";
    local check
    --新增一个u_arg1参数,是对下面定时任务的'test nginx'进行填充
    check = function(premature,u_arg1)
        if not premature then
            ngx.log(ngx.ERR, ' ngx.worker.pid: ',ngx.worker.pid(),' ngx.worker.id: ',ngx.worker.id(),'------', u_arg1)
        end
    end

    --新增参数'test nginx' 
    local ok, err = ngx.timer.every(delay, check, 'test nginx')
    if not ok then
        ngx.log(ngx.ERR, "failed to create timer: ", err)
        return
    end
}

配置7-1

15.2 性能优化

在配置7-1中,Nginx启动了3个worker进程,所以每3s会执行3次worker进程。但有时只需执行一次即可,例如当前共享内存中存放数据时,因为数据是所有worker进程共享的,所以执行一次就足够了。且被启动的worker进程越多,后端的并发就越多,这会增加后端服务器的负载,那么应该怎么减少worker进程重复执行的次数呢?
其实根据输出的日志可以获得每个worker进程的ID,那么,只需利用ID指定一个worker进程来执行定时任务就可以了,示例如下:

init_worker_by_lua_block {
     local delay = 3;
     local ngx = require "ngx";
     local check
     check = function(premature)
         if not premature then
             ngx.log(ngx.ERR, ' ngx.worker.pid: ',ngx.worker.pid(),' ngx.worker.id: ',ngx.worker.id(),"------test nginx !!!")
         end
     end

   --如果worker进程的ID为0就执行定时任务  
   if 0 == ngx.worker.id() then
         local ok, err = ngx.timer.every(delay, check)
         if not ok then
             ngx.log(ngx.ERR, "failed to create timer: ", err)
             return
         end
    end

}

观察日志,会发现每3s worker进程只执行一次。
如果worker进程意外终止,Nginx的master进程会保证在worker进程意外终止后重启新的worker进程,ID保持不变。
如果要求定时任务只在Nginx重载时执行一次,可以使用如下方式:
local ok, err = ngx.timer.at(0,func)
这表示立即执行func函数,且由于没有回调ngx.timer.at的指令,只会执行一次。
注意:关于定时任务,需要在在init_worker_by_lua*的执行阶段中执行(详见8.2节)。

定时任务在Nginx后台运行,不直接和客户端请求打交道,因此不会直接影响请求的响应时间,但这并不代表它不会干扰请求的响应时间,如果在同一时间内有大量定时任务执行,也会降低Nginx的整体性能。此时,可以使用如下指令对正在运行的定时任务进行控制。
lua_max_running_timers
语法:lua_max_running_timers <count>
默认值:lua_max_running_timers 256
配置环境:http
含义:设置被允许的running timers(正在执行回调函数的计时器)的最大数量,如果超过这个数量,就会抛出“N lua_max_running_timers are not enough”,其中N是变量,指的是当前正在运行的running timers的最大数量。

lua_max_pending_timers
语法:lua_max_pending_timers <count>
默认值:lua_max_pending_timers 1024
配置环境:http
含义:设置允许使用的pending timers(执行挂起的定时器)的最大数量,如果在定时任务中超过这个限制,则会报“too many pending timers”错误。

15.3 禁用的Lua API
ngx.timer.every支持用户操作共享内存、读取数据库数据、获取系统时间等,但在定时任务中有些API是被明确禁止的,例如:
1.子请求ngx.location.capture。
2.向客户端输出的Lua API(如 ngx.say、ngx.print 和 ngx.flush)。
3.以ngx.req.开头的Lua API。

十六、常用指令

Ngx_Lua提供了大量的Lua API指令来实现各种功能,本节会介绍一些常用的指令。

16.1 请求重定向
在Nginx中通过rewrite对请求进行重定向,而在Ngx_Lua里可以使用ngx.redirect、ngx.req.set_uri来完成重定向,并且Ngx_Lua还提供了一个具有强大的扩展能力的ngx.exec指令。

ngx.redirect
语法:ngx.redirect(uri, status?)
配置环境:rewrite_by_lua,access_by_lua,content_by_lua*
含义:发出一个 HTTP状态码为301或302的重定向请求到指定的URI。
参数status的可选值有301、302、303、307和308,默认值是302。下面是ngx.redirect重定向和rewrite重定向的对比:

location / {
    # 等同于 rewrite ^/ http://testnginx.com/test? redirect;
    rewrite_by_lua_block {
        return ngx.redirect("/test")
    }
}

上述配置使用了默认的302状态。如果在跳转过程中需要保留请求的参数,可作如下配置:

location / {
    #  等同于 rewrite ^/ http://testnginx.com/test permanent;
    rewrite_by_lua_block {
        local ngx = require "ngx";
        return ngx.redirect("/test?" ..  ngx.var.args  ,301)
    }
}

也可以自定义参数,如下所示:

return ngx.redirect("/test?test=1&a=2" ,301)

支持跳转到其他域名,如http://abc.testnginx.com:
return ngx.redirect("http://abc.testnginx.com",301)

注意:跳转时都加return指令,其作用是为了强调跳转操作,官方推荐使用这种方式。

ngx.req.set_uri
语法:ngx.req.set_uri (uri, jump?)
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua
含义:用参数uri来重写当前的URL,和Nginx的rewrite内部重定向功能相似。例如,rewrite的指令rewrite ^ /test last; 与ngx.req.set_uri("/test", true)功能相似,而rewrite ^ /test break; 与ngx.req.set_uri("/foo", false)功能相似。
如果需要在跳转过程中修改参数,可以使用ngx.req.set_uri_args来完成新的参数配置,操作如下:

ngx.req.set_uri_args("a=1&b=2&c=3")
ngx.req.set_uri("/test", true)

ngx.exec
语法:ngx.exec(uri, args?)
配置环境:rewrite_by_lua,access_by_lua,content_by_lua
含义:使用uri、args参数来完成内部重定向,类似于echo-nginx-module 的echo_exec指令。
示例:

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
        content_by_lua_block {
            return ngx.exec('/test');
        }
    }
    location /test {
        content_by_lua_block {
            ngx.say(ngx.var.args);
        }        
    }
}

下面是ngx_exec指令常用的几种参数设置的示例。
保留之前的参数,如“ngx.exec('/test',ngx.var.args);”。
保留之前的参数,并新增参数,如“ngx.exec('/test' ,ngx.var.args .. 'd=4');”。
去掉之前的参数,并新增参数,如“ngx.exec('/test' , 'd=4');”。

注意:ngx_exec是一个内部重定向指令,不涉及外部的HTTP请求。在使用中推荐采用return ngx.exec(…)的方式。

16.2 日志记录
在使用Lua进行开发的过程中,需要使用日志来输出异常和调试信息,在Lua API中可以使用ngx.log来记录日志。
ngx.log
语法:ngx.log(log_level, ...)
配置环境:init_by_lua,init_worker_by_lua,set_by_lua,rewrite_by_lua,accessby lua,content_by_lua,header_filter_by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificate_by_lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua
含义:根据log_level的等级,将内容记录到error.log的日志文件中。
log_level的级别及其说明见表7-6(和Nginx的error.log日志级别是一致的)。
表7-6 log_level的级别及其说明
Lua-Nginx-Module常用指令(下)
续表
Lua-Nginx-Module常用指令(下)

示例:

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
        content_by_lua_block {
            ngx.say("test ")
            ngx.say("nginx ")
            ngx.log(ngx.ALERT, 'Log Test Nginx')
            ngx.log(ngx.STDERR, 'Log Test Nginx')
            ngx.log(ngx.EMERG, 'Log Test Nginx')
            ngx.log(ngx.ALERT, 'Log Test Nginx')
            ngx.log(ngx.CRIT, 'Log Test Nginx')
            ngx.log(ngx.ERR, 'Log Test Nginx')
            ngx.log(ngx.WARN, 'Log Test Nginx')
            ngx.log(ngx.NOTICE, 'Log Test Nginx')
            ngx.log(ngx.INFO, 'Log Test Nginx')
            ngx.log(ngx.DEBUG, 'Log Test Nginx')
        }
    }

}

执行结果如下:
curl -i 'http://testnginx.com/'
查看error.log 日志,默认在logs/error.log文件中,示例如下:

2018/06/11 11:18:26 [alert] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):4: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"
2018/06/11 11:18:26 [] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):5: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"
2018/06/11 11:18:26 [emerg] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):6: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"
2018/06/11 11:18:26 [alert] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):7: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"
2018/06/11 11:18:26 [crit] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):8: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"
2018/06/11 11:18:26 [error] 1180#1180: *34 [lua] content_by_lua (nginx.conf:66):9: Log Test Nginx, client: 10.19.48.161, server: testnginx.com, request: "GET / HTTP/1.1", host: "testnginx.com"

观察error.log日志可发现,它并没有输出所有级别的日志,这是因为Nginx中error.log的日志级别会影响Lua中日志的级别。如果将error.log的级别修改如下:
error_log /usr/local/nginx_1.12.2/logs/error.log info;
这样Lua的日志就可以打印到INFO级别了,如果需要DEBUG级别的日志,重新编译Nginx并开启DEBUG模式即可。
ngx.log支持多个字符串合并输出,字符串之间以逗号分隔,示例如下:
ngx.log(ngx.ERR, 'Log Test Nginx', 'a', 'b', 'c')
ngx.log单条日志可输出的最大字节数受Nginx的限制,默认最多是2048个字节,即2K。
Lua提供了print命令来简化INFO级别的日志的输出。下面两条语句的作用是一样的:
print("Log Test Nginx ")
ngx.log(ngx.INFO, 'Log Test Nginx')
注意:ngx.print 和print 是两条命令,不要混淆了。

16.3 请求中断处理
在Lua中,可以对请求进行中断处理,有两种情况,如下:
1.中断整个请求,则请求不再继续执行,直接返回到客户端。
2.中断当前的执行阶段,请求会继续执行下一个阶段,并继续响应请求。
它们都是通过ngx.exit指令完成的。

ngx.exit
语法:ngx.exit(status)
配置环境:rewrite_by_lua,access_by_lua,content_by_lua,header_filter_by_lua,ngx.timer.,balancer_by_lua,ssl_certificate_by_lua,ssl_session_fetch_by_lua,sslsession store_by_lua
含义:参数status的值是HTTP的状态码。当参数status>=200时,请求会被中断,并将status的值作为状态值返回给Nginx。
当参数status==0时,请求会中断当前的执行阶段,继续执行下一个阶段(前提是还有下一个阶段)。
配置环境:init_by_lua
,set_by_lua,rewrite_by_lua,access_by_lua,contentby lua,header_filter_by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancerby lua,ssl_certificate_by_lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua
Ngx_Lua HTTP状态码清单见表7-9。
表7-9 Ngx_Lua HTTP状态码清单
Lua-Nginx-Module常用指令(下)
续表
Lua-Nginx-Module常用指令(下)
下面是一个HTTP状态码为0的示例:

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
        set $a  '0';
        rewrite_by_lua_block {
             ngx.var.a = '1';
             --等价于 ngx.exit(0), 0即HTTP状态码
             ngx.exit(ngx.OK)
        }
        echo $a;  #执行结果等于1 
    }
}

ngx.exit(ngx.OK)可以让Nginx退出当前的rewrite_by_lua_block阶段,继续执行下面的阶段,如上面代码中的echo。
如果想中断当前的请求,不再继续后面的执行阶段,可以设置两种退出状态:
设置状态码大于或等于200且小于300,表示成功退出当前请求。
设置状态码大于或等于500,或其他异常的状态,表示失败退出当前请求。
继续使用上面的例子,这次以非0的状态码退出当前请求,如下所示:

location / {
    set $a  '0';           
    rewrite_by_lua_block {
         ngx.var.a = '1';
         ngx.exit(200)  --也可以换成500,数字代表状态码的值
    }
         echo $a;  #没有执行到这一句
}

因为使用了200状态码,所以请求在ngx.exit处被中断后退出了,所以无法执行echo输出的命令。为了强调退出操作,可以在此命令前加上return,如下所示:

return ngx.exit(ngx.OK)

十七、提升开发和测试效率

在使用Lua进行开发的过程中,可能需要频繁修改Lua代码,默认情况下都需重启Nginx才能使修改生效。使用lua_code_cache指令可以对其进行重新配置,并以此来提升开发效率。

语法:lua_code_cache on | off
默认:lua_code_cache on
配置环境:http,server,location,location if

含义:打开或关闭_by_lua_file指定的Lua代码及Lua模块的缓存。如果设置为off,则代码缓存会被关闭,在_by_lua_file修改的代码不需要重载 Nginx配置就可以生效。

注意:此指令只适合用于_by_lua_file中的代码,不适用于 _by_lua_block 和 *_by_lua中的代码,因为这两种指令的代码都是内嵌到Nginx配置文件中的,必须通过reload配置文件才可以使修改生效。把lua_code_cache设置为on只适合在开发环境中使用,不适合在线上环境中使用。

17.1 断开客户端连接
对于某些API请求,客户端只管发送并不等待返回结果,例如,触发一个请求通知远程服务端执行某个任务或进行日志推送。此时,可以使用如下指令断开连接。

ngx.eof
语法:ok, err = ngx.eof()
配置环境:rewrite_by_lua,access_by_lua,content_by_lua*
含义:显示指定响应的输出结束,会告知客户端主动关闭连接,并在服务器端继续执行剩下的操作。

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
        set $a '0';
        content_by_lua_block {
            ngx.var.a = '1';
            --告知客户端主动断开连接
            ngx.eof()
            ngx.sleep(3);  --让请求休眠3s。
            ngx.log(ngx.ERR, 'Test Nginx---',ngx.var.a)
        }
    }
}

执行curl -i 'http://testnginx.com/后,请求会立刻响应一个200状态,表示响应内容已返回,但请求的后续操作仍在服务器端继续执行,3s后会将日志写入error.log
注意:执行完ngx.eof后,如果下一步是发送子请求的指令,那么,子请求会被意外中止,导致无法完成子请求的响应,这是受Nginx中proxy_ignore_client_abort默认值的影响,将proxy_ignore_client_abort设置为on,就可以在执行ngx.eof后继续响应子请求了。

17.2 请求休眠

ngx.sleep
语法:ngx.sleep(seconds)
配置环境:rewrite_by_lua,access_by_lua,content_by_lua,ngx.timer.,sslcertificate by_lua,ssl_session_fetch_by_lua
含义:通过ngx.sleep命令可以在不阻塞Nginx worker进程的情况下,让当前请求休眠指定时间(seconds),seconds最小值为0.001s。
示例:

location / {
    content_by_lua_block {
        --5秒后输出ok。
        ngx.sleep(5);
        ngx.say('ok')
    }
}

17.3 获取系统时间
在Ngx_lua中获取系统时间,都是从Nginx的时间缓存中读取的,不涉及系统调用(系统调用的Lua命令类似于通过os.time获取系统时间)。相关指令的配置环境都是相同的,都适用于如下执行阶段。
init_worker_by_lua, set_by_lua, rewrite_by_lua, access_by_lua, content_by_lua, header_filter_by_lua, body_filter_by_lua, log_by_lua, ngx.timer., balancer_by_lua, ssl_certificate_by_lua, ssl_sessionfetch by_lua, ssl_session_store_by_lua*
在Ngx_lua中获取系统时间的示例如下:

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
       content_by_lua_block {
            ngx.say('ngx.today: ',ngx.today())
            ngx.say('ngx.time: ',ngx.time())
            ngx.say('ngx.now: ',ngx.now())
            ngx.say('ngx.localtime: ',ngx.localtime())
            ngx.say('ngx.utctime: ',ngx.utctime())
            ngx.say('ngx.cookie_time: ',ngx.cookie_time(1528721405))
            ngx.say('ngx.parse_http_time: ',ngx.parse_http_time('Mon, 11-Jun-18 12:50:05 GMT'))
            ngx.say('ngx.update_time: ',ngx.update_time())
       }
    }
}

执行结果如下:
ngx.today: 2018-06-11 #返回系统的本地时间,只包含年、月、日
ngx.time: 1528721734 #返回当前时间的Unix时间戳
ngx.now: 1528721734.775 #返回当前时间的Unix时间戳,浮点数类型,小数部分是毫秒级别
ngx.localtime: 2018-06-11 20:55:34 #返回当前时间
ngx.utctime: 2018-06-11 12:55:34 #返回UTC(Coordinated Universal Time,即世界标准世界)时间
ngx.cookie_time: Mon, 11-Jun-18 12:50:05 GMT #返回一个可以让Cookie过期的时间格式,参数是Unix时间戳格式
ngx.http_time: Mon, 11 Jun 2018 12:50:05 GMT #返回一个可以做HTTP头部的时间格式,如expires或last-modified
ngx.parse_http_time: 1528721405 #返回Unix时间戳,和ngx.http_time输出的时间格式不一样
ngx.update_time: #返回空,作用是强行更新Nginx的时间缓存,此操作会增加性能开销,不建议使用

17.4 编码与解码
利用Ngx_Lua的API,可以进行编码和解码的操作。

ngx.escape_uri
语法:newstr = ngx.escape_uri(str)
配置环境:init_by_lua,init_worker_by_lua,set_by_lua,rewrite_by_lua,accessby lua,content_by_lua,header_filter_by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificate_by_lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua ngx.quote_sql_str
含义:对参数str进行URI编码。

ngx.unescape_uri
语法:newstr = ngx.unescape_uri(str)
配置环境:init_by_lua,init_worker_by_lua,set_by_lua,rewrite_by_lua,accessby lua,content_by_lua,header_filter_by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificate_by_lua
含义:对参数str进行URI解码。

ngx.encode_args
语法:str = ngx.encode_args(table)
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,sslcertificate by_lua
含义:按照URI编码规则,将Lua提供的table类型数据编码成一个字符串。

ngx.decode_args
语法:table, err = ngx.decode_args(str, max_args?)
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificateby lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua
含义:将URI编码的字符串解码为Lua的table类型的数据。

ngx.md5
语法:digest = ngx.md5(str)
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificateby lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua
含义:对str字符串进行MD5加密,并返回十六进制的数据。

ngx.md5_bin
语法:digest = ngx.md5_bin(str)
配置环境:set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua,headerfilter by_lua,body_filter_by_lua,log_by_lua,ngx.timer.,balancer_by_lua,ssl_certificateby lua,ssl_session_fetch_by_lua,ssl_session_store_by_lua
含义:对str字符串进行MD5加密,并返回二进制的数据。
注意:ngx.escape_uri和ngx.unescape_uri作用相反,ngx.encode_args和ngx.decode_args的作用相反。
关于编码、解码操作的示例如下:

server {
    listen       80;
    server_name  testnginx.com;
    default_type 'text/plain';
    location / {
       content_by_lua_block {
            local ngx = require "ngx";
            --对URI进行编码
            ngx.say(ngx.var.uri, '---ngx.escape_uri---',ngx.escape_uri(ngx.var.uri))

            --对已经编码过的URI进行解码
            ngx.say('%2Ftest%2Fa%2Fb%2Fc', '---ngx.unescape_uri---',ngx.unescape_uri('%2Ftest%2Fa%2Fb%2Fc'))

            --将Lua的table类型数据编码成字符串
            local args_table_new =  ngx.encode_args({a = 1, b = 2, c = 3 })
            ngx.say('{a = 1, b = 2, c = 3 }', '---ngx.encode_args---' ,args_table_new)

            --对URI编码的字符串进行解码,解码成table类型的数据
            local args = ngx.var.args
            local args_table = ngx.decode_args(args)
            ngx.say(args, '---ngx.decode_args---', 'a=',args_table["a"])  --获取table中的a的值
            --对URI进行MD5编码,返回十六进制数据
            ngx.say(ngx.var.uri, '---ngx.md5---',ngx.md5(ngx.var.uri))
            --对URI进行MD5编码,返回二进制数据   
            ngx.say(ngx.var.uri, '---ngx.md5_bin---',ngx.md5_bin(ngx.var.uri))
       }
    }
}

执行结果如下:


# curl  'http://testnginx.com/test/a/b/c?a=1&b=2&c=3'
/test/a/b/c---ngx.escape_uri---%2Ftest%2Fa%2Fb%2Fc
%2Ftest%2Fa%2Fb%2Fc---ngx.unescape_uri---/test/a/b/c
{a = 1, b = 2, c = 3 }---ngx.encode_args---b=2&a=1&c=3
a=1&b=2&c=3---ngx.decode_args---a=1
/test/a/b/c---ngx.md5---dfa371a9a8f52c9aadd016bda535fa43
/test/a/b/c---ngx.md5_bin---ߣq©¨