跨服Lua调用
(金庆的专栏 2017.3)
跨服Lua调用是指服务器集群内部A服调用B服上的脚本。
服务器之间已经实现RPC调用,Lua调用是Rpc调用的简化方式。
示例:
-- Tell remote server svr_id that game_clt_id is disconnected.
local arguments = { "Remote.ClientDisconnected", game_clt_id }
remote_runner.run_mfa(svr_id, "event.dispatcher",
"dispatch", arguments)
以上例子相当于调用了其他服务器上的代码:
require("event.dispatcher").dispatch("Remote.ClientDisconnected", game_clt_id)
需要参数为:
* 远程服务器ID
* Lua模块名
* Lua模块中的函数名
* 调用参数列表
remote_runner这样实现:
-- 运行另一服务器上的Lua代码
-- 也支持本服运行(本服RPC调用)
local M = {}
local log = require("log"):new("remote_runner")
local pb = require("protobuf")
local serpent = require("serpent")
-- on_result(result) 生成 rpc 回调函数 cb(resp_str)
local function get_mfa_cb(on_result)
if (not on_result) then return nil end
assert("function" == type(on_result))
local cb = function(resp_str)
assert("string" == type(resp_str))
local resp = pb.decode("svr.RunLuaMfaResponse", resp_str)
local ok, copy = serpent.load(resp.returned_dump)
assert(ok, "Run mfa returns invalid value.")
log:debug("RunLuaMfaResponse: %s", serpent.line(copy))
on_result(table.unpack(copy)) -- 回调时执行
end -- cb
return cb
end -- get_mfa_cb()
-- Run module function with arguments on remote server.
-- 示例 rum_mfa(123, "event.dispatcher", "dispatch", {"EventName", 1,2,3}, nil)
function M.run_mfa(svr_id, module_name, function_name, arguments, on_result)
assert("number" == type(svr_id))
assert("string" == type(module_name))
assert("string" == type(function_name))
assert("table" == type(arguments))
assert(nil == on_result or "function" == type(on_result))
log:debug("Request to call Svr_%s %s.%s()", svr_id, module_name, function_name)
local req = {
module_name = module_name,
function_name = function_name,
arguments_dump = serpent.dump(arguments)
}
local req_str = pb.encode("svr.RunLuaMfaRequest", req)
local cb = get_mfa_cb(on_result)
c_rpc.request_svr(svr_id, "svr.RunLua", "RunMfa", req_str, cb)
end -- run()
return M
通过Rpc服务RunLua.RunMfa实现。run_lua.proto如下定义
syntax = "proto3";
package svr;
// 服务器内部跨服调用Lua
service RunLua {
// 运行 module.function(...arguments...)
rpc RunMfa(RunLuaMfaRequest) returns (RunLuaMfaResponse);
}
message RunLuaMfaRequest {
string module_name = 1;
string function_name = 2;
// arguments_dump = serpent.dump({1,2,3})
string arguments_dump = 3;
}
message RunLuaMfaResponse {
// Get returned table copy:
// local ok, copy = serpent.load(returned_dump)
string returned_dump = 1;
}
服务这样实现:
-- svc_run_lua.lua
-- Run lua by other servers.
local M = {}
local log = require("log"):new("svc_run_lua")
local pb = require("protobuf")
-- Run module.function(...arguments...)
function M.RunMfa(ctx, content)
local req = pb.decode("svr.RunLuaMfaRequest", content)
log:debug("RunMfa %s.%s", req.module_name, req.function_name) -- todo: from where?
local mod = require(req.module_name)
local fun = mod[req.function_name]
local ok, arguments = serpent.load(serpent.dump(req.arguments_dump))
assert(ok, "Illegal arguments.")
local result_table = table.pack(fun(table.unpack(arguments)))
local resp = { returned_dump = serpent.dump(result_table) }
local resp_str = pb.encode("svr.RunLuaMfaResponse", resp)
c_rpc.reply_to(ctx, resp_str)
end -- Run()
return M