skynet skynet.start函数分析

注意:该文章暂且只分析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
skynet skynet.start函数分析_第1张图片然后往下调用到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 处理其他逻辑

你可能感兴趣的:(skynet框架分析)