击杀 BOSS 后要同时做三件事:发奖励、记流水、刷新排行榜,全都写在 killmon 触发里互相拖累,排行榜卡顿会拖慢发奖。消息队列把生产与消费解耦:触发只管把事件入队,独立的消费定时器按自己的节奏逐条处理,谁也不等谁。解耦之后各下游按自己的消费能力处理事件,上游触发路径缩短到入队一行,触发耗时方差缩小了一个数量级。
killmon 触发只做入队动作,消费定时器每秒最多处理 20 条,队列在高峰期充当缓冲,把瞬时洪峰摊平到后续秒级处理。
local QUEUE = {}
local function enqueue(kind, payload)
table.insert(QUEUE, { kind = kind, payload = payload, retry = 0 })
end
setontimerex(195, 1)
function ontimer195()
for i = 1, math.min(20, #QUEUE) do
local e = table.remove(QUEUE, 1)
local ok, err = pcall(dispatch, e)
if not ok and e.retry < 3 then
e.retry = e.retry + 1
table.insert(QUEUE, e)
end
end
end
处理失败的事件重试上限三次,超过后落审计放弃;队列长度超过 5000 条即告警,消费能力跟不上时优先扩容消费端。
local function dispatch(e)
if e.kind == "rank" then
refreshRank(e.payload)
elseif e.kind == "flow" then
saveFlow(e.payload)
end
end
local function queueLen()
return #QUEUE
end
压测灌入一万条事件,消费端逐条处理无丢失;故意让排行榜消费失败三次,核对事件落审计放弃;队列长度 5000 阈值告警触发及时。每秒二十条的消费速率按BOSS击杀高峰的实际产生速率倒推,高峰期队列水位稳定在千条以内。监控队列水位曲线与消费速率,水位持续走高就是消费端性能不足的前兆。
消费失败的 pcall 把错误吞掉没有日志,事件丢了都不知道丢在哪类,err 必须连同事件内容一起落审计。重试没有上限时,一条坏事件让队列原地空转,消费吞吐被拖垮,三次上限加放弃审计双保险。table.remove 从队头删除是平方级开销,队列上万条时每秒消费明显变慢,改用头指针跳跃加定期整理。队列只存内存,重启即丢,关键事件(发奖类)入队同时落一行流水,重启后按流水补偿。dispatch 的分发表用 kind 做键注册处理函数,新增下游只需注册一行,不用改消费循环本体。事件结构里加了入队时间戳,消费端可以看到每条事件的滞留时长,滞留曲线直接反映消费能力是否需要扩容。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 白天在线高峰多数人忙着下副本,夜间在线长尾需要一个轻社交场景。夜市集市每晚 20 点到 23 点在比奇中…
实战应用:用在哪里 好友系统的互动停留在点赞送花,缺一个紧密绑定的玩法。灵魂契约让两位玩家缔结契约:同图作战时共享契约等级带…
实战应用:用在哪里 新手礼包一元宝一件每人限购一次,定价本意是福利,结果被脚本注册的两千个小号搬空四千万金币等值的物资。限购…
实战应用:用在哪里 新值班凌晨三点遇到登录白屏,翻文档翻了小半个钟头才找到处置步骤。值班手册把常见故障的处置步骤结构化:每条…
实战应用:用在哪里 中文字库包含三万多个字符,完整字体包 30MB,低端机加载慢还吃内存。字体渲染方案把字库分成两档:常用 …
实战应用:用在哪里 经验副本每天限进五次,玩家老问“我还能进几次”。副本入口面板把次数余量与重置倒计时直接挂在入口按钮旁:余…