本篇主要内容
- lua service的基础操作
- 介绍其中可能会遇到的坑
需要前提了解的知识
- Skynet的基本启动
lua service基础操作
创建与销毁
在projects/hello_world中,我们在配置文件指定了主入口服务,类似main函数,一般会通过它来启动其他的service。接下来我们来分析下它的代码:
local Skynet = require "skynet"
local Log = require "log"
local function __init__()
Skynet.newservice('debug_console', Skynet.getenv("CONSOLE_PORT"))
Skynet.newservice('hello_world')
Skynet.exit()
end
Skynet.start(__init__)
第1行 skynet的模块是整个框架主要的LuaApi库,绝大部分lua的接口都来源于此。
最后一行,通过Skynet.start可以注册service的初始化函数
-
在init函数中,使用了Skynet.newservice创建了两个service对象。
第一个参数代表服务类名(相当于面向对象中的class), 后面是参数列表
虽然还有其他的创建service的函数,推荐使用newservice。
在init函数的最后,调用了Skynet.exit关闭该service。如果需要创建的service常驻,最后不调用exit函数即可。
如果想要让一个service关闭另一个service怎么做呢?可以用Skynet.kill,不过需要先拿到目标service的地址,比如下面的例子:
print("my_addr:", Skynet.self()) -- 获取自己地址
local obj = Skynet.newservice("hello_world") -- 创建并把地址记录下来
Skynet.kill(obj) -- 杀掉目标
消息发送与分发
-
调用方式有两种,分别是SEND和CALL。SEND表示不等待调用返回,是非阻塞式的;CALL表示会等待返回,是阻塞式的
可以用Skynet.send和Skynet.call发送消息,对应上面的例子,大概是这样:
Skynet.send(addr, "lua", "test_send", "hello jasmine")
Skynet.call(addr, "lua", "test_call", 1, 2)
-
接收方可以使用Skynet.dispatch注册分发函数,一般写在service的启动逻辑中
比如下面这个例子,表示注册了lua协议消息,其中test_send和test_call是消息名:
local CMD = {}
function CMD.test_send(s)
print("test_send:", s)
end
function CMD.test_call(a, b)
print("test_call:", a, b)
Skynet.retpack({code = 200, sum = a + b})
end
Skynet.dispatch("lua", function(session, source, cmd, ...)
local f = assert(CMD[cmd])
f(...)
end)
-
仔细一些同学可能会发现发送和分发都需要写一个"lua"
这个是表示消息的类型,目前默认的已经有十几种,开发者也可以根据需要自定义类型,可以使用下面的接口注册,然后发送方把"lua"改成相应的协议名就可以了:
-- 发送者和接收者都需要注册
skynet.register_protocol {
name = "text", # 协议名称
id = skynet.PTYPE_TEXT, # 协议ID
pack = function(m) return tostring(m) end, # 消息编码
unpack = skynet.tostring, # 消息解码
}
需要注意的地方
- Skynet.newservice时的传参,目前都是string类型,比如传了数字,需要自行转换
练习题
- 阅读并运行练习题仓库中projects/service_rpc的代码,在start服务中增加一段直接杀掉新建的test_rpc服务的逻辑,在test_rpc服务中增加一个关闭服务的接口
- 定义一种新的协议类型(类似text和lua),然后尝试写几个相关的接口