- lua服務都是通過snlua啓動的,snlua服務啓動時,最終會通過skynet.start把回調函數設爲skynet.dispatch_message。
- 當有消息到達時,先調用到
skynet.dispatch_message
, 這裏面再調到raw_dispatch_message
local function raw_dispatch_message(prototype, msg, sz, session, source, ...)
if prototype == skynet.PTYPE_RESPONSE 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
session_id_coroutine[session] = nil
suspend(co, coroutine.resume(co, true, msg, sz))
end
else
local p = proto[prototype]
if p == nil then
if 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
suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...)))
else
unknown_request(session, source, msg, sz, proto[prototype].name)
end
end
end
這個函數會先判斷消息類型是否是Response類型的(向別的服務發起一次RPC調用後,別的服務返回的消息)。
Response類型的消息
如果是Respone,則直接從session_id_coroutine
表內根據session取出co(協程), 這裏面的值是在發起RPC調用前把co緩存進去的,具體位置在:
function suspend(co, result, command, param, size)
...
if command == "CALL" then
session_id_coroutine[param] = co
...
end
取到co後,調用coroutine.resume(co, true, msg, sz)
喚醒協程。
其他消息
如果不是Respone,則走正常的消息響應流程。
首先去proto表內找下有沒有這個協議類型,proto表是調用skynet.register_protocol註冊的,默認會註冊lua
,response
,error
三種類型的協議:
-- skynet.lua line 562
----- register protocol
do
local REG = skynet.register_protocol
REG {
name = "lua",
id = skynet.PTYPE_LUA,
pack = skynet.pack,
unpack = skynet.unpack,
}
REG {
name = "response",
id = skynet.PTYPE_RESPONSE,
}
REG {
name = "error",
id = skynet.PTYPE_ERROR,
unpack = function(...) return ... end,
dispatch = _error_dispatch,
}
end
如果沒找到相應的協議類型,判斷下session是否等於0,如果不等於0就給源服務回一個error, 否則調用unknown_request。
如果有對應的協議,則取出其dispatch函數(這個函數是調用skynet.dispatch函數註冊的,也可以在register_protocol時直接指定)。
取出dispatch函數後,這個函數就是真正要執行的消息回調函數了,但不是直接執行的,而是創建一個協程去執行,看下面的代碼:
local co = co_create(f)
session_coroutine_id[co] = session
session_coroutine_address[co] = source
suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...)))
這個co_create
函數,可以簡單理解成coroutine.create
, (其實它裏面維護了一個協程池,詳細說明)
創建完協程後,把session和source存起來,然後調用resume喚醒協程,開始執行回調函數。