srvNodeMgr.lua 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. --[[
  2. Author: neo
  3. Date: 2021-05-27 14:43:17
  4. LastEditTime: 2021-05-31 20:52:30
  5. LastEditors: Please set LastEditors
  6. Description: 游戏业务节点分配服务
  7. --]]
  8. local baseService = require("baseService")
  9. local nodeMgr = require("nodeMgr")
  10. local util_node = require("utils.util_node")
  11. local lib_game_redis = require("lib_game_redis")
  12. local root = {}
  13. -- match节点分配优化
  14. -- | 1. 玩家更集中
  15. -- | 2. 减少交互拥堵 - 如何判断拥堵
  16. -- TODO:心跳检查
  17. -- 开始心跳定时器
  18. function root.start_timer_heart_beat()
  19. root.timerHeatBeat =
  20. create_timeout(
  21. 500,
  22. function()
  23. root.timer_out_heart_beat()
  24. end
  25. )
  26. end
  27. function root.timer_out_heart_beat()
  28. root.timerHeatBeat.delete()
  29. root.timerHeatBeat = nil
  30. root.start_timer_heart_beat()
  31. if root.mapCluster == nil then
  32. return
  33. end
  34. local currTime = skynet_time()
  35. for k, v in pairs(root.mapCluster) do
  36. local isUpdate = false
  37. for _k, _v in pairs(v) do
  38. if _v.isAlive and currTime >= _v.updateTime + 20 then
  39. _v.isAlive = false
  40. log.warning("节点[%s]已失效", tostring(_v.nodeName))
  41. isUpdate = true
  42. end
  43. end
  44. if isUpdate then
  45. root.update_weight(k)
  46. end
  47. end
  48. end
  49. -- 节点心跳
  50. function root.node_heart_beat(nodeInfo)
  51. if is_empty(nodeInfo) or is_empty(nodeInfo.clusterName) or is_empty(nodeInfo.nodeName) then
  52. return false
  53. end
  54. local clusterName = nodeInfo.clusterName
  55. local nodeName = nodeInfo.nodeName
  56. if root.mapCluster == nil then
  57. root.mapCluster = {}
  58. end
  59. if root.mapCluster[clusterName] == nil then
  60. root.mapCluster[clusterName] = {}
  61. end
  62. nodeInfo.updateTime = skynet_time()
  63. nodeInfo.isAlive = true
  64. -- 是否更新权重
  65. local nInfo = root.get_cluster_node_info(clusterName, nodeName)
  66. -- 重新加入节点
  67. if nInfo == nil or not nInfo.isAlive then
  68. util_node:node_load_init_grains(nodeName)
  69. end
  70. local isUpdateWeight = false
  71. if nInfo == nil or not nInfo.isAlive or nInfo.weight ~= nodeInfo.weight then
  72. isUpdateWeight = true
  73. end
  74. root.mapCluster[clusterName][nodeName] = nodeInfo
  75. -- 更新权重
  76. if isUpdateWeight then
  77. root.update_weight(clusterName)
  78. end
  79. return true
  80. end
  81. -- 停止节点
  82. function root.node_stop(clusterName, nodeName)
  83. if is_empty(clusterName) or is_empty(nodeName) then
  84. return false
  85. end
  86. if root.mapCluster[clusterName] == nil then
  87. return false
  88. end
  89. if root.mapCluster[clusterName][nodeName] == nil then
  90. return false
  91. end
  92. root.mapCluster[clusterName][nodeName].isAlive = false
  93. -- 更新权重
  94. root.update_weight(clusterName)
  95. return true
  96. end
  97. -- 更新权重
  98. function root.update_weight(clusterName)
  99. if root.mapClusterWeight == nil then
  100. root.mapClusterWeight = {}
  101. end
  102. local weightInfo = {}
  103. weightInfo.total = 0
  104. weightInfo.list = {}
  105. for k, v in pairs(root.mapCluster[clusterName]) do
  106. if v.isAlive then
  107. local info = {}
  108. info.nodeName = v.nodeName
  109. info.min = weightInfo.total + 1
  110. weightInfo.total = weightInfo.total + (v.weight or 0)
  111. info.max = weightInfo.total
  112. table.insert(weightInfo.list, info)
  113. end
  114. end
  115. root.mapClusterWeight[clusterName] = weightInfo
  116. end
  117. -- 获取节点信息
  118. function root.get_cluster_node_info(clusterName, nodeName)
  119. if is_empty(clusterName) or is_empty(nodeName) then
  120. return
  121. end
  122. if root.mapCluster == nil or root.mapCluster[clusterName] == nil then
  123. return
  124. end
  125. return root.mapCluster[clusterName][nodeName]
  126. end
  127. -- 更新玩家分配节点
  128. function root.user_band_node(uid, clusterName, nodeName)
  129. if uid == nil then
  130. return false
  131. end
  132. if root.mapUid2NodeInfo == nil then
  133. root.mapUid2NodeInfo = {}
  134. end
  135. if root.mapUid2NodeInfo[uid] == nil then
  136. root.mapUid2NodeInfo[uid] = {}
  137. end
  138. root.mapUid2NodeInfo[uid][clusterName] = nodeName
  139. return true
  140. end
  141. -- 分配match节点
  142. function root.dispatch_match_node(uid, conf)
  143. -- TODO:根据match节点下匹配队列人数分配
  144. -- 本地管理匹配队列玩家列表?
  145. local clusterName = "match"
  146. -- 按照负载集中分配
  147. local nodeName = root.user_dispatch_node_by_grain(uid, clusterName)
  148. if is_empty(nodeName) then
  149. -- 按照权重分配
  150. nodeName = root.user_dispatch_node_by_weight(uid, clusterName)
  151. end
  152. if not is_empty(nodeName) then
  153. root.user_band_node(uid, clusterName, nodeName)
  154. return nodeName
  155. end
  156. end
  157. -- 分配战斗逻辑服节点
  158. function root.dispatch_battle_node(uid)
  159. -- 按照权重做负载均衡
  160. local clusterName = "battle"
  161. local nodeName = root.user_dispatch_node_by_weight(uid, clusterName)
  162. root.user_band_node(uid, clusterName, nodeName)
  163. if is_empty(nodeName) then
  164. return
  165. end
  166. -- 全服 - 战斗ID
  167. local battleId = lib_game_redis:hincrby("battle", "id", 1)
  168. if battleId >= 1000000000 then
  169. battleId = 1
  170. lib_game_redis:hset("battle", "id", battleId)
  171. end
  172. local ret = {}
  173. ret.battleNode = nodeName
  174. ret.battleId = battleId
  175. return ret
  176. end
  177. -- 获取当前战斗数
  178. function root.get_total_battle_count()
  179. if root.mapCluster == nil or root.mapCluster["match"] == nil then
  180. return 0
  181. end
  182. local count = 0
  183. for k, v in pairs(root.mapCluster["match"]) do
  184. local ok, battleCount = nodeMgr.call(v.nodeName, ".BattleMatch", "getBattleCount")
  185. if ok and battleCount then
  186. count = count + battleCount
  187. end
  188. end
  189. return count
  190. end
  191. -- 获取节点类型节点列表
  192. function root.get_cluster_node_list(clusterName)
  193. if is_empty(clusterName) then
  194. return
  195. end
  196. if root.mapCluster == nil or root.mapCluster[clusterName] == nil then
  197. return
  198. end
  199. local nodeList = {}
  200. for k, v in pairs(root.mapCluster[clusterName]) do
  201. if v.isAlive then
  202. table.insert(nodeList, v.nodeName)
  203. end
  204. end
  205. return nodeList
  206. end
  207. ----------------------------------------
  208. -- 分配玩家节点
  209. ----------------------------------------
  210. -- 按照节点权重
  211. function root.user_dispatch_node_by_weight(uid, clusterName)
  212. if is_empty(clusterName) then
  213. return
  214. end
  215. if root.mapCluster == nil then
  216. return
  217. end
  218. if root.mapCluster[clusterName] == nil then
  219. return
  220. end
  221. -- 已分配
  222. if uid and root.mapUid2NodeInfo and root.mapUid2NodeInfo[uid] and root.mapUid2NodeInfo[uid][clusterName] then
  223. local nodeName = root.mapUid2NodeInfo[uid][clusterName]
  224. local nodeInfo = root.get_cluster_node_info(clusterName, nodeName)
  225. if nodeInfo and nodeInfo.isAlive then
  226. return nodeName
  227. end
  228. end
  229. local rand = math.random(1, root.mapClusterWeight[clusterName].total)
  230. for k, v in ipairs(root.mapClusterWeight[clusterName].list) do
  231. if v.min <= rand and rand <= v.max then
  232. log.info("user_dispatch_node_by_weight uid[%s] nodeName[%s]", tostring(uid), tostring(v.nodeName))
  233. return v.nodeName
  234. end
  235. end
  236. end
  237. -- 根据负载
  238. function root.user_dispatch_node_by_grain(uid, clusterName)
  239. if is_empty(clusterName) or root.mapCluster == nil or root.mapCluster[clusterName] == nil then
  240. return
  241. end
  242. -- 已分配
  243. if uid and root.mapUid2NodeInfo and root.mapUid2NodeInfo[uid] and root.mapUid2NodeInfo[uid][clusterName] then
  244. local nodeName = root.mapUid2NodeInfo[uid][clusterName]
  245. local nodeInfo = root.get_cluster_node_info(clusterName, nodeName)
  246. if nodeInfo and nodeInfo.isAlive then
  247. return nodeName
  248. end
  249. end
  250. -- 按顺序分配
  251. local firstNode = nil
  252. for k, v in pairs(root.mapCluster[clusterName]) do
  253. -- 首个节点
  254. if firstNode == nil then
  255. firstNode = v.nodeName
  256. end
  257. -- 该节点当前负载
  258. local grains = util_node:node_load_get_grains(k)
  259. if grains < v.weight * 10 then
  260. log.info(
  261. "user_dispatch_node_by_grain clusterName[%s] uid[%s] nodeName[%s]",
  262. tostring(clusterName),
  263. tostring(uid),
  264. tostring(v.nodeName)
  265. )
  266. return v.nodeName
  267. end
  268. end
  269. -- 找不到节点,返回首个节点
  270. log.info(
  271. "user_dispatch_node_by_grain clusterName[%s] uid[%s] firstNode[%s]",
  272. tostring(clusterName),
  273. tostring(uid),
  274. tostring(firstNode)
  275. )
  276. return firstNode
  277. end
  278. function root.onStart()
  279. root.start_timer_heart_beat()
  280. end
  281. baseService.start(root, ".srvNodeMgr", true)