Commit 3fe4471e6afc014e465adffd5babec56b7379cbb
1 parent
db8e475e
热更新 demo
Showing
8 changed files
with
251 additions
and
3 deletions
Show diff stats
| @@ -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 |
src/agent.lua
| @@ -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 |
src/main.lua
| @@ -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) |
| @@ -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 |