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!