skynet笔记

1、skynet.newservice和skynet.uniqueservice的区别

skynet.newservice:

  • 当调用skynet.newservice时,它会每次都创建一个新的服务实例,即使之前已经存在相同类型的服务实例。
  • 这意味着可以同时启动多个相同类型的服务实例,它们之间是相互独立的。
  • local service1 = skynet.newservice("my_service")
    local service2 = skynet.newservice("my_service")
    

 skynet.uniqueservice:

  • 调用skynet.uniqueservice时,它会检查是否已经存在相同类型的服务实例。如果已经存在,则返回已存在的服务实例的句柄,而不会启动新的实例。
  • 这确保了同一类型的服务只有一个实例在运行。
  • local service1 = skynet.uniqueservice("my_service")
    local service2 = skynet.uniqueservice("my_service") -- 返回与service1相同的句柄
    

2、skynet.core.pack(...)接口的作用

该函数是用于将一组 Lua 值打包成一个二进制字符串的接口。这个接口在底层用于消息的序列化和打包,通常在网络传输、进程间通信等场景中使用。

具体来说,pack 函数接受一组 Lua 值作为参数,并返回一个包含了这些值的二进制字符串。这个字符串可以通过网络或其他方式传递给其他服务或系统组件。

例如:

local c = require "skynet.core"

local data = c.pack(42, "hello", {1, 2, 3})

-- 将 data 发送给其他服务,用于消息传递或通信

在上述例子中,pack 将整数 42、字符串 "hello" 和一个包含数字的数组打包成一个二进制字符串,并将其存储在变量 data 中。

在 Skynet 中,这样的打包操作常常用于将消息序列化,以便通过网络或消息队列传递给其他 Skynet 服务。反之,unpack 函数用于将这样的二进制字符串反序列化为 Lua 值。

请注意,packunpack 通常是 Skynet 框架内部使用的底层接口。在一般的 Skynet 服务代码中,您更可能会使用高级的接口,如 skynet.sendskynet.call,它们会自动处理消息的序列化和反序列化。

3、协程的状态

在Skynet框架中,协程状态的概念通常与消息处理和异步操作密切相关。Skynet使用协程的方式实现了一种事件驱动的异步编程模型。除了"SUSPEND"状态之外,Skynet还定义了其他一些状态,它们对应于协程在不同的事件和阶段的处理。

以下是一些常见的协程状态:

  1. "SUSPEND":表示协程被挂起,通常是因为它正在等待某个事件的发生或在执行异步操作。在这个状态下,协程通过coroutine.yield("SUSPEND")挂起,等待外部事件或其他协程的唤醒。

  2. "RUNNING":表示协程正在运行中,即处于活动状态。协程会在处理消息或执行其他任务时处于这个状态。通常,协程在运行中时不会被挂起。

  3. "DEAD":表示协程已经终止或结束。协程在执行完任务后,或者在发生错误时可能会进入这个状态。已经结束的协程不再可用。

这些状态反映了协程在消息处理过程中的生命周期。在Skynet框架中,通过coroutine.yield传递不同的状态,可以实现协程的挂起和恢复,以及在不同的状态下执行相应的处理逻辑。状态的具体用途可能因具体的业务逻辑而有所不同。

4、local c = require "skynet.core";c.callback();这个callback是干嘛用的

在 Skynet 框架中,c.callback() 是用于注册一个 C 函数(Callback Function)到 Skynet 的底层事件循环中的回调队列中。这样,当 Skynet 的事件循环需要执行回调函数时,它就会调用注册的 C 函数。

通常情况下,c.callback() 会在 C 模块的初始化部分调用,用于注册一些与底层事件循环相关的回调函数。这些回调函数可能用于处理网络事件、定时器、IO 等底层的异步操作。

这个函数的具体用途和效果可能依赖于具体的 Skynet 模块或 C 拓展,因为 Skynet 的底层是使用 C 实现的,并且提供了一些 C API 来与 Lua 交互。在 Skynet 的源码或相关文档中,可能会有详细的说明和用法示例。

总的来说,c.callback() 的作用是将一个 C 函数注册为 Skynet 事件循环的回调,以便在事件发生时执行这个 C 函数。如果您想了解具体的用法和效果,建议查阅 Skynet 的源码或文档。

5、启动一个子服务,具体要经历哪些步骤?

下面以一个wsgate为例

  1. 在etc文件夹下面找到config文件,里面的start = "main"说明你的程序的入口是main.lua
  2. 在main.lua中输入以下代码,启动一个叫wsgate的服务(当然前提是你要有wsgate.lua文件)
    local gate = skynet.uniqueservice("wsgate")  -- 启动一个服务器实例,并返回句柄

     3. 接着进入uniqueservice接口,发现里面是调用skynet.call

function skynet.uniqueservice(global, ...)
	if global == true then
		return assert(skynet.call(".service", "lua", "GLAUNCH", ...))
	else
		return assert(skynet.call(".service", "lua", "LAUNCH", global, ...))  -- 调用.call的时候,会把“LAUNCH”以及后面的数据打包发到底层C语言中去处理,C里面通过LAUNCH映射到cmd_launch
	end
end

     4.进入skynet.call,第一个if是判断要不要打印,这个无伤大雅先不管他;然后再去调用auxsend这个函数和下面的挂起协程

function skynet.call(addr, typename, ...)
	local tag = session_coroutine_tracetag[running_thread]
	if tag then
		c.trace(tag, "call", 2)
		c.send(addr, skynet.PTYPE_TRACE, 0, tag)
	end

	local p = proto[typename]
	local session = auxsend(addr, p.id , p.pack(...))  -- auxsend接口:调用c.cend接口发送消息(其次做了会话冲突处理)
	if session == nil then
		error("call to invalid address " .. skynet.address(addr))
	end
	return p.unpack(yield_call(addr, session))   -- 挂起协程
end

    5.现在去看下auxsend这个函数,这段代码比较长

  1. dangerzone 是一个用于标记处于 "danger zone" 区域的集合,这个区域是一个会随着会话(session)递增而重新设置的区域。这里的 "danger zone" 是为了防止会话的递增与之前的某些会话发生冲突。
  2. set_checkrewindset_checkconflict 是用于设置 auxsendauxtimeout 的辅助函数,根据当前的会话范围选择合适的函数。
  3. checkconflict 函数用于检查会话是否存在冲突,即下一个会话是否已经存在。如果存在冲突,将会进行相应的处理,可能会调整 auxsendauxtimeout 的实现,以确保不发生会话冲突。
  4. auxsend_checkconflictauxtimeout_checkconflict 以及 auxsend_checkrewindauxtimeout_checkrewind 是根据当前会话范围选择的具体的消息发送和超时设置的函数。它们会在发送消息或设置超时时检查是否存在冲突,如果存在冲突,则会进行相应的处理。
  5. 初始时,auxsendauxtimeout 被设置为 auxsend_checkrewindauxtimeout_checkrewind,表示在最初的 "safe zone" 区域中。

        总体来说,这段代码的目的是为了管理会话(session)的递增,并根据当前会话范围的不同来选择适当的消息发送和超时设置的实现。这有助于防止会话冲突,并在需要时进行相应的处理。

然后auxsend会被设置成auxsend_checkconflict或者auxsend_checkrewind,在这两个接口中调用csend接口调用skynet.core底层中的send函数。

TODO:至于为什么要做会话冲突处理,我的理解是怕每个会话的地址重复了,这个理解的还不是很到位,以后理解了再做补充。还有就是send到C语言那边是怎么做处理的,这个以后有空再补充

do ---- avoid session rewind conflict
	local csend = c.send
	local cintcommand = c.intcommand
	local dangerzone
	local dangerzone_size = 0x1000
	local dangerzone_low = 0x70000000
	local dangerzone_up	= dangerzone_low + dangerzone_size

	local set_checkrewind	-- set auxsend and auxtimeout for safezone
	local set_checkconflict -- set auxsend and auxtimeout for dangerzone

	local function reset_dangerzone(session)
		dangerzone_up = session
		dangerzone_low = session
		dangerzone = { [session] = true }
		for s in pairs(session_id_coroutine) do
			if s < dangerzone_low then
				dangerzone_low = s
			elseif s > dangerzone_up then
				dangerzone_up = s
			end
			dangerzone[s] = true
		end
		dangerzone_low = dangerzone_low - dangerzone_size
	end

	-- in dangerzone, we should check if the next session already exist.
	-- 检查会话是否存在冲突,即下一个会话是否已经存在。如果存在冲突,将会进行相应的处理,可能会调整 auxsend 和 auxtimeout 的实现,以确保不发生会话冲突。
	local function checkconflict(session)
		if session == nil then
			return
		end
		local next_session = session + 1
		if next_session > dangerzone_up then
			-- leave dangerzone
			reset_dangerzone(session)
			assert(next_session > dangerzone_up)
			set_checkrewind()
		else
			while true do
				if not dangerzone[next_session] then
					break
				end
				if not session_id_coroutine[next_session] then
					reset_dangerzone(session)
					break
				end
				-- skip the session already exist.
				next_session = c.genid() + 1
			end
		end
		-- session will rewind after 0x7fffffff
		if next_session == 0x80000000 and dangerzone[1] then
			assert(c.genid() == 1)
			return checkconflict(1)
		end
	end

	local function auxsend_checkconflict(addr, proto, msg, sz)
		local session = csend(addr, proto, nil, msg, sz)
		checkconflict(session)
		return session
	end

	local function auxtimeout_checkconflict(timeout)
		local session = cintcommand("TIMEOUT", timeout)
		checkconflict(session)
		return session
	end

	local function auxsend_checkrewind(addr, proto, msg, sz)
		local session = csend(addr, proto, nil, msg, sz)
		if session and session > dangerzone_low and session <= dangerzone_up then
			-- enter dangerzone
			set_checkconflict(session)
		end
		return session
	end

	local function auxtimeout_checkrewind(timeout)
		local session = cintcommand("TIMEOUT", timeout)
		if session and session > dangerzone_low and session <= dangerzone_up then
			-- enter dangerzone
			set_checkconflict(session)
		end
		return session
	end

	set_checkrewind = function()
		auxsend = auxsend_checkrewind
		auxtimeout = auxtimeout_checkrewind
	end

	set_checkconflict = function(session)
		reset_dangerzone(session)
		auxsend = auxsend_checkconflict
		auxtimeout = auxtimeout_checkconflict
	end

	-- in safezone at the beginning
	-- 默认是将auxsend设置成auxsend_checkrewind
	set_checkrewind()
end

        6.yield_call里面主要是将这个协程挂起,等待

local function yield_call(service, session)
	watching_session[session] = service
	session_id_coroutine[session] = running_thread
	local succ, msg, sz = coroutine_yield "SUSPEND"
	watching_session[session] = nil
	if not succ then
		error "call failed"
	end
	return msg,sz
end

6、客户端和服务端通信具体过程

  1. 启动sh和配置文件略过
  2. wsclient启动,创建一个协程去运行console_main_loop,这个函数是用来捕捉命令行中的命令,然后去调用这个CMD中的函数。(这边的作用只是为了捕捉命令以及调用该命令所对应的函数)
    local function console_main_loop()
    	local stdin = socket.stdin()
    	while true do
    		local cmdline = socket.readline(stdin, "\n")
            local cmd, args = cmdline:match("^%s*([^%s]+)%s(.*)")
            LOG("CMD:", cmd)
            LOG("args:", args)
            if cmd == "pm" then
                CMD[cmd](args)
            else
                local split = split_cmdline(cmdline)
                LOG("split:", split)
                cmd = split[1]
                local f = CMD[cmd]
                if f then
                    if cmd == "login" or status == 2 then
                        f(table.unpack(split, 2))
                    else
                        LOG("请先登录再执行该命令")
                    end
                else
                    LOG("unknown cmd")
                end
                inc_seq()
            end
    	end
    end
    
    skynet.start(function ()
        skynet.fork(console_main_loop)
    end)

4、todo:服务端

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值