skynet框架 源码分析 四



        本章主要讲解:skynet的组播服务是怎么发生的,如何运用。

        组播这个过程主要涉及了以下几个服务。
        group_mgr,group_agent,multicast,tunnel,localcast。

        group_mgr是每个服务器集群只有一个(即多个进程共有一个)。
        group_agent是每个harbor(即一个进程)只有一个。
        multicast。每次组播过程会生成两个multicast服务,一个服务用来发送消息(接下来称其为send服务),一个服务用来接受来自其他harbor的消息(接下来称其为recv服务)。这两个服务中,组员大体相同,不过,send服务比recv服务多一些成员,send组中有额外的tunnel服务成员。图解:

skynet框架 源码分析 四_第1张图片
        tunnel是一个记录别的harbor上recv服务的服务。例如,假如有一个组ID为10,在harbor1上有一个组员,在harbor2上有一个组员,那么会在harbor1上生成一个tunnel服务指向harbor2上的组ID为10的recv组服务。这样在harbor1上给组10发广播的时候,harbor2的成员也能收到消息。

        运行service文件夹下的testgroup.lua文件,启动一个测试组播的服务。让我们来读源码:

local skynet = require "skynet"
local group = require "mcgroup"

skynet.start(function()
	local gid = group.create()
	local gaddr = group.address(gid)
	local g = {}
	print("=== Create Group ===",gid,skynet.address(gaddr))
	for i=1,10 do
		local address = skynet.newservice("testgroup_c", tostring(i))
		table.insert(g, address)
		group.enter(gid , address)
	end
	skynet.cast(g,"text","Cast")
	skynet.sleep(1000)
	skynet.send(gaddr,"text","Hello World")
	skynet.exit()
end)

        首先,引入mcgroup库。mcgroup,库会启动全局组播服务group_mgr,和本harbor的group_agent服务。
        group.create会向group_mgr申请一个组号ID(此文假设其为A组)
        group.address是获取ID组的发送服务地址,在这里面会生成一个multicast服务,也就是send服务,并将send服务的handle赋值gaddr。
        for循环中,开启了10个组播模拟服务,这个我就不解释了。看一下group.enter在干什么。

function group.enter(id, handle)
	handle = handle or skynet.self()
	skynet.send(SERVICE, "lua" , "ENTER", handle, id)
end

        向全局group_mgr发送进入某个组的请求。也就是将生成的testgroup_c服务添加到A组的send服务中。不过在此过程中继续看group_mgr的源码。

-- 为某个harbor上创建接收服务,并将其添加到其他harbor上,
-- 同时将别的harbor上的接受该广播组的服务添加到本harbor上
local function create_group_in(harbor, id)	
	local group_agent = assert(harbor_ctrl[harbor])
	local g = multicast[id]
	g[harbor] = false	
	-- 在某个harbor上创建这个组的接受服务
	local receive_group_addr = skynet.call(group_agent, "lua", "CREATE", id)
	-- 向将别的组的接收地址注册到本组管理器中
	for _,mc in pairs(g) do
		if mc then			
			skynet.send(group_agent, "lua", "AGENT", id, mc)
		end
	end
	-- 向别的组发送当前接收组的地址
	for harbor_id, f_group_agent in pairs(harbor_ctrl) do
		if harbor_id ~= harbor then
			skynet.send(f_group_agent, "lua", "AGENT", id, receive_group_addr
		end
	end
	-- g跟组id值有关系,全都是各个harbor的接收组地址
	-- 一个组有一个g(multicast[id])表,g表中 harbor_id = 组(第id个组的地址)接收组地址
	g[harbor] = receive_group_addr	
end

function command.ENTER(address, harbor, id)
	local g = multicast[id]
	assert(g,id)
	if g[harbor] == nil then
		create_group_in(harbor, id)
	end
	local group_agent = assert(harbor_ctrl[harbor])

	skynet.send(group_agent, "lua", "ENTER", id, address)
end

        首先,我们把当前harbor命名为harbor1,把其他的harbor命名为harborN。

        开始读代码。在group_mgr的ENTER接口中,做了两件事情,第一件:将自己的recv服务通过另外一个的harbor(称其为harbor2)的group_agent,添加到harbor2上。在harbor2上是如何做到的呢。我们继续读代码,发现harbor2上的group_agent在收到消息后,会为A组生成一个并生成一个tunnel服务指向harbor1上的recv服务,而后将tunnel添加到A组关联的send服务,若在harbor2上没有send服务,就生成一个;第二件事:就是将当前的testgroup_c服务添加到本harbor的send服务和recv服务中。(这其中会有一个问题,加入harbor2上没有A组的成员,我觉得就没办法在harbor2上想A组发送广播消息。)

        在经过这以后,我们获得了两个组播服务,分别是recv服务,和send服务。而后,简单的看下一下面这句源码

skynet.send(gaddr,"text","Hello World")

        其实就是像send服务发送一条广播消息,hello world。









你可能感兴趣的:(多线程,服务器,分布式)