Snax

来源:互联网 发布:连不上树熊网络 编辑:程序博客网 时间:2024/05/16 12:06

snax 是一个方便的skynet服务实现的简单框架.(简单是相对于skynet的api而言)


使用snax服务先要在 Config 中配置snax 用于路径查找,每个snax服务都有一个用于启动服务的名字,推荐按lua的模块命名规则,但目前不推荐服务名中包含"点"(在路径搜索上尚未支持 . 与 / 的替换).在启动服务时会按查找路径搜索对应的文件.


snax服务用lua编写,但并不是一个独立的lua程序.它里面包含了一组lua函数,会被snax框架分析加载.


test/pingserver.lua 就是一个简单的snax服务范例

local skynet = require "skynet"local i = 0local hello = "hello"function response.ping(hello)    skynet.sleep(100)    return helloendfunction accept.hello()    i = i + 1    print (i, hello)endfunction response.error()    error "throw an error"endfunction init( ... )    print ("ping server start:", ...)endfunction exit(...)    print ("ping server exit:", ...)end

snax服务的启动和退出


每个snax服务中都需要定义一个init函数,启动这个服务会调用这个函数,并把启动参数传给它.

snax 服务还可以定义一个exit  函数用于响应服务退出的时间,同样能接受一些参数.

和标准的skynet服务不同,这些参数的类型不受限制,可以是lua的复杂数据类型.(而skynet服务受底层限制,只可以接受字符串参数)


启动一个snax服务有三种方式:

local snax = require "snax"

1.snax.newservice(name,...) : 可以把一个服务启动多份,传入服务名和参数,它会返回一个对象,用于和这个启动的服务交互.如果多次调用newservice,即使名字相同,也会生成多份服务的实例,它们各自独立,由不同的对象区分.


2.snax.uniqueservice(name,...) : 和上面的api类似,但在一个节点上只会启动一份同名服务.如果你多次调用它,会返回相同的对象.

3.snax.globalservice(name,...) : 和上面的api类似,但在整个skynet网络中(如果你启动了多个节点),只会有一个同名服务.


对于匿名服务,你无法再别处通过名字得到和它交互的对象.如果你有这个需求,可以把对象的 .handle域通过消息发送给别人.handle是一个数字,即snax服务的skynet服务地址.


这个数字的接收方可以通过snax.bind(handle,typename)把它转换成服务对象,这里第二个参数需要传入服务的启动名,以用来了解这个服务有哪些远程方法可以供调用,当然,你也可以直接把.type域和.handle一起发送过去,而不必再源代码上约定.


如果你想让一个snax服务退出,调用snax.kill(obj,...)即可.


snax.self() 用来获取自己这个服务对象,它等价于snax.bind(skynet.self(),SERVER_NAME)


snax.exit(...) 退出当前服务,它等价于snax.kill(snax.self(),...).


注:snax的接口约定是通过分析对应的lua源文件完成的,所以无论是消息发送方还是消息接收方都必须可以读到其消息处理的源文件.不仅snax.newservice会加载对应的源文件生成新的服务;调用者本身也会在自身的lua虚拟机内加载对应的lua文件做一个分析;同样,snax.bind也会按typename分析对应的源文件.


RPC调用

snax服务中可以定义一组函数用于响应其它服务提出的请求,并给出(或不给出)回应,一个非snax服务也可以调用snax服务的远程方法.


要定义这类远程方法,可以通过定义function response.foobar(...)来声明一个远程方法.foobar是方法名,response前缀表示这个方法一定有一个回应.你可以通过函数返回值来回远程调用.


调用这个远程方法,可以通过obj.req.foobar(...) 来调用它.obj是服务对象,req表示这个调用需要接收返回值,foobar是方法的名字.其实,在创建出obj对象时,框架已经了解了这个foobar方法有返回值,这里多此一举让使用者明确req类型是为了让调用者(以及潜在的代码维护者)清楚这次调用是阻塞的,会导致服务挂起,等待对方的回应.


如果你设计的协议不需要返回值,那么可以通过定义function accept.foobar(...) 来声明.这里可以有和response 组相同方法名.通过accept前缀来区分response组下同名的方法.这类方法的实现函数不可以有返回值.


调用这类不需要返回值的远程方法,应该使用obj.post.foobar(...).这个调用不会阻塞,所以你也不用担心这里的服务会挂起,因为别的消息进入改变了服务的内部状态.


注:post不适用于Cluster模式,只能用于本地消息或master/slave结构下的消息.


热更新


snax是支持热更新的(只能热更新snax框架编写的lua服务).但热更新更多的用途是做不停机的bug修复,不应用与常规的版本更新.所以,热更新的api被设计成下面这个样子.更适合打补丁,


你可以通过snax.hotfix(obj,patchcode)来向obj提交一个patch.

举个例子,你可以向上面列出的pingserver提交一个patch.


snax.hotfix(obj, [[local ilocal hellofunction accept.hello()    i = i + 1    print ("fix", i, hello)endfunction hotfix(...)    local temp = i    i = 100    return tempend]]))
这样便改写了accept.hello方法.在patch中声明的local i 和 local hello 在之前的版本中也有同名的local 变量.snax的热更新机制会重新映射这些local 变量.让patch中的新函数对应已有的local 变量,所以你可以安全的继承服务的内部状态.


patch中可以包含一个 function hotfix(...) 函数,在patch提交后立刻执行,这个函数可以用来查看或修改snax服务的线上状态(因为local变量会被关联).hotfix的函数返回值会传递给snax.hotfix的调用者.


所以,你也可以提交一个仅包含hotfix函数的patch,而不修改任何代码.这样的patch通常用于查看snax服务的内部状态(内部local变量的值),或用于修改它们.

注: 不可以在patch中增加新的远程方法.


test/testping.lua是对pingserver的调用范例,你可以查看它加深理解.


snax的实现分两部分:让服务工作起来的框架,实现在service/snaxd.lua;snax的api,在lualib/snax.lua以及lualib/snax/*中,有兴趣的同学可以阅读源码

0 0
原创粉丝点击