Skynet 入門筆記(2):Service 消息收發
編寫第一個 service 成功了,接下來考慮多個 service 之間如何通信的問題。
skynet 是單進程多線程框架,每個 lua service 獨立運行在自己的 lua vm 裏,採用 actor 併發模型。
這篇博客要實現的是讓一個 service 發送消息,另一個 service 處理消息。
actor 併發模型
Actor模型又稱爲參與者模型,基本理念是每個併發的線程(或者進程、服務等其他併發的東西)都是一個參與者,參與者之間互相發送消息,決定如何迴應消息或者啓動更多參與者。
用人話來說,就是參與者給另一個參與者發送消息,然後繼續幹自己的事情;收到消息的參與者決定要怎麼處理消息:做點什麼,創建新參與者,或者拋棄這個消息。
skynet 啓動新 service
依照前文配置 skynet
修改project/service/main.lua
。
-- main.lua
local skynet = require "skynet"
skynet.start(function()
-- print("Hello world")
skynet.newservice "myservice/worker"
skynet.newservice "myservice/msg_dispatcher"
skynet.exit()
end)
主要修改的地方是加入了兩行skynet.newservice
api參考文檔在這裏
確實挺簡陋的,就一句話。
newservice(name, ...) 啓動一個名爲 name 的新服務。
name
參數注意用相對於project/service
的路徑即可,不用加lua
的後綴名。
消息發送者
在project/service
裏創建目錄myservice
,然後在裏面創建新文件msg_dispatcher.lua
-- msg_dispatcher.lua
local skynet = require "skynet"
function sendmsg()
skynet.send("worker", "lua", "say", "Hello world!")
skynet.timeout(100, sendmsg)
end
skynet.start(function()
skynet.timeout(100, sendmsg)
end)
其中 function skynet.timeout(ti, func)
是定時器,第一個參數是時間,單位是 1/100 秒,第二個參數是回調函數。
然後是function skynet.send(addr, type, ...)
,第一個addr
是服務的地址,這個地址可以是服務的32位整數識別id,也可以是字符串別名;別名需要該服務自行註冊才能使用。第二個type
參數是消息類型,常用的就是lua
雖然我也不知道爲啥常用但大家都這麼用。後面的變長參數一般來說第一個約定是命令類型,再往後是命令參數,看下面的處理就知道了。
消息處理者
在project/service/myservice
裏創建文件worker.lua
,然後這麼寫。
-- work.lua
local CMD = {}
CMD.say = function (text)
print(text)
end
skynet.start(function()
skynet.register("worker")
skynet.dispatch("lua", function(session, source, cmd, ...)
print("[worker] received `"..cmd.."`")
local f = CMD[cmd]
end)
end)
幾個api的介紹如下。
register(name)
給當前服務起一個字符串名。dispatch(type, func)
爲 type 類型的消息設定一個處理函數。
回調函數的簽名是這樣:
function (session, source, ...)
其中dispatch
的回調函數前兩個參數分別是這樣解釋。
session
是爲了確保能將消息處理結果對應到某條消息,就像是服務器收到同一個客戶端的2條網絡請求,一條請求js一條請求css,如果誰先處理完誰就返回的話,可能css的請求就收到了js的內容。source
是消息的來源,是一個整數id。
後面的 cmd 參數其實也屬於變長參數的一部分,但是一般約定變長參數第一個參數是服務要執行的命令 (RPC的感覺),所以就給了個 cmd 的名字。變長參數剩餘的部分就是處理函數的參數了。
當然用其他處理方法也沒問題,只是常用模式。
最終結果
[:00000001] LAUNCH logger
[:00000002] LAUNCH snlua bootstrap
[:00000003] LAUNCH snlua launcher
[:00000004] LAUNCH snlua cdummy
[:00000005] LAUNCH harbor 0 4
[:00000006] LAUNCH snlua datacenterd
[:00000007] LAUNCH snlua service_mgr
[:00000008] LAUNCH snlua main
[:00000009] LAUNCH snlua myservice/msg_dispatcher
[:0000000a] LAUNCH snlua myservice/worker
[:00000008] KILL self
[:00000002] KILL self
[room_mgr] received `say` from `10`
Hello world!
[room_mgr] received `say` from `10`
Hello world!
[room_mgr] received `say` from `10`
Hello world!