跨服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