启动服务
local skynet = require"skynet"
skynet.start(function ()
-- body
end)
启动一个服务需要调用 skynet 的 start 接口,并向其传入一个启动函数。
定义消息和处理消息
skynet 的服务是用来接收和处理消息的,那么服务会接收什么样的消息呢。通常会在传入的启动函数中注册该服务能够处理的消息类型定义和分发方式。
- register_protocol 用于消息定义
- dispatch 用于消息分发
local skynet = require"skynet"
local LUACMD = {}
function LUACMD.echo(msg)
return msg
end
local JSONCMD = {}
function JSONCMD.echo(msg)
print_r(json.decode(msg))
return msg
end
skynet.start(function ()
skynet.dispatch("lua", function(session, source, cmd, ...)
local f = assert(LUACMD[cmd])
skynet.ret(skynet.pack(f(...)))
end)
-- 定义新的 json 消息类型
skynet.register_protocol {
name = "json",
id = 20,
pack = function(m)
return json.encode(m)
end,
unpack = function(m)
local msg_str = skynet.tostring(m)
return json.decode(msg_str)
end,
}
-- 分发 json 类型的消息
skynet.dispatch("json", function (session, source, msg)
local cmd,args = assert(msg.cmd), msg.args
skynet.ret(cmd(table.unpack(args)))
end)
end)
以上代码实现了 lua 、json 两种类型消息的分发,其中 lua 是系统已经实现定义好的消息类型, json 类型消息是自定义的类型。
自定义的消息类型时,需提供 name、id、pack、unpack 四个信息。name 和 id 在系统中需唯一,因为系统实现了十多种消息类型占用了前十多个正整数,因而将 id 设为 20。pack 和 unpack 分别是消息的编码和解码函数。
所谓解码,指当收到一个服务收到一个该类型的消息之后,首先会经过 unpack 处理一遍。好比收到的 json
消息实为 json 字符串,需经过 json.decode 成 lua 类型数据以便使用。
类似的方式,当从该服务发送一条 json 消息给其他服务时,也会使用 pack 进行 json.encode 处理一下才会变为 json 格式字符串。当然该其他服务也需实现了能处理 json 消息才好。
实现服务间通信
大多数情况下,仅使用 lua 消息就可以很好的实现我们的业务,因而一个服务的样子大致是这样:
local skynet = require"skynet"
local CMD = {}
function CMD.echo(msg)
return msg
end
function CMD.foo()
return "bar"
end
skynet.start(function ()
skynet.dispatch("lua", function( session, source, cmd, ... )
local f = assert(CMD[cmd])
skynet.ret(skynet.pack(f(...)))
end)
end)
按这个方式,模拟两个 玩家服务 通过 聊天服务 互发消息。
chatd.lua
local skynet = require"skynet"
require"skynet.manager"
local agents = {}
local mt = {}
mt.__index = mt
function mt.msg(from,to,msg)
local to_agent = assert(agents[to])
skynet.send(to_agent,"lua","msg",from,msg)
end
function mt.add_agent(name,agent)
agents[name] = agent
end
skynet.start(function ()
skynet.dispatch("lua", function(session,source,cmd,...)
local f = assert(mt[cmd])
return skynet.ret(skynet.pack(f(...)))
end)
skynet.register(".chatd")
end)
agent.lua
local skynet = require"skynet"
local name
local mt = {}
mt.__index = mt
function mt.msg(from, msg)
skynet.error(string.format("agent [%s] get msg:[%s] from agent[%s]", name,msg,from))
end
function mt.send(from,to,msg)
local chatd = skynet.localname(".chatd")
skynet.send(chatd,"lua","msg",from,to,msg)
end
function mt.setname(agent_name)
name = agent_name
end
skynet.start(function (agent_name)
name = agent_name
skynet.dispatch("lua", function(session,source,cmd,...)
local f = assert(mt[cmd])
return skynet.ret(skynet.pack(f(...)))
end)
end)
main.lua
local skynet = require"skynet"
skynet.start(function()
local chatd = skynet.newservice("chatd") -- 启动聊天服务
local tom = skynet.newservice("agent") -- 启动玩家并设置名称为tom
skynet.call(tom,"lua","setname","tom")
local jim = skynet.newservice("agent") -- 启动玩家并设置名称为jim
skynet.call(jim,"lua","setname","jim")
skynet.call(chatd,"lua","add_agent","tom",tom) -- 把 tom 注册到 聊天服
skynet.call(chatd,"lua","add_agent","jim",jim) -- 把 jim 注册到 聊天服
skynet.send(tom,"lua","send","tom","jim","foo") -- 通知 tom 让其向 jim 发消息
skynet.send(jim,"lua","send","jim","tom","bar") -- 通知 jim 让其向 tom 发消息
skynet.exit()
end)
执行
skynet/skynet etc/config
输出
[: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 chatd
[:0000000a] LAUNCH snlua agent
[:0000000b] LAUNCH snlua agent
[:00000008] KILL self
[:0000000b] agent [jim] get msg:[foo] from agent[tom]
[:00000002] KILL self
[:0000000a] agent [tom] get msg:[bar] from agent[jim]