skynet lua服務callback執行過程

  1. lua服務都是通過snlua啓動的,snlua服務啓動時,最終會通過skynet.start把回調函數設爲skynet.dispatch_message。
  2. 當有消息到達時,先調用到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喚醒協程,開始執行回調函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章