【Lua杂谈】服务端架构skynet简易入门项目——create-skynet

前言

skynet通信原理与源码分析一文中,我们已经详尽地弄清楚了skynet地通信架构,为我们上手skynet提供了极大的帮助。因此本篇文章接续上文,正式上手使用skynet。

skynet入门项目:create-skynet

要做一个基于skynet的项目,首先需要一个好的模板。skynet的最佳实践并非将服务卸载skynet模块中,而是将skynet当作一个单独的库/SDK看待,自己独立在另外的目录写业务逻辑。因此,笔者在数月前简单地整合了一下skynet的boilerplate项目——create-skynet,采用这个项目搭建skynet服务端结构会较为清晰。

服务&库的约定

在create-skynet的配置中,每个服务的lua文件入口以config中luaservice项为准:

  • service/服务名.lua
  • service/服务名/main.lua
  • skynet/service/服务名.lua(默认的服务)

skynet启动时会根据服务名注册相应服务,因此自己在service下定义的服务名最好不要与skynet原有服务重名。

skynet在为每一个服务读取lua库的时候,会根据运行skynet脚本的工作目录以及config里的设置去读取,这是由config的lua_pathlua_cpath为准的。在create-skynet里,lualib的位置有:

  • service/服务名/?.lua
  • lualib/?.lua
  • luaclib/?.so
  • skynet/lualib/?.lua(默认)
  • skynet/luaclib/?.so(默认)

因此后续在服务逻辑中require时,需要注意lualib文件所在的位置。

skynet更多具体的config设置,可参考官方wiki

启动skynet服务

create-skynet实现了skynet最简单的sproto服务的例子。如果您对sproto不了解,可以参阅笔者的lua专用rpc协议sproto一文。

在create-skynet里,config的start项指定了初始的服务入口——main,因此我们只需在service/main.lua里写内容,skynet就会自动读到。

-- service/main.lua
local skynet = require "skynet"

skynet.start(function()
    skynet.error("create-skynet start server~")
    skynet.uniqueservice("proto")
    local debug_console_port = skynet.getenv("debug_console_port")
    if debug_console_port then
        skynet.newservice("debug_console", debug_console_port)
    end
    skynet.newservice("db")
    local watchdog = skynet.newservice("watchdog")
    local watchdog_port = skynet.getenv("watchdog_port")
    skynet.call(watchdog, "lua", "start", {
        port = watchdog_port,
        nodelay = true
    })
    skynet.error("Watchdog listening on", watchdog_port)
    skynet.exit()
end)

在主入口service/main.lua中,通过skynet.start(callback)的形式,就可以定义该服务启动时的逻辑。首先启动了协议服务proto;其次通过skynet.getenv读取debug_console_port配置项,如果有则启动skynet内置debug_console服务;而后启动db服务,是一个意思意思的内存kv数据库;之后启动watchdog,监听配置的watchdog_port所对应的端口。

我们可以通过解构watchdog服务,从而了解skynet服务的基本样式。

-- service/watchdog/main.lua
local skynet = require "skynet"

local CMD = {}
local SOCKET = {}
local gate
local agent = {}

function SOCKET.open(fd, addr)
    skynet.error("New client from : " .. addr)
    agent[fd] = skynet.newservice("agent")
    skynet.call(agent[fd], "lua", "start", {
        gate = gate, client = fd, watchdog = skynet.self()
    })
end

local function close_agent(fd)
    local a = agent[fd]
    agent[fd] = nil
    if a then
        skynet.call(gate, "lua", "kick", fd)
        -- disconnect never return
        skynet.send(a, "lua", "disconnect")
    end
end

function SOCKET.close(fd)
    print("socket close",fd)
    close_agent(fd)
end

function SOCKET.error(fd, msg)
    print("socket error",fd, msg)
    close_agent(fd)
end

function SOCKET.warning(fd, size)
    -- size K bytes havn't send out in fd
    print("socket warning", fd, size)
end

function SOCKET.data(fd, msg)
end

function CMD.start(conf)
    skynet.call(gate, "lua", "open" , conf)
end

function CMD.close(fd)
    close_agent(fd)
end

skynet.start(function()
    skynet.dispatch("lua", function(session, source, cmd, subcmd, ...)
        if cmd == "socket" then
            local f = SOCKET[subcmd]
            f(...)
            -- socket api don't need return
        else
            local f = assert(CMD[cmd])
            skynet.ret(skynet.pack(f(subcmd, ...)))
        end
    end)
    gate = skynet.newservice("gate")
end)

watchdog服务主入口的代码中,出现了skynet.dispatch,它会为某个类型的消息注册回调函数从而进行处理。如果哪个服务调用了skynet.send/call(watchdog地址, "lua", ...),那么watchdog就会拿回调函数跟后边...的参数凑上去,执行业务逻辑了。

在skynet架构中,watchdog与内置的网关服务gate是强耦合的。我们可以看到watchdog在刚启动时也会新增gate服务,然后service/main.lua调用skynet.call(watchdog, "lua", "start", 配置)时,watchdog主入口中对应的CMD.start就会被执行,然后在gate调用的gateserver.lua中,CMD.open的开启端口监听的逻辑被执行了。之后,如果有新的连接,gate会通知watchdog新连接的fd跟地址,并且而watchdog只需要负责新增agent服务,根据socket的不同情况执行相应回调管理agent就好了。

agent服务里,只需要根据连接的fd,读取或发送数据就好,网关服务gate会帮你分包。这里agent主入口也采用官方例子中的代码,基本的套路也是一方面不断read这个fd出来的数据,识别sproto,然后调用db服务存取数据;另一方面会隔一段时间向fd写入心跳包,实现双工长连接tcp。此处便不再赘述啦~

总结

skynet的基本用法便是如此,可以阅读create-skynet全部代码以了解。后面还有很多挖掘点,可以查看wiki等资料深入探索~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

utmhikari

创作不易,共同助力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值