跳到主要内容

服务

服务作为Actor的载体,是线程调度的最小单元。暂时只有Lua类型的服务, Lua Service用一个LuaVM表示,它们可以独占或者共享线程。不同Lua Service之间不能直接访问,它们只能通过消息通信来交互。服务主要分为唯一服务普通服务。moon中创建服务的API是moon.new_service(service_conf)

---@class service_conf
---@field name string 表示服务的名称.
---@field file string 表示服务的启动脚本文件路径. 注意此路径是相对于工作目录的, 不会受到luapath影响。
---@field unique? boolean 表示服务是否为唯一服务, 是一个可选的布尔型变量, 默认值为 false。如果设置为 true, 则可以使用 `moon.queryservice(name)` 函数查询服务的 ID
---@field threadid? integer 表示服务运行的工作线程ID [1-N], 是一个可选的整数型变量,默认值为 0。如果设置为非零值, 则会在指定的工作线程中创建服务, 并且把改线程设置为非共享。否则服务会被添加到当前具有最少服务数的工作线程中。

--- 创建一个服务
---@async
---@param config service_conf @创建服务的配置, 除了基本配置, 也可以用来传递额外的参数到新创建的服务中。
---@return integer @ 返回创建的服务ID, 如果ID为0则表示服务创建失败。
moon.new_service(config)

获取当前服务的ID

    moon.id  

获取当前服务的name

    moon.name

根据名字查询唯一服务的ID

    local id = moon.queryservice("unique_service_name")

服务主动退出

    moon.quit()

强制某个服务退出

    moon.kill(id)

bootstrap服务

bootstrap服务是Node启动时自动创建第一个服务,它有一些特点:

  • bootstrap服务会在threadid=1的线程中创建(可以用来监控当前Node的状态,此时指定其它独占线的程服务时,尽量不要指定1号线程)
  • bootstrap服务不是唯一服务, 方便编写工具脚本时自动退出进程
  • 通常用来创建和管理其它唯一服务, 和控制Node进程的退出流程。
  • 如果bootstrap服务退出了(初始化失败或者调用moon.quit()主动退出),则Node进程也会退出。
  • 作为当前Node最后退出的服务, 可以用来保存一些全局共享的只读数据,如加载 lua-protobuf 协议文件

创建一个简单服务

test.lua

local moon = require("moon")
print("hello world")

moon.quit() --退出

运行./moon test.lua

2023-06-08 16:46:22.970 | 11336     | INFO  | INIT with 4 workers.
2023-06-08 16:46:22.971 | 42660 | INFO | WORKER-1 START
2023-06-08 16:46:22.972 | 39104 | INFO | WORKER-2 START
2023-06-08 16:46:22.972 | 27764 | INFO | WORKER-3 START
2023-06-08 16:46:22.972 | 11500 | INFO | WORKER-4 START
2023-06-08 16:46:22.979 | :01000001 | INFO | [WORKER 1] new service [bootstrap]
2023-06-08 16:46:22.984 | :01000001 | INFO | hello world (test.lua:2)
2023-06-08 16:46:22.985 | :01000001 | INFO | [WORKER 1] destroy service [bootstrap]
2023-06-08 16:46:22.996 | 11500 | INFO | WORKER-4 STOP
2023-06-08 16:46:22.996 | 27764 | INFO | WORKER-3 STOP
2023-06-08 16:46:22.996 | 39104 | INFO | WORKER-2 STOP
2023-06-08 16:46:22.996 | 42660 | INFO | WORKER-1 STOP
2023-06-08 16:46:22.996 | 11336 | INFO | STOP

唯一服务

游戏服务器经常会有一些运行期常驻的服务,如 Gate, Center, WorldMap, DB proxy。这些服务拥有共同的特点:

  • 长时间存活,直到进程关闭
  • 经常被其它服务访问
  • 是当前节点的重要组件,缺少它们,一般在游戏逻辑上会产生异常行为
  • 它们之间有一定的依赖规则,需要制定启动和关闭顺序

这些服务就可以用唯一服务来编写,moon中的唯一服务有如下特点

  • 唯一服务通常非常重要,如果启动失败,所在的节点就不应该继续运行。
  • 名字唯一, 可以使用moon.queryservice(name)查询到服务ID, 方便被其他服务访问。
  • 使用者需要控制唯一服务的退出,方便在进程正常关闭时,制定多个唯一服务的退出顺序。如数据库连接服务应该最后退出。
  • 可以独占线程提高处理和响应能力。

创建唯一服务

moon.async(function()
local id = moon.new_service({
name = "worldmap", -- 注意名字不能和其它唯一服务重复
file = "worldmap.lua",
unique = true --唯一服务标识
--threadid = 2, --可选: 独占id为2的线程
})
assert(id>0,"create service failed")
end)

查询唯一服务

local id = moon.queryservice("worldmap")
assert(id>0, "queryservice worldmap failed")
moon.send("lua", id, arg1, arg2, arg3, ...)

唯一服务退出

  • 优雅退出

注册一个消息处理函数,进程关闭时,bootstrap 服务向其它唯一服务,按逻辑顺序,发送消息。

--- 唯一服务
function CMD.Shutdown()
-- 处理一些退出前的操作
moon.quit()
return true
end
--- bootstrap 服务
assert(moon.call(unqiue_service_id1, "Shutdown"))
assert(moon.call(unqiue_service_id2, "Shutdown"))
assert(moon.call(unqiue_service_id3, "Shutdown"))
  • 自动退出
---进程正常关闭时会调用此函数
moon.shutdown(function()
--- do something
moon.quit()
end)
  • 强制退出
--other service
moon.kill(id)

普通服务

拆分是提高并发处理能力的关键手段,有些游戏服务是需要在运行时动态创建和删除的,他们多个实例之间通常没有关联,为了利用多核能力提高服务器负载,可以用普通服务表示。如玩家个人数据和逻辑,副本场景,Moba游戏的房间。普通服务不能使用moon.queryservice查询到服务ID。它的创建和删除通常是与游戏逻辑相关的,需要使用者(通常是一个唯一服务器)自己保存。

RoomManager服务:

local roomid = 0
local rooms = {} ---保存房间ID和服务ID的映射

moon.async(function()
for i=1,100 do
local addr = moon.new_service({
name = "room",
file = "room.lua",
})

roomid = roomid + 1
rooms[roomid] = addr ---自己保存映射关系
end
end)

注意普通服务,在收到到进程关闭信号时会直接退出,这样做的好处是,在编写某些命令行脚本时能简化一些代码。如果需要控制普通服务的退出,可以覆盖掉默认退出逻辑,来手动管理

moon.shutdown(function ()
--do nothing
end)

--然后在管理服务中给它发送消息,优雅的关闭
function CMD.Shutdown()
-- 处理一些退出前的操作
moon.quit()
return true
end

创建服务时可以传递传递额外的参数

moon.async(function()
local id = moon.new_service({
name = "worldmap",
file = "worldmap.lua",
unique = true,
threadid = 2, --独占id为2的线程
test_arg1 = 1001,
test_arg2 = "hello",
test_arg3 = {a=1,b=2,c=3}
})
assert(id>0,"create service failed")
end)

worldmap.lua

local moon = require("moon")
local conf = ...
print_r(conf) --打印上面传递的服务配置