跨服节点部署后,每日结算任务在两个节点同时跑了一遍,排行奖励发了双份。任务调度的分布式锁保证同一时刻只有一个节点执行:锁键记录持有节点,60 秒心跳续期,节点失联 300 秒后锁自动释放给其他节点接手。锁机制的目标很纯粹:同一任务全集群任一时刻至多一个执行者。
节点执行任务前先抢锁:锁键为空即可写入自己的节点标识,锁被其他节点持有时直接跳过本轮执行,不做等待。抢锁失败的节点只记录一次让位日志,避免每个检查周期都刷一条无意义的重复日志。节点标识统一用固定 ID 而不是可变名字,ID 与机器的对应关系登记在运维台账。
local LOCK_KEY = "SCHED_LOCK_DAILY"
local NODE_ID = "node_a"
local function tryLock()
local holder = getsysvar(LOCK_KEY) or ""
if holder == "" or holder == NODE_ID then
setsysvarex(LOCK_KEY, NODE_ID, 1)
return true
end
return false
end
持锁节点每 60 秒续一次心跳时间戳,其他节点发现心跳超过 300 秒没更新,判定持锁节点失联,清锁接手任务。
setontimerex(204, 60)
function ontimer204()
local holder = getsysvar(LOCK_KEY) or ""
local ts = tonumber(getsysvar("LOCK_TS") or "0")
if holder ~= "" and holder ~= NODE_ID and os.time() - ts > 300 then
setsysvarex(LOCK_KEY, "", 1)
sendcentermsg("任务锁 " .. LOCK_KEY .. " 超时释放")
end
if holder == NODE_ID then
setsysvarex("LOCK_TS", os.time(), 1)
end
end
双节点并发抢锁压测两百轮,同一时刻只有一个节点持有锁;持锁节点模拟宕机,300 秒后锁被其他节点接管;续期心跳断供即触发超时释放。抢锁成功的节点立即执行任务,抢锁失败的一方记录一次让位,不让任务空窗。监控锁的持有时长与切换次数,切换频繁说明有节点的心跳线程不稳定。
抢锁的读与写不是原子操作,两节点在同一个检查窗口同时写入,后写覆盖先写,依赖引擎系统变量的串行写入顺序才保证只有一方成功。锁超时释放没有区分正常长任务,长任务跑到 300 秒被别人抢走锁,任务时长登记进锁信息后按任务时长放宽。锁键全局只有一个,不同定时任务互相抢锁,锁键按任务名拆分。节点改名后旧名持有锁无法被自己续期,节点标识统一用固定 ID 而不是可变名字。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 行会活动集中在攻城战,日常缺乏轻量社交场景。行会篝火答题做行会固定社交活动:每周三、周日晚在行会驻地点燃…
实战应用:用在哪里 全服答题活动办过几期,参与的人不少但黏性不足,答完就散。科举答题把答题做成两段式:每天晚八点的乡试答题筛…
n n n 实战应用:用在哪里 n战斗中打字沟通慢且危险,队伍协作靠喊话效率太低。快捷短语栏在屏幕底部提供八条可自定义的战斗…
n n n 实战应用:用在哪里 n龙脉区域的野外宝箱是散人玩家的每日福利:每逢整点在龙脉区域随机刷新十五个宝箱,分三档给不同…
实战应用:用在哪里 同一玩家的两条消息走了不同的处理路径,先发的后到,装备穿上的消息比脱下的消息晚处理,面板状态错乱。消息有…
实战应用:用在哪里 战斗日志全量打印,一天写满一块硬盘,翻日志像大海捞针。日志采样按类别分策略:错误日志全量保留、行为日志按…