新入门skynet系列视频b站网址 https://www.bilibili.com/video/BV19d4y1678X
# skynet.newservice创建snlua服务
之前讲 服务间请求和响应的时候,我们在main服务里启动了一个db服务。看代码
--main.lua
local skynet = require "skynet"
skynet.start(function()--lua服务的入口函数
local db = skynet.newservice("db")--启动一个db服务
local key = "zhangsan"
local age = skynet.call(db, "lua", "GET",key) --发送 lua类型 的请求给db服务,然后等待对方回应
skynet.exit()
end)
我们调用 skynet.newservice("db")
启动了一个db服务。当我们调用skynet.newservice的时候,当前协程会挂起。他会发送一个请求给 launcher服务,表示请启动一个db服务。launcher服务收到请求后,会启动创建db服务。当db服务启动完成后,会通知launcher服务,此时launcher服务才会发送响应消息给我们的main服务。这个时候,我们skynet.newservice调用才会返回。
关于launcher服务的创建,看这里 launcher的创建 .
我们具体看看 skynet.newservice("db")
function skynet.newservice(name, ...)
return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end
这里调用skynet.call 。注意参数 "LAUNCH", "snlua", “db”
。我们再看看launcher服务是怎么处理的。
--launcher.lua
function command.LAUNCH(_, service, ...)--收到服务main 请求创建一个服务db 的消息
launch_service(service, ...) --此时 service是 "snlua"
return NORET --注意这里 NORET 表示 当前协程不用返回响应给main服务
end
skynet.dispatch("lua", function(session, address, cmd , ...) --设置任务函数 f
cmd = string.upper(cmd)
local f = command[cmd] --此时cmd是 "LAUNCH"
if f then
local ret = f(address, ...)
if ret ~= NORET then
skynet.ret(skynet.pack(ret))
end
end
end)
继续看 launch_service 函数。实际上这个函数主要是调用了skynet.launch创建了模块为snlua的lua服务。因为skynet.launch函数的特点如下
- 在当前工作线程下调用 skynet_context_new 创建db服务,并给对应的队列push一个消息
- 在合适的时机,队列的消息被取出被处理,同时执行服务对应的lua文件
所以当前launcher服务只能等待。 当被创建的db服务的lua文件执行完成初始化后,也就是发送一个完成服务创建的通知给launcher时,launcher才会回应main服务。
local function launch_service(service, ...)
local param = table.concat({...}, " ")
local inst = skynet.launch(service, param)-- 此时service是 "snlua" param 是 "db"
local session = skynet.context()
local response = skynet.response() --这个返回一个闭包
if inst then --inst是db服务的地址
services[inst] = service .. " " .. param
instance[inst] = response
launch_session[inst] = session
else
response(false)
return
end
return inst
end
上面的代码实际上记录了请求者main服务的信息。现在假设db服务的db.lua文件执行完成了初始化。也就是db.lua的skynet.start注册的定时器触发了。还记得吗?这个定时器主要做了两件事 1. 执行启动函数 2.发送服务创建完成的通知给launcher服务。我们看看skynet.start函数。
--skynet.lua
function skynet.init_service(start)
local function main()
skynet_require.init_all()
start() --执行启动函数 start_func
end
local ok, err = xpcall(main, traceback)
if not ok then
--略
else
skynet.send(".launcher","lua", "LAUNCHOK")--任何服务在完成start后 都会发送 LAUNCHOK 消息给 launcher服务
end
end
function skynet.start(start_func)
c.callback(skynet.dispatch_message)
init_thread = skynet.timeout(0, function()
skynet.init_service(start_func)
init_thread = nil
end)
end
注意上面的 行10 代码。我们现在看看launcher收到这个 LAUNCHOK 消息的处理过程。
--每一个lua服务启动完成后(即start_func函数执行完毕 都会发送一个LAUNCHOK 消息给 launcher服务
function command.LAUNCHOK(address) --收到服务db send过来的消息 表示服务db自身已经完成创建
-- init notice
local response = instance[address] --address是db服务的地址
if response then
response(true, address) -- 发送响应给 之前的 main服务
instance[address] = nil
launch_session[address] = nil
end
return NORET
end
行4 通过db的地址找到了之前已经准备好的响应闭包。然后在 行6 执行这个闭包。这样launcher就算是真正响应main服务了,虽然这个响应是延迟的。我们可以具体看看这个 闭包。实际上在 行13回应了main服务。相当于告诉main服务,你要的db服务,我已经给你创建好了。
function skynet.response(pack)
pack = pack or skynet.pack
local co_session = assert(session_coroutine_id[running_thread], "no session")
session_coroutine_id[running_thread] = nil --这里不再保存session了 因为下面的response闭包已经保存session了
local co_address = session_coroutine_address[running_thread]
local function response(ok, ...)
local ret
if unresponse[response] then
if ok then
ret = c.send(co_address, skynet.PTYPE_RESPONSE, co_session, pack(...))
end
unresponse[response] = nil
ret = ret ~= nil
else
ret = false
end
pack = nil
return ret
end
unresponse[response] = co_address --表示当前服务 还欠 co_address 一个响应 ,此时co_address就是我们的main服务
return response
end
此时我们应该回到main服务调用 skynet.newservice("db")的地方了
function skynet.newservice(name, ...)
return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...)
end
此时调用skynet.call挂起的协程,重新被唤醒,并返回地址给main服务了。
标签:服务,--,db,newservice,snlua,launcher,skynet,end From: https://www.cnblogs.com/waittingforyou/p/16966134.html