注意:该文章暂且只分析skynet框架的lua层,底层待后续分析
首先知道 启动一个服务 必须调用 skynet.start函数 且服务间的调度必须通过消息的形式才能驱动
现在的代码都可在skynet.lua 中可以找到
//源码如下
function skynet.start(start_func)
c.callback(skynet.dispatch_message)
skynet.timeout(0, function()
skynet.init_service(start_func)
end)
end
该函数先注册了消息回调函数 skynet.dispatch_message
然后调用skynet.timeout
//timeout 实现
function skynet.timeout(ti, func)
local session = c.intcommand("TIMEOUT",ti)
assert(session)
local co = co_create(func) //创建完成
assert(session_id_coroutine[session] == nil)
session_id_coroutine[session] = co //绑定关系,后续raw_dispatch_message会用到
end
c.intcommand(“TIMEOUT”,ti) 向底层注册一个超时时间,此时 ti = 0,这个定时服务交由Timer线程处理
然后co_create(func) 创建一个协程
//co_create 实现
local function co_create(f)
local co = table.remove(coroutine_pool)
if co == nil then
co = coroutine_create(function(...)
f(...)
local args = {...}
skynet.error("co_create 128")
for k,v in ipairs(args) do
print(k,v)
end
while true do
local session = session_coroutine_id[co]
if session and session ~= 0 then
local source = debug.getinfo(f,"S")
skynet.error(string.format("Maybe forgot response session %s from %s : %s:%d",
session,
skynet.address(session_coroutine_address[co]),
source.source, source.linedefined))
end
-- coroutine exit
local tag = session_coroutine_tracetag[co]
if tag ~= nil then
if tag then c.trace(tag, "end") end
session_coroutine_tracetag[co] = nil
end
local address = session_coroutine_address[co]
if address then
release_watching(address)
session_coroutine_id[co] = nil
end
-- recycle co into pool
f = nil
coroutine_pool[#coroutine_pool+1] = co
-- recv new main function f
f = coroutine_yield "SUSPEND"
f(coroutine_yield())
end
end)
else
-- pass the main function f to coroutine, and restore running thread
local running = running_thread
coroutine_resume(co, f)
running_thread = running
end
return co
end
当服务第一次启动时,coroutine_pool中是没有可用的协程的
timeout函数执行完后,Timer线程会给服务发送一个消息加到该服务的邮箱中,然后该消息会被放入到全局邮箱队列,等待工作线程调度,当工作线程调度是, 会调用该服务注册的消息处理函数skynet.dispatch_message (上面有提到)
//dispatch_message 实现
function skynet.dispatch_message(...)
local succ, err = pcall(raw_dispatch_message,...)
while true do
local key,co = next(fork_queue)
if co == nil then
break
end
fork_queue[key] = nil
local fork_succ, fork_err = pcall(suspend,co,coroutine_resume(co))
if not fork_succ then
if succ then
succ = false
err = tostring(fork_err)
else
err = tostring(err) .. "\n" .. tostring(fork_err)
end
end
end
assert(succ, tostring(err))
end
该函数会先调用 raw_dispatch_message函数
//raw_dispatch_message 实现
local function raw_dispatch_message(prototype, msg, sz, session, source)
//此时底层Timer线程给服务发送的消息类型prototype = 1 msg = nil sz = 0
-- skynet.PTYPE_RESPONSE = 1, read skynet.h
if prototype == 1 then
local co = session_id_coroutine[session] //取到对应的协程
if co == "BREAK" then
session_id_coroutine[session] = nil
elseif co == nil then
unknown_response(session, source, msg, sz)
else
local tag = session_coroutine_tracetag[co]
if tag then c.trace(tag, "resume") end
session_id_coroutine[session] = nil
suspend(co, coroutine_resume(co, true, msg, sz)) //唤醒协程co 传入参数true, msg, sz
end
else
local p = proto[prototype]
if p == nil then
if prototype == skynet.PTYPE_TRACE then
-- trace next request
trace_source[source] = c.tostring(msg,sz)
elseif session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, prototype)
end
return
end
local f = p.dispatch --是否注册消息处理函数
if f then
local ref = watching_service[source]
if ref then
watching_service[source] = ref + 1
else
watching_service[source] = 1
end
local co = co_create(f)
session_coroutine_id[co] = session
session_coroutine_address[co] = source
local traceflag = p.trace
if traceflag == false then
-- force off
trace_source[source] = nil
session_coroutine_tracetag[co] = false
else
local tag = trace_source[source]
if tag then
trace_source[source] = nil
c.trace(tag, "request")
session_coroutine_tracetag[co] = tag
elseif traceflag then
-- set running_thread for trace
running_thread = co
skynet.trace()
end
end
suspend(co, coroutine_resume(co, session,source, p.unpack(msg,sz))) --解包数据
else
trace_source[source] = nil
if session ~= 0 then
c.send(source, skynet.PTYPE_ERROR, session, "")
else
unknown_request(session, source, msg, sz, proto[prototype].name)
end
end
end
end
唤醒co时,会调用对应的function
然后往下调用到suspend 参数为co,true,SUSPEND
suspend(co, coroutine_resume(co, session,source, p.unpack(msg,sz)))
-- suspend is local function
function suspend(co, result, command, param, param2)
skynet.error(skynet.address(skynet.self()), result, command, param, param2)
if not result then
local session = session_coroutine_id[co]
if session then -- coroutine may fork by others (session is nil)
local addr = session_coroutine_address[co]
if session ~= 0 then
-- only call response error
local tag = session_coroutine_tracetag[co]
if tag then c.trace(tag, "error") end
c.send(addr, skynet.PTYPE_ERROR, session, "")
end
session_coroutine_id[co] = nil
session_coroutine_address[co] = nil
session_coroutine_tracetag[co] = nil
end
error(debug.traceback(co,tostring(command)))
end
if command == "SUSPEND" then --由传入的参数可知 会跑到这里
dispatch_wakeup() --检测wakeup_queue列表,唤醒对应co 后续讲到 这里wakeup_queue为空
dispatch_error_queue()
elseif command == "QUIT" then
-- service exit
return
elseif command == "USER" then
-- See skynet.coutine for detail
error("Call skynet.coroutine.yield out of skynet.coroutine.resume\n" .. debug.traceback(co))
elseif command == nil then
skynet.error("co:",co)
-- debug trace
return
else
error("Unknown command : " .. command .. "\n" .. debug.traceback(co))
end
end
流程是:调用skynet.start ------ 注册消息回调 skynet.dispatch_message -----调用skynet.timeout -----创建协程 ------ Timer线程消息回调 ------ 调用skynet.dispatch_message ------调用 raw_dispatch_message 唤醒线程------调用服务绑定的function – 调用suspend 处理其他逻辑