skynet 入门笔记(2):service 消息收发

原创
2018/06/27 14:47
阅读数 2K

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的介绍如下。

  1. register(name) 给当前服务起一个字符串名。
  2. dispatch(type, func) 为 type 类型的消息设定一个处理函数。

回调函数的签名是这样:

function (session, source, ...)

其中dispatch的回调函数前两个参数分别是这样解释。

  1. session是为了确保能将消息处理结果对应到某条消息,就像是服务器收到同一个客户端的2条网络请求,一条请求js一条请求css,如果谁先处理完谁就返回的话,可能css的请求就收到了js的内容。
  2. 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!
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部