openresty定时器ngx.timer详解

一、主要作用:

通过在后台启用nginx轻线程在指定的延时后,调用指定的函数,可以实现类似于javascript中的setTimeout和setInterval类似的功能。

注意:

  • 有些ngx_lua中的API不能在timer中调用,因为timer是与任何请求无关的,因此,比如子请求、ngx.req.*和向下游输出的API(如ngx.print,ngx.flush等)不能在timer中使用。

二、主要指令

这两个指令决定了定时器最大运行数量和等待数量

  • 1、lua_max_running_timers

指定最大运行的定时器数量(默认值为256)

  • 2、lua_max_pending_timers

指定最大等待运行的定时器的数量(默认值为1024)

三、主要API

1、ngx.timer.at
  • 用法:ok,err = ngx.timer(delay, callback, arg1, arg2,...)

  • 参数:

    • delay表示延迟的时间,以秒为单位(可以使用小数)
    • callback表示延迟后执行的函数
    • arg1,arg2,…表示传入callback的参数
    • callback的定义如下:
  1. local handler
  2. handler = function(premature)
  3. //要执行的功能代码
  4. end

注:

  • premature这个参数不是调用ngx.timer.at传入的参数,它是一个固定的参数,表示定时器是否过期
  • 作用:在指定的延迟时间(delay)后,执行callback这个函数

例如:

在5秒后,将字符串”create timer success”记录到日志中

  1. local ok,err = ngx.timer.at(5, function()
  2. ngx.log(ngx.ERR, 'create timer success')
  3. end)
2、ngx.timer.running_count

用法:count = ngx.timer.running_count()

作用:获取正在运行的定时器的数量

3、ngx.timer.pending_count

用法:count = ngx.timer.pending_count()

作用:获取正在等待的定时器的数量

四、通过ngx.timer.at来实现定时任务

在openresty中一般使用init_worker_by_lua*指令与ngx.timer.at来配合使用实现定时任务

注:

  • init_worker_by_lua*指令会在每个woker初始化时执行,因此在有多个worker的情况下,得判断worker的ID,让指定的worker来执行某个定时任务,以防止重复执行
  • ngx.timer.at只会执行一次,它的功能类似于javascript的setTimeout函数,因此要实现定时任务,需要嵌套使用ngx.timer.at
1、在每隔X秒执行一个函数

实现类似于javascript中的setInterval的功能

  1. local delay = 5
  2. local handler
  3. handler = function(premature)
  4. if not premature then
  5. ngx.log(ngx.ERR, os.date("%Y-%m-%d %H:%M:%S", ngx.time()).." : create timer success")
  6. local ok,err = ngx.timer.at(delay, handler)
  7. if not ok then
  8. ngx.log(ngx.ERR, "failed to create the timer:", err)
  9. end
  10. end
  11. end
  12. if ngx.worker.id() == 1 then
  13. local ok,err = ngx.timer.at(delay, handler)
  14. if not ok then
  15. ngx.log(ngx.ERR, "failed to create the timer:", err)
  16. end
  17. end

重启nginx,我们查看nginx的错误日志:

2、实现在某个固定时间执行一个函数

类现类似于Linux的crontab的功能

  • 原理:每次在触发定时器函数时判断当前时间与执行时间是否一致,如果一致,则执行功能代码。(这里要注意的是定时器延迟时间的粒度问题,要考虑固定时间的时间点的粒度与延迟时间的粒度,比如这个定时器是每5秒执行一次,如果这个定时任务,精确到秒,那么需要注意是否可以满足其精确度)

例如:在每天的02:00时向日志输入一条信息

  1. local delay = 5 -- 延迟时间
  2. local interval_time = '0200' -- 执行任务的时间点,格式可以随意,如02:00
  3. local handler
  4. handler = function(premature, interval_time)
  5. if not premature then
  6. local nowtime = os.date("%H%M")
  7. if nowtime == interval_time then
  8. ngx.log(ngx.ERR, ngx.today().." : create interval task success")
  9. -- 这是要修改interval_time的时间点,不然会在每隔5秒又重复执行
  10. interval_time = tostring(tonumber(interval_time) - 1)
  11. end
  12. local ok, err = ngx.timer.at(delay, handler, interval_time)
  13. if not ok then
  14. ngx.log(ngx.ERR, "failed to create the timer:", err)
  15. return
  16. end
  17. end
  18. end
  19. if ngx.worker.id() == 1 then
  20. local ok, err = ngx.timer.at(delay, handler, interval_time)
  21. if not ok then
  22. ngx.log(ngx.ERR, "failed to create the timer:", err)
  23. return
  24. end
  25. end