snax 是一個方便 skynet 服務實現的簡單框架。(簡單是相對於 skynet 的 api 而言)
使用 snax 服務先要在 Config 中配置 snax 用於路徑查找。每個 snax 服務都有一個用於啓動服務的名字,推薦按 lua 的模塊命名規則,但目前不推薦在服務名中包含"點" (在路徑搜索上尚未支持 . 與 / 的替換)。在啓動服務時會按查找路徑搜索對應的文件。
12.1 snax服務基礎API
啓動snax服務的API
local snax = require "snax"
snax.newservice(name, ...) --可以把一個服務啓動多份。傳入服務名和參數,它會返回一個對象,用於和這個啓動的服務交互。如果多次調用 newservice ,即使名字相同,也會生成多份服務的實例,它們各自獨立,由不同的對象區分。注意返回的不是服務地址,是一個對象。
snax.uniqueservice(name, ...) --和上面 api 類似,但在一個節點上只會啓動一份同名服務。如果你多次調用它,會返回相同的對象。
snax.globalservice(name, ...) --和上面的 api 類似,但在整個 skynet 網絡中(如果你啓動了多個節點),只會有一個同名服務。
查詢snax服務
snax.queryservice(name) --查詢當前節點的具名服務,返回一個服務對象。如果服務尚未啓動,那麼一直阻塞等待它啓動完畢。
snax.queryglobal(name) --查詢一個全局名字的服務,返回一個服務對象。如果服務尚未啓動,那麼一直阻塞等待它啓動完畢。
snax.self() --用來獲取自己這個服務對象,與skynet.self不同,它不是地址。
snax服務退出
snax.kill(obj, ...) --如果你想讓一個 snax 服務退出,調用
snax.exit(...) --退出當前服務,它等價於 snax.kill(snax.self(), ...) 。
通過snax服務地址獲取snax服務對象
對於匿名服務,你無法在別處通過名字得到和它交互的對象。如果你有這個需求,可以把對象的handle通過消息發送給別人。 handle 是一個數字,即 snax 服務的 skynet 服務地址。
--把handle轉換成服務對象。這裏第二個參數需要傳入服務的啓動名,以用來了解這個服務有哪些遠程方法可以供調用。當然,你也可以直接把 .type 域和 .handle 一起發送過去,而不必在源代碼上約定。
snax.bind(handle, typename)
snax啓動查找服務路徑是config.path的snax變量來指定
snax = root.."examples/?.lua;"..root.."test/?.lua;".."my_workspace/?.lua" --添加my_workspace路徑
12.2 最簡單snax服務
每個 snax 服務中都需要定義一個 init 函數,啓動這個服務會調用這個函數,並把啓動參數傳給它。
snax 服務還可以定義一個 exit 函數用於響應服務退出的事件,同樣能接收一些參數。
和標準的 skynet 服務不同,這些參數的類型不受限制,可以是 lua 的複雜數據類型。(而 skynet 服務受底層限制,只可以接受字符串參數)
寫一個最簡單的snax服務simplesnax.lua如下:
local skynet = require "skynet"
local snax = require "skynet.snax"
function init( ... ) --snax服務初始化時會調用該回調函數,可以獲取到啓動參數
skynet.error ("snax server start:", ...)
end
function exit(...) --snax服務初始化時會調用該回調函數,可以獲取到退出參數
skynet.error ("snax server exit:", ...)
end
snax不是普通服務,需要與snax框架配合使用,必須使用snax.newservice、snax.uniqueservice、snax.globalservice這個三個函數來啓動。
在console服務中啓動snax服務時,需要指定爲snax:
snax simplesnax nengzhong #輸入這一行 [:0100000a] LAUNCH snlua snaxd simplesnax [:0100000a] snax server start: nengzhong
12.3 snax服務請求
snax請求分爲無響應請求與有響應請求。
對snax服務發請求的方法
--無響應請求,obj是snax對象,post表示無響應請求,CMD具體的請求命令,...爲請求參數列表,發送完馬上返回
obj.post.CMD(...)
--有響應請求,obj是snax對象,req表示有響應請求,CMD具體的請求命令,...爲請求參數列表,發送完等待響應
obj.req.CMD(...)
12.3.1 snax處理無響應請求
修改simplesnax.lua:
local skynet = require "skynet"
local snax = require "skynet.snax"
function accept.hello(...) --通過obj.post.hello
skynet.error("hello", ...)
end
function accept.quit(...) --obj.post.quit來觸發回調函數
snax.exit(...)
--等同snax.kill(snax.self(), ...)
end
function init( ... )
skynet.error("snax server start:", ...)
end
function exit(...)
skynet.error("snax server exit:", ...)
end
編寫testsimplesnax.lua:
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.newservice("simplesnax", 123, "abc", false) --啓動simplessnax服務,並傳遞參數
skynet.error("snax service", obj, "startup")
local r = obj.post.hello(123, "abc", false) --調用simplesnax中的accept.hello方法
skynet.error("hello return:", r)
obj.post.quit("exit now") --退出服務
end)
運行結果:
testsimplesnax [:0100000a] LAUNCH snlua testsimplesnax [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: 123 abc false [:0100000a] snax service [simplesnax:100000b] startup [:0100000a] hello return: nil #post方法沒有返回值 [:0100000b] hello 123 abc false [:0100000b] snax server exit: exit now #服務退出的時候exit函數調用 [:0100000b] KILL self
12.3.2 處理有響應請求
修改simplesnax.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
function response.echo(str) --當其他服通過obj.req.echo調用的時候,觸發該回調函數,並返回應答
skynet.error("echo", str)
return str:upper()
end
function init( ... )
skynet.error("snax server start:", ...)
end
function exit(...)
skynet.error("snax server exit:", ...)
end
修改testsimplesnax.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.newservice("simplesnax", 123, "abc", false)
skynet.error("snax service", obj, "startup")
local r = obj.req.echo("nengzhong") --調用simplesnax中的response.echo方法
skynet.error("echo return:", r)
end)
運行結果:
testsimplesnax [:0100000a] LAUNCH snlua testsimplesnax [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: 123 abc false [:0100000a] snax service [simplesnax:100000b] startup [:0100000b] echo nengzhong [:0100000a] echo return: NENGZHONG #得到返回值
12.4 snax全局唯一服
修改testsimplesnax.lua:
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.uniqueservice("simplesnax", 123, "abc") --啓動simplessnax服務
obj = snax.queryservice("simplesnax") --查詢全局唯一服
snax.kill(obj, 123, "abc")
local gobj = snax.globalservice("simplesnax", 123, "abc") --啓動simplessnax服務
gobj = snax.queryglobal("simplesnax") --查詢全節點全局唯一服
snax.kill(gobj, 123, "abc")
skynet.exit()
end)
運行結果:
testsimplesnax [:0100000a] LAUNCH snlua testsimplesnax [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: 123 abc [:0100000b] snax server exit: 123 abc [:0100000b] KILL self [:0100000c] LAUNCH snlua snaxd simplesnax [:0100000c] snax server start: 123 abc [:0100000c] snax server exit: 123 abc [:0100000c] KILL self [:0100000a] KILL self
12.5 snax服務熱更
snax 是支持熱更新的(只能熱更新 snax 框架編寫的 lua 服務)。但熱更新更多的用途是做不停機的 bug 修復,不應用於常規的版本更新。所以,熱更新的 api 被設計成下面這個樣子。更適合打補丁。
你可以通過 snax.hotfix(obj, patchcode) 來向 obj 提交一個 patch 。
12.5.1 函數patch
simplesnax.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
local i = 10
gname = "nengzhong"
function accept.hello(...) --通過obj.post.hello
skynet.error("hello", i, gname, ...)
end
function accept.quit(...) --obj.post.quit來觸發回調函數
snax.exit(...)
--等同snax.kill(snax.self(), ...)
end
function init( ... )
skynet.error("snax server start:", ...)
end
function exit(...)
skynet.error("snax server exit:", ...)
end
testhotfix.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.newservice("simplesnax") --啓動simplessnax服務
obj.post.hello() --未更新之前調用一次
local r = snax.hotfix(obj, [[
function accept.hello(...)
print("fix hello", i, gname, ...) --skynet.error不能用了
end
]])
skynet.error("hotfix return:", r)
obj.post.hello() --更新之後再調用一次
obj.post.quit() --沒更新quit函數,還是能調用
skynet.exit()
end)
運行結果:
testhotfix [:0100000a] LAUNCH snlua testhotfix [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: [:0100000b] hello 10 nengzhong fix hello nil nengzhong #熱更成功,但是局部變量成了nil,全局變量gname還存在 [:0100000a] hotfix return: nil [:0100000a] KILL self [:0100000b] snax server exit: #熱更完quit函數還存在 [:0100000b] KILL self
12.5.2 local變量patch
上面的結果,我們發現local i不能用了,skynet也不能用了,這是因爲local變量沒有映射,需要我們自己來映射一下。
修改testhotfix.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.newservice("simplesnax") --啓動simplessnax服務
obj.post.hello() --未更新之前調用一次
local r = snax.hotfix(obj, [[
local skynet
local i
function accept.hello(...)
skynet.error("fix hello", i, gname, ...)
end
]])
skynet.error("hotfix return:", r)
obj.post.hello() --更新之後再調用一次
obj.post.quit() --沒更新quit函數,還是能調用
skynet.exit()
end)
運行結果:
testhotfix [:0100000a] LAUNCH snlua testhotfix [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: [:0100000b] hello 10 nengzhong [:0100000a] hotfix return: nil [:0100000a] KILL self [:0100000b] fix hello 10 nengzhong #變量skynet,i映射成功 [:0100000b] snax server exit: [:0100000b] KILL self
在 patch 中聲明的 local skynet 和 local i在之前的版本中也有同名的 local 變量。 snax 的熱更新機制會重新映射這些 local 變量。讓 patch 中的新函數對應已有的 local 變量,所以你可以安全的繼承服務的內部狀態。
12.5.3 修改snax服務線上狀態
patch 中可以包含一個 function hotfix(...) 函數,在 patch 提交後立刻執行。這個函數可以用來查看或修改 snax 服務的線上狀態(因爲 local 變量會被關聯)。hotfix 的函數返回值會傳遞給 snax.hotfix 的調用者。
修改testhotfix.lua
local skynet = require "skynet"
local snax = require "skynet.snax"
skynet.start(function ()
local obj = snax.newservice("simplesnax") --啓動simplessnax服務
obj.post.hello() --未更新之前調用一次
local r = snax.hotfix(obj, [[
local skynet
local i
function accept.hello(...)
skynet.error("fix hello", i, gname, ...)
end
function hotfix(...)
local temp = i
i = 100
return temp
end
]])
skynet.error("hotfix return:", r)
obj.post.hello() --更新之後再調用一次
obj.post.quit() --沒更新quit函數,還是能調用
skynet.exit()
end)
運行結果:
testhotfix [:0100000a] LAUNCH snlua testhotfix [:0100000b] LAUNCH snlua snaxd simplesnax [:0100000b] snax server start: [:0100000b] hello 10 nengzhong [:0100000a] hotfix return: 10 #hotfix有返回值 [:0100000a] KILL self [:0100000b] fix hello 100 nengzhong #修改了local i的值 [:0100000b] snax server exit: [:0100000b] KILL self
所以,你也可以提交一個僅包含 hotfix 函數的 patch ,而不修改任何代碼。這樣的 patch 通常用於查看 snax 服務的內部狀態(內部 local 變量的值),或用於修改它們。