Commit 3fe4471e6afc014e465adffd5babec56b7379cbb

Authored by zhouhaihai
1 parent db8e475e

热更新 demo

src/actions/HttpAction.lua 0 → 100644
@@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
  1 +
  2 +local skynet = require "skynet"
  3 +local sharedata = require "skynet.sharedata"
  4 +local codecache = require "skynet.codecache" -- 清空缓存用
  5 +
  6 +local _M = {}
  7 +
  8 +-- 清空代码和csvdata 缓存 (无论什么热更新 都要先更新服务器 然后调用这个 api 再进行更新)
  9 +function _M.clearcache(query, body)
  10 + skynet.error(string.format("clearcache time: %s", skynet.timex()))
  11 + codecache.clear()
  12 + return 'success'
  13 +end
  14 +
  15 +--重新加载 csvdata -- 需要先调用 clearcache
  16 +function _M.reload_csvdata(query, body)
  17 + skynet.error(string.format("reload_csvdata time: %s", skynet.timex()))
  18 + local status = skynet.call('CSVDATA', "lua", "reload")
  19 + if status == "ok" then
  20 + -- 所有需要 更新的服务 都更新一下数据
  21 + pcall(skynet.call, 'WATCHDOG', "lua", "reloadCsvData")
  22 + pcall(skynet.call, 'NGXD', "lua", "reloadCsvdb")
  23 + end
  24 + return 'success'
  25 +end
  26 +
  27 +--post 需要更改的 globalCsv json
  28 +function _M.global_csv(query, body)
  29 + if not body or body == "" then return end
  30 + local change = json.decode(body) -- json 表
  31 + if not next(change) then return end
  32 + skynet.error(string.format("global_csv change time: %s, value: %s", skynet.timex(), body))
  33 + -- 更新所有需要更新globalDefine 的地方
  34 + pcall(skynet.call, 'WATCHDOG', "lua", "reloadCsvData", change)
  35 + pcall(skynet.call, 'NGXD', "lua", "reloadCsvdb", change) -- 他也要改
  36 +
  37 + return 'success'
  38 +end
  39 +
  40 +
  41 +-- 热更新代码 -- 针对 agent 执行发送过来的代码 -- 代码要规范~
  42 +
  43 +--[=[ eg:
  44 +1. *Action 重新加载 (action 每次用才require 直接清掉重新加载) (某些)
  45 + body = "package.loaded['actions.*Action']=nil"
  46 +
  47 +2. 修改 global 方法 和 变量 直接覆盖
  48 + body = """
  49 + function a()
  50 + print(123)
  51 + end
  52 + """
  53 +
  54 +3. 修改 role 方法
  55 +
  56 + body = """
  57 + local role = ...
  58 + if role then
  59 + role.getItemCount = function(self)
  60 + return "hehe"
  61 + end
  62 + end
  63 + """
  64 +]=]
  65 +
  66 +function _M.hotfix(query, body)
  67 + if not body or body == "" then
  68 + return
  69 + end
  70 + skynet.error(string.format("hotfix time: %s, code: %s", skynet.timex(), body))
  71 + local ok = pcall(load, body)
  72 + if not ok then return end
  73 + pcall(skynet.call, 'WATCHDOG', "lua", "hotfix", body)
  74 + return 'success'
  75 +end
  76 +
  77 +
  78 +
  79 +
  80 +return _M
0 \ No newline at end of file 81 \ No newline at end of file
@@ -16,7 +16,6 @@ skynet = require "skynet" @@ -16,7 +16,6 @@ skynet = require "skynet"
16 redisproxy = require "shared.redisproxy" 16 redisproxy = require "shared.redisproxy"
17 datacenter = require "skynet.datacenter" 17 datacenter = require "skynet.datacenter"
18 mcast_util = require "services/mcast_util" 18 mcast_util = require "services/mcast_util"
19 -globalCsv = require "csvdata/GlobalDefine"  
20 19
21 local CMD = {} 20 local CMD = {}
22 local agentInfo = {} -- { client_fd, role, gate_serv, open_timer} 21 local agentInfo = {} -- { client_fd, role, gate_serv, open_timer}
@@ -237,6 +236,11 @@ function CMD.start(session, source, gate, fd, ip) @@ -237,6 +236,11 @@ function CMD.start(session, source, gate, fd, ip)
237 agent_util:reset() 236 agent_util:reset()
238 math.randomInit() 237 math.randomInit()
239 238
  239 +
  240 + -- 这里加载配表 不然热更新对某些 agent 无效
  241 + csvdb = sharedata.query("csvdata")
  242 + globalCsv = require "csvdata/GlobalDefine"
  243 +
240 -- 这里将消息伪装成 watchdog 发出,这样就由 A->B->C->B->A 变成 A->B->C->A 244 -- 这里将消息伪装成 watchdog 发出,这样就由 A->B->C->B->A 变成 A->B->C->A
241 skynet.redirect(gate, source, "lua", session, skynet.pack("forward", fd, 0, skynet.self())) 245 skynet.redirect(gate, source, "lua", session, skynet.pack("forward", fd, 0, skynet.self()))
242 end 246 end
@@ -270,6 +274,26 @@ function CMD:usubUnion() @@ -270,6 +274,26 @@ function CMD:usubUnion()
270 agentInfo.userv = nil 274 agentInfo.userv = nil
271 end 275 end
272 276
  277 +function CMD.reloadCsvdb(globalData)
  278 + if globalData then
  279 + for k, v in pairs(globalData) do
  280 + globalCsv[k] = v
  281 + end
  282 + else
  283 + csvdb = sharedata.query("csvdata")
  284 + end
  285 +end
  286 +
  287 +function CMD.hotfix(code)
  288 + local ok, func = pcall(load, code)
  289 + if ok then
  290 + ok = pcall(func, agentInfo.role)
  291 + end
  292 + if not ok then
  293 + skynet.error("hotfix error by code " .. code)
  294 + end
  295 +end
  296 +
273 local function routeGM(cmd, params) 297 local function routeGM(cmd, params)
274 if type(params) ~= "table" or not agentInfo.role then 298 if type(params) ~= "table" or not agentInfo.role then
275 return "指令失败" 299 return "指令失败"
@@ -304,8 +328,6 @@ skynet.start(function() @@ -304,8 +328,6 @@ skynet.start(function()
304 328
305 cs = queue() 329 cs = queue()
306 330
307 - -- csv  
308 - csvdb = sharedata.query("csvdata")  
309 -- 错误码特殊处理 331 -- 错误码特殊处理
310 -- todo 332 -- todo
311 -- for key, value in pairs(csvdb["sys_codesCsv"]) do 333 -- for key, value in pairs(csvdb["sys_codesCsv"]) do
@@ -8,11 +8,14 @@ skynet.start(function() @@ -8,11 +8,14 @@ skynet.start(function()
8 skynet.newservice("debug_console", 10001) 8 skynet.newservice("debug_console", 10001)
9 9
10 local ngxd = skynet.newservice("services/ngxd", 11001) 10 local ngxd = skynet.newservice("services/ngxd", 11001)
  11 + local httpd = skynet.newservice("services/httpweb", 8001)
11 local watchdog = skynet.newservice("services/watchdog", max_client) 12 local watchdog = skynet.newservice("services/watchdog", max_client)
  13 +
12 skynet.call(watchdog, "lua", "start", { 14 skynet.call(watchdog, "lua", "start", {
13 port = 12001, 15 port = 12001,
14 maxclient = max_client, 16 maxclient = max_client,
15 ngxd = ngxd, 17 ngxd = ngxd,
  18 + httpd = httpd,
16 19
17 redishost = "127.0.0.1", 20 redishost = "127.0.0.1",
18 redisport = 6100, 21 redisport = 6100,
src/services/agent_ctrl.lua
@@ -132,6 +132,19 @@ function _M:check_agent_status() @@ -132,6 +132,19 @@ function _M:check_agent_status()
132 end 132 end
133 end 133 end
134 134
  135 +function _M:reload_csvdata(globalData)
  136 + for uid, pack in pairs(self.u2f) do
  137 + local agent = get_a(pack)
  138 + pcall(skynet.send, agent, "lua", "reloadCsvdb", globalData)
  139 + end
  140 +end
  141 +function _M:hotfix(code)
  142 + for uid, pack in pairs(self.u2f) do
  143 + local agent = get_a(pack)
  144 + pcall(skynet.send, agent, "lua", "hotfix", code)
  145 + end
  146 +end
  147 +
135 local function query_agent_response(fd, response) 148 local function query_agent_response(fd, response)
136 local head = string.pack("H", actionCodes.Role_queryLoginRpc + rpcResponseBegin) 149 local head = string.pack("H", actionCodes.Role_queryLoginRpc + rpcResponseBegin)
137 150
src/services/csvdatad.lua
@@ -57,8 +57,35 @@ local function handle_timeout() @@ -57,8 +57,35 @@ local function handle_timeout()
57 skynet.timeout(100*5, handle_timeout) 57 skynet.timeout(100*5, handle_timeout)
58 end 58 end
59 59
  60 +-- 重新加载csvdb
  61 +
  62 +
  63 +local CMD = {}
  64 +function CMD.reload()
  65 + -- 重新加载 csvdata
  66 + csvdb = {}
  67 + for k, v in pairs(package.loaded) do
  68 + if k:find("csvdata") then
  69 + package.loaded[k] = nil
  70 + end
  71 + end
  72 + require("csvdata.init")
  73 + require("csvdata.init_adv")
  74 +
  75 + sharedata.update("csvdata", csvdb)
  76 + skynet.sleep(1) -- 睡一觉再返回
  77 + return 'ok'
  78 +end
  79 +
60 skynet.start(function () 80 skynet.start(function ()
61 travCsv("src/csvdata", file2timeMap) 81 travCsv("src/csvdata", file2timeMap)
62 sharedata.new("csvdata", csvdb) 82 sharedata.new("csvdata", csvdb)
63 -- handle_timeout() 83 -- handle_timeout()
  84 +
  85 + skynet.dispatch("lua", function(_, _, command, ...)
  86 + local f = CMD[command]
  87 + skynet.ret(skynet.pack(f(...)))
  88 + end)
  89 +
  90 + skynet.register "CSVDATA"
64 end) 91 end)
src/services/httpweb.lua 0 → 100644
@@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
  1 +local skynet = require "skynet"
  2 +local socket = require "skynet.socket"
  3 +local httpd = require "http.httpd"
  4 +local sockethelper = require "http.sockethelper"
  5 +local urllib = require "http.url"
  6 +require "shared.init"
  7 +require "utils.init"
  8 +
  9 +local table = table
  10 +local string = string
  11 +
  12 +local port, watchdog = ...
  13 +port = tonumber(port)
  14 +
  15 +local key = "zhaolu1234dangge"
  16 +local function response(id, ...)
  17 + local ok, err = httpd.write_response(sockethelper.writefunc(id), ...)
  18 + if not ok then
  19 + -- if err == sockethelper.socket_error , that means socket closed.
  20 + skynet.error(string.format("fd = %d, %s", id, err))
  21 + end
  22 +end
  23 +
  24 +
  25 +local CMD = require "actions.HttpAction"
  26 +
  27 +local function start()
  28 + local listen_socket = socket.listen("0.0.0.0", port)
  29 + print("Listen web port " .. port)
  30 + socket.start(listen_socket , function(id, addr)
  31 + socket.start(id)
  32 + local code, url, method, header, body = httpd.read_request(sockethelper.readfunc(id), 8192)
  33 + if code then
  34 + if code ~= 200 then
  35 + response(id, code)
  36 + else
  37 + local path, query = urllib.parse(url)
  38 + local cmd = path:match("([^/]+)")
  39 + if not CMD[cmd] or not query then
  40 + response(id, 404)
  41 + socket.close(id)
  42 + return
  43 + end
  44 + local query = urllib.parse_query(query)
  45 + if query.key ~= key then
  46 + response(id, 404)
  47 + socket.close(id)
  48 + return
  49 + end
  50 + local content = CMD[cmd](query, body)
  51 + if not content then
  52 + code = 404
  53 + end
  54 + response(id, code, content)
  55 + end
  56 + else
  57 + if url == sockethelper.socket_error then
  58 + skynet.error("socket closed")
  59 + else
  60 + skynet.error(url)
  61 + end
  62 + end
  63 + socket.close(id)
  64 + end)
  65 +end
  66 +
  67 +local function __init__()
  68 + skynet.dispatch("lua", function(_,_, command, ...)
  69 + if command == "start" then
  70 + skynet.ret(skynet.pack(start(...)))
  71 + end
  72 + end)
  73 +end
  74 +
  75 +skynet.start(__init__)
0 \ No newline at end of file 76 \ No newline at end of file
src/services/ngxd.lua
@@ -93,12 +93,29 @@ local function start() @@ -93,12 +93,29 @@ local function start()
93 end 93 end
94 end 94 end
95 95
  96 +local CMD = {}
  97 +function CMD.reloadCsvdb(globalData)
  98 + if globalData then
  99 + for k, v in pairs(globalData) do
  100 + globalCsv[k] = v
  101 + end
  102 + else
  103 + csvdb = sharedata.query("csvdata")
  104 + end
  105 +end
  106 +
  107 +
96 local function __init__() 108 local function __init__()
97 skynet.dispatch("lua", function(_,_, command, ...) 109 skynet.dispatch("lua", function(_,_, command, ...)
98 if command == "start" then 110 if command == "start" then
99 skynet.ret(skynet.pack(start(...))) 111 skynet.ret(skynet.pack(start(...)))
  112 + else
  113 + local f = CMD[command]
  114 + skynet.ret(skynet.pack(f(...)))
100 end 115 end
101 end) 116 end)
  117 +
  118 + skynet.register "NGXD"
102 end 119 end
103 120
104 skynet.start(__init__) 121 skynet.start(__init__)
src/services/watchdog.lua
1 local skynet = require "skynet" 1 local skynet = require "skynet"
  2 +require "skynet.manager"
2 local redisproxy = require "shared.redisproxy" 3 local redisproxy = require "shared.redisproxy"
3 local socket = require "skynet.socket" 4 local socket = require "skynet.socket"
4 local netpack = require "skynet.netpack" 5 local netpack = require "skynet.netpack"
@@ -72,6 +73,7 @@ function CMD.start(conf) @@ -72,6 +73,7 @@ function CMD.start(conf)
72 create_mutilcast() 73 create_mutilcast()
73 74
74 skynet.call(conf.ngxd, "lua", "start") 75 skynet.call(conf.ngxd, "lua", "start")
  76 + skynet.call(conf.httpd, "lua", "start")
75 77
76 -- roomServer = skynet.newservice("services/roomServer") 78 -- roomServer = skynet.newservice("services/roomServer")
77 -- skynet.call(roomServer, "lua", "start") 79 -- skynet.call(roomServer, "lua", "start")
@@ -87,6 +89,14 @@ function CMD.forceClose(fd) @@ -87,6 +89,14 @@ function CMD.forceClose(fd)
87 agent_ctrl:exit_agent(fd) 89 agent_ctrl:exit_agent(fd)
88 end 90 end
89 91
  92 +function CMD.reloadCsvData(globalData)
  93 + agent_ctrl:reload_csvdata(globalData)
  94 +end
  95 +
  96 +function CMD.hotfix(code)
  97 + agent_ctrl:hotfix(code)
  98 +end
  99 +
90 skynet.start(function() 100 skynet.start(function()
91 skynet.dispatch("lua", function(session, source, cmd, subcmd, ...) 101 skynet.dispatch("lua", function(session, source, cmd, subcmd, ...)
92 if cmd == "socket" then 102 if cmd == "socket" then
@@ -98,6 +108,7 @@ skynet.start(function() @@ -98,6 +108,7 @@ skynet.start(function()
98 skynet.ret(skynet.pack(f(subcmd, ...))) 108 skynet.ret(skynet.pack(f(subcmd, ...)))
99 end 109 end
100 end) 110 end)
  111 + skynet.register "WATCHDOG"
101 -- 数据库服务 112 -- 数据库服务
102 redisd = skynet.newservice("services/redisd") 113 redisd = skynet.newservice("services/redisd")
103 114