• openresty定时任务


    定时任务场景

    OpenResty 中,有时候需要在后台定期地执行某些任务,比如同步数据、清理日志等。OpenResty 提供了 ngx.timer 来解决这类需求,可以把ngx.timer看作是 OpenResty 模拟的客户端请求,用以触发对应的回调函数。

    OpenResty 的定时任务可以在任意处理阶段发起任意多个定时器,执行任意的功能。分为下面两种:

    • ngx.timer.at,用来执行一次性的定时任务;
    • ngx.time.every,用来执行固定周期的定时任务。
      使用ngx.timer可以突破 init_worker_by_lua 中不能使用 cosocket 的限制。

    一次性定时任务

    下面这段代码,启动了一个延时为0的定时任务。它启动了回调函数 handler,并在这个函数中,用 cosocket 去访问一个网站:

    init_worker_by_lua_block {
        local function handler()
            local sock = ngx.socket.tcp()
            local ok, err = sock:connect(“www.baidu.com", 80)
        end
        local ok, err = ngx.timer.at(0, handler)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样,就绕过了 cosocket 在这个阶段不能使用的限制。
    ngx.timer.at 并没有解决周期性运行这个需求,在上面的示例中,它是一个一次性的任务。

    周期性定时任务

    基于 ngx.timer.at 这个API,如果要做到周期性运行:
    1、可以在回调函数中,使用一个while true 的死循环,执行完任务后 sleep 一段时间,自己来实现周期任务;
    2、你还可以在回调函数的最后,再创建另外一个新的 timer。
    timer 的本质是一个请求,虽然这个请求不是终端发起 的;而对于请求来讲,在完成自己的任务后它就要退出,不能一直常驻,否则很容易造成各种资源的泄漏。
    第一种使用 while true 来自行实现周期任务的方案并不靠谱。第二种方案虽然是可行的,但递归地创建 timer ,并不容易理解。

    OpenResty 后面新增的 ngx.time.every API,就是专门为了解决这个问题而出现的,它是更加接近 crontab 的解决方案。
    在启动了一个 timer 之后,就再也没有机会来取消这个定时任务了,毕竟 ngx.timer.cancel 还是一个 todo 的功能。
    就会面临一个问题:定时任务是在后台运行的,并且无法取消;如果定时任务的数量很多,就很容易耗尽系统资源。

    所以,OpenResty 提供了 lua_max_pending_timers 和 lua_max_running_timers 这两个指令,来对其进行限制。前者代表等待执行的定时任务的最大值,后者代表当前正在运行的定时任务的最大值。
    可以通过 Lua API,来获取当前等待执行和正在执行的定时任务的值:

    content_by_lua_block {
        ngx.timer.at(3, function() end)
        ngx.say(ngx.timer.pending_count())
    }
    
    • 1
    • 2
    • 3
    • 4

    这段代码会打印出 1,表示有 1 个计划任务正在等待被执行。

    content_by_lua_block {
        ngx.timer.at(0.1, function() ngx.sleep(0.3) end)
        ngx.sleep(0.2)
        ngx.say(ngx.timer.running_count())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这段代码会打印出 1,表示有 1 个计划任务正在运行中。

    ngx.timer.at(0, func)

    指定0延迟,在这种情况下,当前处理程序执行时,计时器将立即过期。当计时器过期时,计时器回调中的Lua代码在一个“轻线程”中运行,该线程与创建计时器的原始请求完全分离。
    因此,与创建它们的请求具有相同生存期的对象(如cosocket)不能在原始请求和计时器回调函数之间共享。

    Nginx的许多Lua API都是在计时器回调的上下文中启用的,比如流/数据报cosockets(ngx.socket.tcp和ngx.socket.udp)、共享内存字典(ngx.shared.DICT)、用户协程(协程)、用户“轻线程”(ngx.thread)、ngx.exit、ngx.now/ngx.time、ngx.md5/ngx。sha1_bin都是允许的。但是子请求API(如ngx.location.capture)、ngx.req.*API、下游输出API(如ngx.say、ngx.print和ngx.flush)在此上下文中被显式禁用。

    可以将大多数标准Lua值(nils、布尔值、数字、字符串、表、闭包、文件句柄等)显式地作为用户参数或隐式地作为回调闭包的up值传递给计时器回调。然而,有几个例外:不能传递coroutine.create和ngx.thread.spawn返回的任何线程对象,或ngx.socket.tcp、ngx.socket.udp和ngx.req.socket返回的任何cosocket对象。因为这些对象的生存期绑定到创建它们的请求上下文,而计时器回调与创建请求的上下文分离(通过设计),并在其自己的(假)请求上下文中运行。如果试图在创建请求的边界上共享线程或cosocket对象,则会出现“未找到co-ctx”错误(对于线程)或“错误请求”(对于cosocket)。但是,可以在计时器回调中创建所有这些对象。

    OpenResty:特权进程和定时任务

  • 相关阅读:
    面试MySQL
    微信小程序基础
    一维时间序列信号的小波时间散射变换(MATLAB 2021)
    SpringBoot使用@Async的总结!
    ava异常处理面试题及答案
    某手创作服务 __NS_sig3 sig3 | js 逆向
    linux常用命令及解释大全(一)
    【吃瓜之旅】第五章吃瓜学习
    凉鞋的 Unity 笔记 101. Hello Unity
    C++超复杂的构造和析构函数执行顺序详解
  • 原文地址:https://blog.csdn.net/songfeihu0810232/article/details/128120282