--[[ Author: zkj Date: 2021-05-27 14:43:17 LastEditTime: 2021-05-31 20:52:30 LastEditors: Please set LastEditors Description: 游戏业务节点分配服务 --]] local baseService = require("baseService") local nodeMgr = require("nodeMgr") local util_node = require("utils.util_node") local lib_game_redis = require("lib_game_redis") local root = {} -- match节点分配优化 -- | 1. 玩家更集中 -- | 2. 减少交互拥堵 - 如何判断拥堵 -- TODO:心跳检查 -- 开始心跳定时器 function root.start_timer_heart_beat() root.timerHeatBeat = create_timeout( 500, function() root.timer_out_heart_beat() end ) end function root.timer_out_heart_beat() root.timerHeatBeat.delete() root.timerHeatBeat = nil root.start_timer_heart_beat() if root.mapCluster == nil then return end local currTime = skynet_time() for k, v in pairs(root.mapCluster) do local isUpdate = false for _k, _v in pairs(v) do if _v.isAlive and currTime >= _v.updateTime + 20 then _v.isAlive = false log.warning("节点[%s]已失效", tostring(_v.nodeName)) isUpdate = true end end if isUpdate then root.update_weight(k) end end end -- 节点心跳 function root.node_heart_beat(nodeInfo) if is_empty(nodeInfo) or is_empty(nodeInfo.clusterName) or is_empty(nodeInfo.nodeName) then return false end local clusterName = nodeInfo.clusterName local nodeName = nodeInfo.nodeName if root.mapCluster == nil then root.mapCluster = {} end if root.mapCluster[clusterName] == nil then root.mapCluster[clusterName] = {} end nodeInfo.updateTime = skynet_time() nodeInfo.isAlive = true -- 是否更新权重 local nInfo = root.get_cluster_node_info(clusterName, nodeName) -- 重新加入节点 if nInfo == nil or not nInfo.isAlive then util_node:node_load_init_grains(nodeName) end local isUpdateWeight = false if nInfo == nil or not nInfo.isAlive or nInfo.weight ~= nodeInfo.weight then isUpdateWeight = true end root.mapCluster[clusterName][nodeName] = nodeInfo -- 更新权重 if isUpdateWeight then root.update_weight(clusterName) end return true end -- 停止节点 function root.node_stop(clusterName, nodeName) if is_empty(clusterName) or is_empty(nodeName) then return false end if root.mapCluster[clusterName] == nil then return false end if root.mapCluster[clusterName][nodeName] == nil then return false end root.mapCluster[clusterName][nodeName].isAlive = false -- 更新权重 root.update_weight(clusterName) return true end -- 更新权重 function root.update_weight(clusterName) if root.mapClusterWeight == nil then root.mapClusterWeight = {} end local weightInfo = {} weightInfo.total = 0 weightInfo.list = {} for k, v in pairs(root.mapCluster[clusterName]) do if v.isAlive then local info = {} info.nodeName = v.nodeName info.min = weightInfo.total + 1 weightInfo.total = weightInfo.total + (v.weight or 0) info.max = weightInfo.total table.insert(weightInfo.list, info) end end root.mapClusterWeight[clusterName] = weightInfo end -- 获取节点信息 function root.get_cluster_node_info(clusterName, nodeName) if is_empty(clusterName) or is_empty(nodeName) then return end if root.mapCluster == nil or root.mapCluster[clusterName] == nil then return end return root.mapCluster[clusterName][nodeName] end -- 更新玩家分配节点 function root.user_band_node(uid, clusterName, nodeName) if uid == nil then return false end if root.mapUid2NodeInfo == nil then root.mapUid2NodeInfo = {} end if root.mapUid2NodeInfo[uid] == nil then root.mapUid2NodeInfo[uid] = {} end root.mapUid2NodeInfo[uid][clusterName] = nodeName return true end -- 分配match节点 function root.dispatch_match_node(uid, conf) -- TODO:根据match节点下匹配队列人数分配 -- 本地管理匹配队列玩家列表? local clusterName = "match" -- 按照负载集中分配 local nodeName = root.user_dispatch_node_by_grain(uid, clusterName) if is_empty(nodeName) then -- 按照权重分配 nodeName = root.user_dispatch_node_by_weight(uid, clusterName) end if not is_empty(nodeName) then root.user_band_node(uid, clusterName, nodeName) return nodeName end end -- 分配战斗逻辑服节点 function root.dispatch_battle_node(uid) -- 按照权重做负载均衡 local clusterName = "battle" local nodeName = root.user_dispatch_node_by_weight(uid, clusterName) root.user_band_node(uid, clusterName, nodeName) if is_empty(nodeName) then return end -- 全服 - 战斗ID local battleId = lib_game_redis:hincrby("battle", "id", 1) if battleId >= 1000000000 then battleId = 1 lib_game_redis:hset("battle", "id", battleId) end local ret = {} ret.battleNode = nodeName ret.battleId = battleId return ret end -- 获取当前战斗数 function root.get_total_battle_count() if root.mapCluster == nil or root.mapCluster["match"] == nil then return 0 end local count = 0 for k, v in pairs(root.mapCluster["match"]) do local ok, battleCount = nodeMgr.call(v.nodeName, ".BattleMatch", "getBattleCount") if ok and battleCount then count = count + battleCount end end return count end -- 获取节点类型节点列表 function root.get_cluster_node_list(clusterName) if is_empty(clusterName) then return end if root.mapCluster == nil or root.mapCluster[clusterName] == nil then return end local nodeList = {} for k, v in pairs(root.mapCluster[clusterName]) do if v.isAlive then table.insert(nodeList, v.nodeName) end end return nodeList end ---------------------------------------- -- 分配玩家节点 ---------------------------------------- -- 按照节点权重 function root.user_dispatch_node_by_weight(uid, clusterName) if is_empty(clusterName) then return end if root.mapCluster == nil then return end if root.mapCluster[clusterName] == nil then return end -- 已分配 if uid and root.mapUid2NodeInfo and root.mapUid2NodeInfo[uid] and root.mapUid2NodeInfo[uid][clusterName] then local nodeName = root.mapUid2NodeInfo[uid][clusterName] local nodeInfo = root.get_cluster_node_info(clusterName, nodeName) if nodeInfo and nodeInfo.isAlive then return nodeName end end local rand = math.random(1, root.mapClusterWeight[clusterName].total) for k, v in ipairs(root.mapClusterWeight[clusterName].list) do if v.min <= rand and rand <= v.max then log.info("user_dispatch_node_by_weight uid[%s] nodeName[%s]", tostring(uid), tostring(v.nodeName)) return v.nodeName end end end -- 根据负载 function root.user_dispatch_node_by_grain(uid, clusterName) if is_empty(clusterName) or root.mapCluster == nil or root.mapCluster[clusterName] == nil then return end -- 已分配 if uid and root.mapUid2NodeInfo and root.mapUid2NodeInfo[uid] and root.mapUid2NodeInfo[uid][clusterName] then local nodeName = root.mapUid2NodeInfo[uid][clusterName] local nodeInfo = root.get_cluster_node_info(clusterName, nodeName) if nodeInfo and nodeInfo.isAlive then return nodeName end end -- 按顺序分配 local firstNode = nil for k, v in pairs(root.mapCluster[clusterName]) do -- 首个节点 if firstNode == nil then firstNode = v.nodeName end -- 该节点当前负载 local grains = util_node:node_load_get_grains(k) if grains < v.weight * 10 then log.info( "user_dispatch_node_by_grain clusterName[%s] uid[%s] nodeName[%s]", tostring(clusterName), tostring(uid), tostring(v.nodeName) ) return v.nodeName end end -- 找不到节点,返回首个节点 log.info( "user_dispatch_node_by_grain clusterName[%s] uid[%s] firstNode[%s]", tostring(clusterName), tostring(uid), tostring(firstNode) ) return firstNode end function root.onStart() root.start_timer_heart_beat() end baseService.start(root, ".srvNodeMgr", true)