ngx_lua之ngx.timer.at

语法

ok, err = ngx.timer.at(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.*, balancer_by_lua*, ssl_certificate_by_lua*


使用方法

使用用户自定义的回调函数以及可选的自定义参数创建一个Nginx定时器。
第一个参数delay用于指定定时器的延迟,单位是秒。可以将其指定为小数,比如0.001代表1毫秒,也可以将delay指定为0,在这种情况下,当当前的handler执行的时候,定时器会立即到期(可以理解为:立即执行用户自定义的回调函数)。
第二个参数callback可以是任何Lua函数,在指定的延迟之后,这个回调函数会在后台的“轻量级线程”中被调用。用户自定义的回调函数会自动的被Nginx核心使用prematureuser_arg1user_arg2等参数调用。premature是一个布尔值,代表定时器是否是提前到期的。user_arg1user_arg2等参数是在调用ngx.timer.at的时候指定的用户自定义参数。
当Nginx worker进程尝试关闭的时候,定时器会提前到期。比如:通过HUP信号触发的配置文件重载,或者是关闭Nginx。当Nginx Worker尝试关闭的时候,不能再使用非0的延迟调用ngx.timer.at创建新的定时器。在这种情形下,ngx.timer.at会返回nil和一个描述错误信息的字符串("process exiting")。
v0.9.3版本开始,即使Nginx worker进程开始关闭,也允许创建0延迟计时器。
当一个定时器到期的时候,定时器回调函数中的Lua代码会在后台的“轻量级线程”中执行。它与创建定时器的原始请求是完全分离的。因此与创建定时器的请求具有相同生命周期的对象,比如cosockets,是不能在原始的请求和定时器回调函数中共享的。
下面是一个简单的例子:

 location / {
     ...
     log_by_lua '
         local function push_data(premature, uri, args, status)
             -- push the data uri, args, and status to the remote
             -- via ngx.socket.tcp or ngx.socket.udp
             -- (one may want to buffer the data in Lua a bit to
             -- save I/O operations)
         end
         local ok, err = ngx.timer.at(0, push_data,
                                      ngx.var.uri, ngx.var.args, ngx.header.status)
         if not ok then
             ngx.log(ngx.ERR, "failed to create timer: ", err)
             return
         end
     ';
 }


也可以创建无限重复执行的定时器。比如,通过在计时器的回调函数中,递归地调用ngx.timer.at,实现每5秒触发一次的定时器。下面是一个简单的例子:

 local delay = 5
 local handler
 handler = function (premature)
     -- do some routine job in Lua just like a cron job
     if premature then
         return
     end
     local ok, err = ngx.timer.at(delay, handler)
     if not ok then
         ngx.log(ngx.ERR, "failed to create the timer: ", err)
         return
     end
 end

 local ok, err = ngx.timer.at(delay, handler)
 if not ok then
     ngx.log(ngx.ERR, "failed to create the timer: ", err)
     return
 end


因为定时器回调函数是在后台运行的,他们的运行时间没有添加到任何客户端请求的响应时间中,因此,定时器很容易在服务器内部堆积,消耗系统资源。
为了阻止某些极端的后果,比如Nginx服务器崩溃,在Nginx worker进程内,对挂起的定时器的数量和正在运行的定时器的数量都有限制。挂起的定时器就是还没有到期的定时器,正在运行的定时器是指回调函数正在运行的定时器。
挂起的定时器的最大数量是由lua_max_pending_timers指令控制的,正在运行的定时器的最大数量是由lua_max_running_timers指令控制的。
当前的实现是:每个正在运行的定时器都会从全局的连接记录列表(在nginx.conf中通过worker_connections指定配置的)中消耗一个连接(本质上是一个伪连接)。因此确保worker_connections指令被设置为一个足够大的值,以便能够同时处理真正的连接和定时器回调函数需要的伪连接。
许多Nginx的Lua API都可以在定时器中使用,比如ngx.socket.tcpngx.socket.udpngx.shared.DICTcoroutine.*ngx.thread.*ngx.exitngx.now/ngx.timengx.md5/ngx.sha1_bin。但是子请求API,比如ngx.location.capturengx.req.* API,downstream输出API,比如ngx.sayngx.printngx.flush是无法使用的。
可以显式的使用用户自定义参数或隐式的使用闭包,传递大多数的Lua值(nils,booleans,numbers,strings,tables,closures,file handles等)给定时器回调函数。不能传递给定时器回调函数的对象包括:coroutine.createngx.thread.spawn返回的任何线程对象,ngx.socket.tcpngx.socket.udpngx.req.socket返回的任何cosocket对象,因为这些对象的生命周期被绑定到了创建它们的请求上下文中,而定时器回调函数与创建请求的上下文是完全分离的,它运行在自己的伪请求上下文中。如果企图共享这类对象的话,会报错no co ctx found(thread对象)或bad request(cosocket对象)。因此,应该在定时器回调函数中创建这些对象。


参考文档

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: