上两篇skynet主体流程, skynet怎么启动lua文件介绍了skynet代码的主体流程,还有怎么启动lua逻辑.
我们知道skynet业务逻辑都是在lua里写的,这样大大提高了开发效率,而且使热更新成为可能,究竟lua层是怎么保证逻辑正常运行,一个服务在lua层的表现形式又是怎么样的呢?这篇将为您讲解.
一个服务在lua层的入口是skynet.start(),他的实现为:
function skynet.start(start_func)
c.callback(skynet.dispatch_message) --①
skynet.timeout(0, function() --②
skynet.init_service(start_func)
end)
end
①这里是在c接口里指定服务的lua层回调函数dispatch_message,上篇我们有讲到过.
②是通过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
end
① 调用c的timeout接口,他会产生一个session id
② 启动协程池,关联func,session Id
c接口的timeout的实现:
1 根据context产生一个该context递增的session id
2 如果参数time==0,产生一条PTYPE_RESPONSE类型的消息压入队列;如果不为0,则增加时间节点
由于上面的参数是0,则产生一条PTYPE_RESPONSE类型的消息,包含了session id
当消息队列捕获并执行该消息的时候,就会调用lua层的回调函数skynet.dispatch_message了,最终调用raw_dispatch_message.它的实现:
local function raw_dispatch_message(prototype, msg, sz, session, source, ...)
-- 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
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
timeout产生的消息执行时会走分支①,他的session关联了一个协程,他是在timeout中调用co_create()产生的. co_create()是为了复用协程而产生的,具体请参考这篇:lua协程池
找到session关联的协程后,②开始执行suspend(co, corutine.resume(co,...)),实际上是先执行了corutine.resume(co),启动协程,执行了协程里的函数,即timeout传入的函数.suspend函数用来对协程的调度和调配,为了突出主干,下篇再讲suspend函数.至此一个服务的入口函数就通skynet.start()调用起来了