skynet框架:日程表服务

实现一个日程表服务(crontab),支持

  1. 服务启动自动对齐系统时间,以秒为单位检查触发定时任务(task);
  2. 以服务(同节点)为单位注册定时任务方式:
    1)以自然时间字符串方式:2024-01-01 00:00:00
    2)以定时间隔方式:每秒/每分/每时/每天/每周/每月/每年
  3. 取消指定定时任务;
  4. 定时任务触发逻辑支持超时释放;
  5. 单例;

对齐系统时间:服务启动时计算当前时间到下一秒的间隔,定时驱动日程表心跳raw_timeout

local _, offset = math.modf(skynet.time())
local leftms = 100 - timeout_factor(offset) -- 到下一秒的间隔值
skynet.timeout(leftms, raw_timeout)

function raw_timeout()
    skynet.timeout(100, raw_timeout) -- 每秒触发
    skynet.fork(remind_tasks)
end

function remind_tasks()
    local cbt = skynet.timefloor()
    self._t_frames[cbt-1] = nil -- 释放上一秒的任务集
    local frames = self._t_frames[cbt]
    if table.empty(frames) then
        return
    end
    for _, frame in ipairs(frames) do
        frame:remind() -- 触发任务处理
    end
end

提供注册任务API

---@description 为source服务注册自然时间为timestr的定时任务
---@param
---| '"time2str"'   # 自然时间字符串
---| '"source"'     # 服务id
---| '"proto"'      # 触发回调时使用的协议类型,默认是lua
---| '"cmd"'        # 任务回调的CMD
---| '"ltype"'      # 循环任务的类型:每秒/每分等
---| '"..."'        # 透传给任务回调处理的参数
function mark(timestr, source, proto, cmd, ltype, ...) end

---@description 为source服务注册循环类型定时任务,默认使用lua类型
---@param
---| '"ltype"'      # 循环任务的类型:每秒/每分等
---| '"lparam"'     # 循环任务的自定义参数
function loopmark(ltype, lparam, source, cmd, ...) end

---@description 循环任务的类型
TASK_LOOP_TYPE_SEC = 1 ---# 每秒
TASK_LOOP_TYPE_MIN = 2 ---# 每分钟
TASK_LOOP_TYPE_HUR = 3 ---# 每小时
TASK_LOOP_TYPE_DAY = 4 ---# 每天
TASK_LOOP_TYPE_WEK = 5 ---# 每周
TASK_LOOP_TYPE_MON = 6 ---# 每月
TASK_LOOP_TYPE_YEA = 7 ---# 每年

取消定时任务API

---@description 取消source服务的定时任务
function unmark(timestr, source, cmd, ltype) end

任务处理逻辑需要处理容错、与主流程解耦

---@description 对source服务发起请求,超时interval返回失败处理
function safe_call(interval, source, proto, cmd, ...)
    interval = interval or 1
    local co = coroutine.running()
    local ret
    skynet.fork(function(...)
        ret = table.pack(skynet.call(source, proto, cmd, ...) or false)
        if co then skynet.wakeup(co) end
    end, ...)
    skynet.sleep(interval * 100)
    co = nil
    if not ret then return false end
    return table.unpack(ret)
end

设计skynet框架下服务

local skynet = require "skynet"
local debug = require "skynet.debug"

local Command = { _crontab = nil }

function Command.init()
    Command._crontab = require "crontab"
    assert(Command._crontab, "_crontab init error")
end

local function proto_lua_dispatch(_, _, cmd, ...)
    local f = Command._crontab[cmd]
    if f then
        local msg, len = skynet.pack(f(Command._crontab, ...))
        skynet.ret(msg, len)
    else
        skynet.error(string.format("Unknown command : [%s]", tostring(cmd)))
        skynet.response()(false)
    end
end

local function init()
    skynet.init(Command.init)
    skynet dispatch("lua", proto_lua_dispatch)
end

skynet.start(init)
posted @ 2024-07-12 22:12  linxx-  阅读(4)  评论(0编辑  收藏  举报