本章主要讲解:skynet的组播服务是怎么发生的,如何运用。
组播这个过程主要涉及了以下几个服务。
group_mgr,group_agent,multicast,tunnel,localcast。
group_mgr是每个服务器集群只有一个(即多个进程共有一个)。
group_agent是每个harbor(即一个进程)只有一个。
multicast。每次组播过程会生成两个multicast服务,一个服务用来发送消息(接下来称其为send服务),一个服务用来接受来自其他harbor的消息(接下来称其为recv服务)。这两个服务中,组员大体相同,不过,send服务比recv服务多一些成员,send组中有额外的tunnel服务成员。图解:
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
开始读代码。在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。