游戏开发:排行榜结算的方案设计

对玩法、活动的排行榜排名进行结算发奖,是常见的业务需求。结算以榜单为单位,根据排名对上榜单位(可能是玩家/联盟/单服)进行发奖,由相关的业务服务负责。讨论几种实现思路;

  1. 实时顺序结算

使用定时器驱动结算服务对业务的结算检查:

local function doSettle()
    local function pickReward(pos, unit)
        local id = unit.id
        if not rewardFlag then -- 奖励已领取标记
            setFlagAndPick(id)
        end
    end
	-- settle rank
    local pos2rankUnit = {--[[ get data from sort zset --]]}
    for pos=minRankIdx, maxRankIdx  do
        local unit = pos2rankUnit[pos]
        pickReward(pos, unit)
        skynet.sleep(100)	-- 削峰
    end
end

local function checkSettle()
    local function checkSchedule()
    	-- time to settle
    end
    if _check_lock then
    	return
    end
    _check_lock = true
    if checkSchedule() then
    	skynet.fork(function ()
            doSettle()
            _check_lock = nil
        end)
    end
end

skynet.timeout(100, checkSettle)

方案需要:

1)处理临界区保护,将核心结算逻辑通过_check_lock标记保护起来,避免被重复执行;

2)支持重入,发奖单位记录领取标记,避免重复发奖;

3)削峰,结算流程做定时休眠让出执行权,减少性能波动;

方案适用于对有限的榜单有限的排名进行结算。方案实时发奖结算,性能可控(单位时间内处理的业务量是固定的),消耗线性(榜单数量+每个榜单需要发奖的单位数量),一次性结算的榜单越多,需要的时间就越久(主要是削峰的时间),一般对全服性排名的玩法适用,比如某个赛季制玩法,赛季内全服排名,赛季结算时只对一个榜的前N名进行结算发奖;

单服内的玩法,比如新服冲榜,每个单服有固定数量的榜单,当同一时间需要结算的新服数量越多,完整结算完成需要的时间也越长,逻辑的滞后性会比较明显,当然实现需求没有问题;

  1. 多服务协作实时结算

一些业务场景下,可以明显地将结算分摊到多个结算服务中,比如多分组的玩法,每个分组独立排名结算,这时候结算更适合由每个分组的服务独立负责结算,各分组并行处理,最后只到玩法的中央服务标记完成,统计所有分组结算情况来判断结算全部完成;

方案是对1方案结算效率上的补充,总耗时大致等于单个分组的耗时,需要处理的是性能峰值问题,峰值取决于同时结算的分组数量,需要做时间分摊:

-- run on every group
local RANGE_SEC = 10 * 60 -- 10分钟结算完
local function doSettle()
	-- ...
end
-- 随机一个时间开始结算
skynet.timeout(math.random(RANGE_SEC), doSettle)
  1. 惰性触发式结算

通过上榜单位的行为(比如玩家登录)触发领奖计算检查,结算服务只在结算时刻保存榜单数据的快照,对外提供领奖检查API:

local function makeSnapshot()
	local function checkSchedule()
    	-- time to settle
    end
    if checkSchedule() then
    	if not picture then
            take_rank_picture() -- 保存排行榜数据
        end
    end
end
skynet.timeout(100, makeSnapshot)

function API_tryPickReward(id)
    local pos = picture[id]
	if pos and not rewardFlag then -- 奖励已领取标记
		setFlagAndPick(id)
    end
end

方案没有明显的性能边界情况,触发式的领奖方式可以将发奖流程分散到各个异步的请求动作里,受skynet的消息队列调度,性能上没有问题。非实时的方式,需要依赖上榜单位的触发,可能存在未领奖励的情况。更适合大范围的结算发奖,比如全服玩家发奖,登录和在线跨天触发,不登录的玩家不发奖,可以依据不同的业务需求考虑。

posted @ 2025-05-22 23:39  linxx-  阅读(66)  评论(0)    收藏  举报