From 3fe4471e6afc014e465adffd5babec56b7379cbb Mon Sep 17 00:00:00 2001 From: zhouhaihai Date: Sat, 31 Aug 2019 15:57:02 +0800 Subject: [PATCH] 热更新 demo --- src/actions/HttpAction.lua | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/agent.lua | 28 +++++++++++++++++++++++++--- src/main.lua | 3 +++ src/services/agent_ctrl.lua | 13 +++++++++++++ src/services/csvdatad.lua | 27 +++++++++++++++++++++++++++ src/services/httpweb.lua | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/services/ngxd.lua | 17 +++++++++++++++++ src/services/watchdog.lua | 11 +++++++++++ 8 files changed, 251 insertions(+), 3 deletions(-) create mode 100644 src/actions/HttpAction.lua create mode 100644 src/services/httpweb.lua diff --git a/src/actions/HttpAction.lua b/src/actions/HttpAction.lua new file mode 100644 index 0000000..5cfcba9 --- /dev/null +++ b/src/actions/HttpAction.lua @@ -0,0 +1,80 @@ + +local skynet = require "skynet" +local sharedata = require "skynet.sharedata" +local codecache = require "skynet.codecache" -- 清空缓存用 + +local _M = {} + +-- 清空代码和csvdata 缓存 (无论什么热更新 都要先更新服务器 然后调用这个 api 再进行更新) +function _M.clearcache(query, body) + skynet.error(string.format("clearcache time: %s", skynet.timex())) + codecache.clear() + return 'success' +end + +--重新加载 csvdata -- 需要先调用 clearcache +function _M.reload_csvdata(query, body) + skynet.error(string.format("reload_csvdata time: %s", skynet.timex())) + local status = skynet.call('CSVDATA', "lua", "reload") + if status == "ok" then + -- 所有需要 更新的服务 都更新一下数据 + pcall(skynet.call, 'WATCHDOG', "lua", "reloadCsvData") + pcall(skynet.call, 'NGXD', "lua", "reloadCsvdb") + end + return 'success' +end + +--post 需要更改的 globalCsv json +function _M.global_csv(query, body) + if not body or body == "" then return end + local change = json.decode(body) -- json 表 + if not next(change) then return end + skynet.error(string.format("global_csv change time: %s, value: %s", skynet.timex(), body)) + -- 更新所有需要更新globalDefine 的地方 + pcall(skynet.call, 'WATCHDOG', "lua", "reloadCsvData", change) + pcall(skynet.call, 'NGXD', "lua", "reloadCsvdb", change) -- 他也要改 + + return 'success' +end + + +-- 热更新代码 -- 针对 agent 执行发送过来的代码 -- 代码要规范~ + +--[=[ eg: +1. *Action 重新加载 (action 每次用才require 直接清掉重新加载) (某些) + body = "package.loaded['actions.*Action']=nil" + +2. 修改 global 方法 和 变量 直接覆盖 + body = """ + function a() + print(123) + end + """ + +3. 修改 role 方法 + + body = """ + local role = ... + if role then + role.getItemCount = function(self) + return "hehe" + end + end + """ +]=] + +function _M.hotfix(query, body) + if not body or body == "" then + return + end + skynet.error(string.format("hotfix time: %s, code: %s", skynet.timex(), body)) + local ok = pcall(load, body) + if not ok then return end + pcall(skynet.call, 'WATCHDOG', "lua", "hotfix", body) + return 'success' +end + + + + +return _M \ No newline at end of file diff --git a/src/agent.lua b/src/agent.lua index 28b95b3..7dd7c0c 100644 --- a/src/agent.lua +++ b/src/agent.lua @@ -16,7 +16,6 @@ skynet = require "skynet" redisproxy = require "shared.redisproxy" datacenter = require "skynet.datacenter" mcast_util = require "services/mcast_util" -globalCsv = require "csvdata/GlobalDefine" local CMD = {} local agentInfo = {} -- { client_fd, role, gate_serv, open_timer} @@ -237,6 +236,11 @@ function CMD.start(session, source, gate, fd, ip) agent_util:reset() math.randomInit() + + -- 这里加载配表 不然热更新对某些 agent 无效 + csvdb = sharedata.query("csvdata") + globalCsv = require "csvdata/GlobalDefine" + -- 这里将消息伪装成 watchdog 发出,这样就由 A->B->C->B->A 变成 A->B->C->A skynet.redirect(gate, source, "lua", session, skynet.pack("forward", fd, 0, skynet.self())) end @@ -270,6 +274,26 @@ function CMD:usubUnion() agentInfo.userv = nil end +function CMD.reloadCsvdb(globalData) + if globalData then + for k, v in pairs(globalData) do + globalCsv[k] = v + end + else + csvdb = sharedata.query("csvdata") + end +end + +function CMD.hotfix(code) + local ok, func = pcall(load, code) + if ok then + ok = pcall(func, agentInfo.role) + end + if not ok then + skynet.error("hotfix error by code " .. code) + end +end + local function routeGM(cmd, params) if type(params) ~= "table" or not agentInfo.role then return "指令失败" @@ -304,8 +328,6 @@ skynet.start(function() cs = queue() - -- csv - csvdb = sharedata.query("csvdata") -- 错误码特殊处理 -- todo -- for key, value in pairs(csvdb["sys_codesCsv"]) do diff --git a/src/main.lua b/src/main.lua index d72eed9..cffa0ff 100644 --- a/src/main.lua +++ b/src/main.lua @@ -8,11 +8,14 @@ skynet.start(function() skynet.newservice("debug_console", 10001) local ngxd = skynet.newservice("services/ngxd", 11001) + local httpd = skynet.newservice("services/httpweb", 8001) local watchdog = skynet.newservice("services/watchdog", max_client) + skynet.call(watchdog, "lua", "start", { port = 12001, maxclient = max_client, ngxd = ngxd, + httpd = httpd, redishost = "127.0.0.1", redisport = 6100, diff --git a/src/services/agent_ctrl.lua b/src/services/agent_ctrl.lua index d22e679..9412c69 100644 --- a/src/services/agent_ctrl.lua +++ b/src/services/agent_ctrl.lua @@ -132,6 +132,19 @@ function _M:check_agent_status() end end +function _M:reload_csvdata(globalData) + for uid, pack in pairs(self.u2f) do + local agent = get_a(pack) + pcall(skynet.send, agent, "lua", "reloadCsvdb", globalData) + end +end +function _M:hotfix(code) + for uid, pack in pairs(self.u2f) do + local agent = get_a(pack) + pcall(skynet.send, agent, "lua", "hotfix", code) + end +end + local function query_agent_response(fd, response) local head = string.pack("H", actionCodes.Role_queryLoginRpc + rpcResponseBegin) diff --git a/src/services/csvdatad.lua b/src/services/csvdatad.lua index 6ef8575..2841835 100644 --- a/src/services/csvdatad.lua +++ b/src/services/csvdatad.lua @@ -57,8 +57,35 @@ local function handle_timeout() skynet.timeout(100*5, handle_timeout) end +-- 重新加载csvdb + + +local CMD = {} +function CMD.reload() + -- 重新加载 csvdata + csvdb = {} + for k, v in pairs(package.loaded) do + if k:find("csvdata") then + package.loaded[k] = nil + end + end + require("csvdata.init") + require("csvdata.init_adv") + + sharedata.update("csvdata", csvdb) + skynet.sleep(1) -- 睡一觉再返回 + return 'ok' +end + skynet.start(function () travCsv("src/csvdata", file2timeMap) sharedata.new("csvdata", csvdb) -- handle_timeout() + + skynet.dispatch("lua", function(_, _, command, ...) + local f = CMD[command] + skynet.ret(skynet.pack(f(...))) + end) + + skynet.register "CSVDATA" end) diff --git a/src/services/httpweb.lua b/src/services/httpweb.lua new file mode 100644 index 0000000..12e19d7 --- /dev/null +++ b/src/services/httpweb.lua @@ -0,0 +1,75 @@ +local skynet = require "skynet" +local socket = require "skynet.socket" +local httpd = require "http.httpd" +local sockethelper = require "http.sockethelper" +local urllib = require "http.url" +require "shared.init" +require "utils.init" + +local table = table +local string = string + +local port, watchdog = ... +port = tonumber(port) + +local key = "zhaolu1234dangge" +local function response(id, ...) + local ok, err = httpd.write_response(sockethelper.writefunc(id), ...) + if not ok then + -- if err == sockethelper.socket_error , that means socket closed. + skynet.error(string.format("fd = %d, %s", id, err)) + end +end + + +local CMD = require "actions.HttpAction" + +local function start() + local listen_socket = socket.listen("0.0.0.0", port) + print("Listen web port " .. port) + socket.start(listen_socket , function(id, addr) + socket.start(id) + local code, url, method, header, body = httpd.read_request(sockethelper.readfunc(id), 8192) + if code then + if code ~= 200 then + response(id, code) + else + local path, query = urllib.parse(url) + local cmd = path:match("([^/]+)") + if not CMD[cmd] or not query then + response(id, 404) + socket.close(id) + return + end + local query = urllib.parse_query(query) + if query.key ~= key then + response(id, 404) + socket.close(id) + return + end + local content = CMD[cmd](query, body) + if not content then + code = 404 + end + response(id, code, content) + end + else + if url == sockethelper.socket_error then + skynet.error("socket closed") + else + skynet.error(url) + end + end + socket.close(id) + end) +end + +local function __init__() + skynet.dispatch("lua", function(_,_, command, ...) + if command == "start" then + skynet.ret(skynet.pack(start(...))) + end + end) +end + +skynet.start(__init__) \ No newline at end of file diff --git a/src/services/ngxd.lua b/src/services/ngxd.lua index d3e36f4..9a52901 100644 --- a/src/services/ngxd.lua +++ b/src/services/ngxd.lua @@ -93,12 +93,29 @@ local function start() end end +local CMD = {} +function CMD.reloadCsvdb(globalData) + if globalData then + for k, v in pairs(globalData) do + globalCsv[k] = v + end + else + csvdb = sharedata.query("csvdata") + end +end + + local function __init__() skynet.dispatch("lua", function(_,_, command, ...) if command == "start" then skynet.ret(skynet.pack(start(...))) + else + local f = CMD[command] + skynet.ret(skynet.pack(f(...))) end end) + + skynet.register "NGXD" end skynet.start(__init__) diff --git a/src/services/watchdog.lua b/src/services/watchdog.lua index a4cb406..bafb8e6 100644 --- a/src/services/watchdog.lua +++ b/src/services/watchdog.lua @@ -1,4 +1,5 @@ local skynet = require "skynet" +require "skynet.manager" local redisproxy = require "shared.redisproxy" local socket = require "skynet.socket" local netpack = require "skynet.netpack" @@ -72,6 +73,7 @@ function CMD.start(conf) create_mutilcast() skynet.call(conf.ngxd, "lua", "start") + skynet.call(conf.httpd, "lua", "start") -- roomServer = skynet.newservice("services/roomServer") -- skynet.call(roomServer, "lua", "start") @@ -87,6 +89,14 @@ function CMD.forceClose(fd) agent_ctrl:exit_agent(fd) end +function CMD.reloadCsvData(globalData) + agent_ctrl:reload_csvdata(globalData) +end + +function CMD.hotfix(code) + agent_ctrl:hotfix(code) +end + skynet.start(function() skynet.dispatch("lua", function(session, source, cmd, subcmd, ...) if cmd == "socket" then @@ -98,6 +108,7 @@ skynet.start(function() skynet.ret(skynet.pack(f(subcmd, ...))) end end) + skynet.register "WATCHDOG" -- 数据库服务 redisd = skynet.newservice("services/redisd") -- libgit2 0.21.2