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