Skynet服务器框架(十二) (debug_console)控制台管理

在 skynet 框架中提供了一个控制台服务 debug_console ,用于帮助我们查看 skynet 的一些运行时数据,而且在需要热更代码的时候也需要借助控制台来实现。接下来,我们先看看如何使用控制台,然后在大致看一遍底层的源码实现。

工具准备

由于启动控制台服务后,需要借助 nctelnet 来远程连接控制台,所需需要确保机器上已安装了此类工具,这里我使用的是 nc 6.40 ,可以先查询本地是否已安装了该工具:

$ nc --version
Ncat: Version 6.40 ( http://nmap.org/ncat )

假如未安装,直接使用 yum 工具进行安装:


$ yum install nc

启动服务

在 skynet 的启动会服务(通常是在 skynet 启动时,也就是在 config 启动配置文件所指定的运行时第一个服务 start = "main" ,即 main.lua)中新增一句代码即可启动控制台服务:


skynet.newservice("debug_console", 8000)

第二个参数指定了远程连接的端口号,此端口可以根据自己需要进行修改,在启动 skynet 服务时看到如下 log 输出代表控制台启动成功:


[:0100000a] LAUNCH snlua debug_console 8000
[:0100000a] Start debug console at 127.0.0.1:8000

然后在新的 shell 窗口中连接控制台,连接成功如下:


$ nc 127.0.0.1 8000
Welcome to skynet console

官方建议:

出于安全考虑,调试控制台只能监听本地地址 127.0.0.1 ,所以如果需要远程使用,需要先登录到本机,然后再连接。

安装使用 telnet 并连接控制台服务其实也不难:


$ yum install telnet
$ telnet 127.0.0.1:8000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Welcome to skynet console

帮助文档

直接使用 help 指令查询帮助文档,了解所有可用的指令 :


[root@localhost ~]# nc 127.0.0.1 8000
Welcome to skynet console
help
call    call address ...
clearcache  clear lua code cache
cmem    Show C memory info
debug   debug address : debug a lua service
exit    exit address : kill a lua service
gc  gc : force every lua service do garbage collect
help    This help message
info    info address : get service infomation
inject  inject address luascript.lua
kill    kill address : kill service
list    List all the service
log launch a new lua service with log
logoff  logoff address
logon   logon address
mem mem : show memory status
ping    ping address
service List unique service
shrtbl  Show shared short string table info
signal  signal address sig
snax    lanuch a new snax service
start   lanuch a new lua service
stat    Dump all stats
task    task address : show service task detail
<CMD OK>

直接在当前 shell 窗口输入对应的指令并带上参数即可,官方文档介绍参数格式:

命令的一般格式是 命令 地址 ,有些命令不带地址,会针对所有的服务。当输入地址时,可以使用 :01000001 这样的格式指代一个服务地址:由冒号开头的 8 位 16 进制数字,也可以省略前面两个数字的 harbor id 以及接下来的连续 0 ,比如 :01000001 可以简写为 1 。所有活动的服务可以输入 list 列出。

源码解析

服务的 lua 源码在 service\debug_console.lua 中,从 skynet.start 方法开始:


skynet.start(function()
    local listen_socket = socket.listen (ip, port)
    skynet.error("Start debug console at " .. ip .. ":" .. port)
    socket.start(listen_socket , function(id, addr)
        -- 返回执行结果给指令请求端
        local function print(...)
            local t = { ... }
            for k,v in ipairs(t) do
                t[k] = tostring(v)
            end
            socket.write(id, table.concat(t,"\t"))
            socket.write(id, "\n")
        end
        socket.start(id)
        skynet.fork(console_main_loop, id , print)
    end)
end)

这里启动了一个 Socket 监听:


local listen_socket = socket.listen (ip, port)

监听的 ip 和端口可以在启动服务时传入:


local ip = (arg.n == 2 and arg[1] or "127.0.0.1")   -- 默认是本地 ip 127.0.0.1 ,或者是传入两个参数时的第一个参数
local port = tonumber(arg[arg.n])   -- 传入参数的最后一个

启动完监听端口后,启动一个协程来运行一个死循环,用于监控 socket 连接端输入的命令,并解析运行命令,将结果通过 socket 返回给 socket 连接端:


skynet.fork(console_main_loop, id , print)

命令结果输出:

此处传入的最后一个参数 print 是一个 function ,用于向 socket 连接端返回执行指令的打印结果,以文本的形式,这里可以简单地理解为:socket 监听端与连接端的一种聊天模式。

命令输入:

在协程中的死循环用于监听命令请求端输入的命令,基本逻辑如下:


    pcall(function()
        while true do
            local cmdline = socket.readline(stdin, "\n")
            if not cmdline then
                break
            end
            -- http 命令数据解析
            if cmdline:sub(1,4) == "GET " then
                -- http
                local code, url = httpd.read_request(sockethelper.readfunc(stdin, cmdline.. "\n"), 8192)
                local cmdline = url:sub(2):gsub("/"," ")
                docmd(cmdline, print, stdin)
                break
            end
            -- nc 或 telnet 命令数据解析
            if cmdline ~= "" then
                docmd(cmdline, print, stdin)
            end
        end
    end)
  • 通过 socket.readline 读取最新的命令行,没有新命令则结束本次;

  • 有新命令,则解析命令,这里接续要区分 nctelnet 传入的 socket 数据和通过 Http 请求发过来的数据。

docmd 是根据解析结果调用具体执行接口的方法:

local function docmd(cmdline, print, fd)
    local split = split_cmdline(cmdline)
    local command = split[1]
    local cmd = COMMAND[command]
    local ok, list
    if cmd then
        ok, list = pcall(cmd, table.unpack(split,2))
    else
        cmd = COMMANDX[command]
        if cmd then
            split.fd = fd
            split[1] = cmdline
            ok, list = pcall(cmd, split)
        else
            print("Invalid command, type help for command list")
        end
    end

    if ok then
        if list then
            if type(list) == "string" then
                print(list)
            else
                dump_list(print, list)
            end
        end
        print("<CMD OK>")
    else
        print(list)
        print("<CMD Error>")
    end
end

重要接口

  • info address

    查看指定服务详情的接口

  • inject inject address

    热更时常用到的接口

优化控制台

在 skynet 的 debug_console 中不能使用键盘的上下键来追溯之前的输入历史命令,每次都要重新输入命令。想优化这个操作,需要使用到 readline 的 history 特性,可以通过安装 rlwrap 工具来实现。具体的安装步骤如下:

  • 安装 readline ,因为 rlwrap 依赖于此工具,在 skynet 编译的时候也依赖与此工具:

    $ yum -y install readline-devel
  • 安装 rlwrap ,这是一个开源的工具,源码在 Github hanslub42/rlwrap 上可以找到,最新的版本是 v 0.43 ,可以下载源码来进行编译安装,但这里为了节省时间还是建议直接到 这里 下载安装包 rlwrap-0.43.tar.gz ,然后解压安装:

    $ tar -zxf rlwrap-0.43.tar.gz   
    $ cd rlwrap-0.43  
    $ ./configure -q # -q表示静默不打印配置日志 
    $ make && make install

    检查安装成功:

    $ rlwrap --version
    rlwrap 0.43

rlwrap 常用的快捷:


Ctrl+L 清屏,实际是将当前行置顶
Ctrl+P 上一条命令
Ctrl+N 下一条命令
Ctrl+U 从光标处删除到行首
Ctrl+W 向前删除一个单词
Ctrl+B 光标向前移动一个位置
Ctrl+T 光标处字符与前一个字符交换位置
Ctrl+Z 后台运行,使用fg调出
Ctrl+H 相当于删除键
Ctrl+J 相当于回车键
Ctrl+O 相当于回车键
Ctrl+M 相当于回车键

需要在 nc 中使用到 rlwrap 的功能,连接时的指令需要加上 rlwrap 前缀,变成:


rlwrap nc 127.0.0.1 8000

不加前缀的话,rlwrap 工具是不起作用的。

参考

猜你喜欢

转载自blog.csdn.net/linshuhe1/article/details/79563235