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 变量的值),或用于修改它们。