服务
服务作为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) --打印上面传递的服务配置