5-bootstrap服务的启动
新入门skynet系列视频b站网址 https://www.bilibili.com/video/BV19d4y1678X
bootstrap服务的启动
bootstrap实际上也是一个snlua服务。他是我们skynet启动时创建的第二个服务。
void
skynet_start(struct skynet_config * config) {
//首先创建日志服务
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
skynet_handle_namehandle(skynet_context_handle(ctx), "logger");//给日志服务绑定一个名字
//创建bootstrp服务 注意这里服务并没有完成所有流程 因为工作线程还没有启动 服务的第一个消息只是push进队列了
bootstrap(ctx, config->bootstrap);//"snlua bootstrap"
start(config->thread);
//other code
}
行8就是启动一个bootstrap服务,继续看
static void
bootstrap(struct skynet_context * logger, const char * cmdline) {
//name是 snlua
//arge是 bootstrap
struct skynet_context *ctx = skynet_context_new(name, args);
}
我们直接开始看bootstrap.lua的执行
local skynet = require "skynet"
local harbor = require "skynet.harbor"
local service = require "skynet.service"
require "skynet.manager" -- import skynet.launch, ...
skynet.start(function()
local standalone = skynet.getenv "standalone"
--`skynet.launch(servicename, ...)` 用于启动一个 C 模块的服务。主要意思是 第一个参数是 c 模块的名字 可以是snlua logger 这种
local launcher = assert(skynet.launch("snlua","launcher"))--snlua的创建分为两步 1.push一个启动消息给自己 2.处理启动消息并执行对应lua文件 属于异步
skynet.name(".launcher", launcher)
local harbor_id = tonumber(skynet.getenv "harbor" or 0)
if harbor_id == 0 then --单节点模式
assert(standalone == nil)
standalone = true
skynet.setenv("standalone", "true") --注意这里设置为ture
--skynet.newservice 是启动一个snlua服务,第一个参数是服务对应的lua文件的名字 会阻塞当前协程
local ok, slave = pcall(skynet.newservice, "cdummy")
skynet.name(".cslave", slave)
else -- 非单节点模式
-- 略
end
if standalone then
local datacenter = skynet.newservice "datacenterd"
skynet.name("DATACENTER", datacenter)
end
skynet.newservice "service_mgr"
pcall(skynet.newservice,skynet.getenv "start" or "main") --最终启动我们配置的main服务
skynet.exit() --最后自己功成身退
end)
这里我们说一下 单节点模式
和 多节点模式
。
-
如果我们的harbor_id配置为0 或者不配置,那么表示我们的skynet运行在单节点模式。也就是skynet节点都是独立的。如果我再启动一个skynet节点,同时也是单节点模式,那么这两个节点相对对方来说,都是外部网络。就像一个玩家主动连接我们的skynet节点一样,我们把玩家当作是外部网络。
-
如果我们harbor_id配置为大于0的数字,那么一般会启动多个skynet节点,同时每个skynet配置的harbor_id是不同的。这就是说明,这些skynet节点形成了一个网络.当然这种集群模式现在skynet不推荐了。
而我们的课程只讨论单节点模式下的skynet.
接下来我们再一次分析bootstrap服务入口函数的执行过程,直到bootstrap服务自己退出
skynet.exit发送了一个消息给launcher,当然launcher没有任何反应。因为 bootstrap不是通过 skynet.newservice创建的。所以launcher里面是查不到bootstrap服务的,所以不会做进一步处理。
之后skynet.exit通知底层,然后挂起了定时器协程,即coroutine_yield "QUIT"
,当然也没机会发送启动ok的消息给launcher了。
之后suspend通过这个"QUIT"判断服务要退出,所以把当前运行的定时器协程关闭,同时return,表示 skynet.dispatch_message函数处理完成。
接下来我们简单的看看底层是如何退出服务的。
因为在通知底层退出的时候,我们其实正在bootstrap服务的回调函数中,也即是ctx是被引用的。所以ctx还不会被马上释放。当我们的skynet.dispach_message结束的时候,ctx就没有引用了,此时服务队列会被放进全局队列,之后服务会被标记释放。当工作线程下次取出队列,得到handle,发现通过handle无法找到ctx的时候,就会开始真正释放服务相关的内容了。