本章主要講解: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
首先,我們把當前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。