- 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唤醒协程,开始执行回调函数。