0
点赞
收藏
分享

微信扫一扫

Redis Lua脚本调试

​​​​

 

从版本3.2开始,Redis包含一个完整的Lua调试器,可以用来使编写复杂Redis脚本的任务更加简单。

由于Redis 3.2仍处于测试阶段,请​​unstable​​从Github 下载Redis 的分支并编译它以测试调试器。您可以使用Redis unstable来调试稍后在稳定版本的Redis中运行的脚本,因此调试器已经可用于实际术语。

Redis Lua调试器,代号LDB,具有以下重要功能:

  • 它使用服务器 - 客户端模型,因此它是一个远程调试器。Redis服务器充当调试服务器,而默认客户端则是​​redis-cli​​。但是,可以通过遵循服务器实现的简单协议来开发其他客户端。
  • 默认情况下,每个新的调试会话都是分叉会话。这意味着在调试Redis Lua脚本时,服务器不会阻塞并可用于开发或者为了并行执行多个调试会话。这也意味着在脚本调试会话结束后回滚更改,因此可以使用与上一个调试会话完全相同的Redis数据集再次重新启动新的调试会话。
  • 可以根据需要使用备用同步(非分叉)调试模型,以便可以保留对数据集的更改。在此模式下,服务器会阻止调试会话处于活动状态。
  • 支持逐步执行。
  • 支持静态和动态断点。
  • 支持将调试脚本记录到调试器控制台中。
  • 检查Lua变量。
  • 跟踪由脚本执行的Redis命令。
  • Redis和Lua值的漂亮印刷。
  • 无限循环和长执行检测,模拟断点。

快速开始

开始使用Lua调试器的一个简单方法是观看此视频介绍:

重要说明:请确保使用Redis生产服务器避免调试Lua脚本。请改用开发服务器。另请注意,使用同步调试模式(这不是默认设置)会导致Redis服务器阻塞调试会话持续的所有时间。

要使用​​redis-cli​​以下步骤启动新的调试会话:

  1. 使用首选编辑器在某个文件中创建脚本。假设您正在编辑位于的Redis Lua脚本​​/tmp/script.lua​​。
  2. 启动调试会话:
    ./redis-cli --ldb --eval(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​--eval(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​redis-cli​​您可以将键名和参数传递给脚本,并以逗号分隔,如下例所示:

    ./redis-cli --ldb --eval(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​redis-cli​​不再接受其正常命令的特殊模式,而是打印一个帮助屏幕并将未修改的调试命令直接传递给Redis。

    唯一没有传递给Redis调试器的命令是:

    • ​quit​​​ - 这将终止调试会话。这就像删除所有断点并使用​​continue​​​debugging命令一样。此外,命令将退出​​redis-cli​​。
    • ​restart​​- 调试会话将从头开始重新启动,从文件重新加载新版本的脚本。因此,正常的调试周期包括在调试之后修改脚本,并调用​​restart​​以便在新脚本更改时再次开始调试。
    • ​help​​ - 此命令传递给Redis Lua调试器,该调试器将打印如下命令列表:

    lua debugger> help
    Redis Lua debugger help:
    [h]elp Show this help.
    [s]tep Run current line and stop again.
    [n]ext Alias for step.
    [c]continue Run till next breakpoint.
    [l]list List source code around current line.
    [l]list [line] List source code around [line].
    line = 0 means: current position.
    [l]list [line] [ctx] In this form [ctx] specifies how many lines
    to show before/after [line].
    [w]hole List all source code. Alias for 'list 1 1000000'.
    [p]rint Show all the local variables.
    [p]rint <var> Show the value of the specified variable.
    Can also show global vars KEYS and ARGV.
    [b]reak Show all breakpoints.
    [b]reak <line> Add a breakpoint to the specified line.
    [b]reak -<line> Remove breakpoint from the specified line.
    [b]reak 0 Remove all breakpoints.
    [t]race Show a backtrace.
    [e]eval(in a different callframe).
    [r]edis <cmd> Execute a Redis command.
    [m]axlen [len] Trim logged Redis replies and Lua var dumps to len.
    Specifying zero as <len> means unlimited.
    [a]abort Stop the execution of the script. In sync
    mode dataset changes will be retained.

    Debugger functions you can call from Lua scripts:
    redis.debug() Produce logs in the debugger console.
    redis.breakpoint() Stop execution as if there was a breakpoint in the
    next line of code.

    请注意,启动调试器时,它将以步进模式启动。它将停止在脚本的第一行,它在执行之前实际执行某些操作。

    从这一点开始,您通常会调用​​step​​以执行该行并转到下一行。在您执行步骤时,Redis将显示服务器执行的所有命令,如以下示例所示:

    * Stopped at 1, stop reason = step over
    -> 1 redis.call('ping')
    lua debugger> step
    <redis> ping
    <reply> "+PONG"
    * Stopped at 2, stop reason = step over

    的​​<redis>​​​和​​<reply>​​​线示出通过仅执行的行执行的命令,以及来自服务器的答复。请注意,这仅在步进模式下发生。如果您使用​​continue​​以执行脚本直到下一个断点,则不会将命令转储到屏幕上以防止输出过多。

    终止调试会话

    当脚本自然终止时,调试会话结束并 ​​redis-cli​​​以正常的非调试模式返回。您可以像往常一样使用该​​restart​​命令重新启动会话。

    停止调试会话的另一种方法是​​redis-cli​​​ 通过按下手动中断​​Ctrl+C​​​。请注意,也无论如何打破之间的连接​​redis-cli​​​和​​redis-server​​将中断调试会话。

    关闭服务器时,所有分叉的调试会话都将终止。

    缩写调试命令

    调试可能是一个非常重复的任务。因此,每个Redis调试器命令都以不同的字符开头,您可以使用单个初始字符来引用该命令。

    因此,例如,​​step​​​您可以输入而不是键入​​s​​。

    断点

    如在线帮助中所述,添加和删除断点很简单。只需使用​​b 1 2 3 4​​​在第1,2,3,4行添加​​b 0​​​断点。该命令将删除所有断点。可以使用as参数删除选定的断点,即我们要删除的断点所在的行,但前缀为减号。例如​​b -3​​,从第3行中删除断点。

    请注意,将断点添加到Lua从不执行的行(如声明局部变量或注释)将不起作用。断点将被添加,但由于脚本的这部分将永远不会被执行,程序将永远不会停止。

    动态断点

    使用该​​breakpoint​​​命令可以将断点添加到特定行中。但是,有时我们只想在发生特殊情况时才停止执行程序。为此,您可以使用​​redis.breakpoint()​​Lua脚本中的 函数。调用时,它会模拟下一行中将要执行的断点。

    if counter > 10 then redis.breakpoint() end

    此功能在调试时非常有用,因此我们可以避免多次手动继续执行脚本,直到遇到给定条件。

    同步模式

    如前所述,但默认LDB使用分叉会话来回滚脚本在调试时所操作的所有数据更改。在调试期间,确定性通常是一件好事,因此可以启动连续的调试会话,而无需将数据库内容重置为其原始状态。

    但是,为了跟踪某些错误,您可能希望保留每个调试会话对密钥空间执行的更改。当这是一个好主意,你应该使用一个特殊的选项,启动调试器​​ldb-sync-mode​​​中​​redis-cli​​。

    ./redis-cli --ldb-sync-mode --eval(231, 243, 237); padding: 0px 3px; border-radius: 4px; overflow-wrap: break-word; text-indent: 0px;">​abort​​​命令可以使操作中的更改中途停止脚本到数据集。请注意,与正常结束调试会话相比,这是不同的。如果你只是中断​​redis-cli​​​脚本将完全执行,然后会话终止。相反,​​abort​​您可以在中间中断脚本执行,并在需要时启动新的调试会话。

    从脚本记录

    该​​redis.debug()​​命令是一个功能强大的调试工具,可以在Redis Lua脚本中调用,以便将内容记录到调试控制台中:

    lua debugger> list
    -> 1 local a = {1,2,3}
    2 local b = false
    3 redis.debug(a,b)
    lua debugger> continue
    <debug> line 3: {1; 2; 3}, false

    如果脚本在调试会话之外执行,​​redis.debug()​​则根本不起作用。请注意,该函数接受多个参数,这些参数由输出中的逗号和空格分隔。

    正确显示表和嵌套表,以便为程序员调试脚本使值易于观察。

    使用​​print​​​和检查程序状态​​eval​

    虽然​​redis.debug()​​可以使用该函数直接从Lua脚本中打印值,但通常在步进或停止到断点时观察程序的局部变量很有用。

    该​​print​​​命令就是这样,并在调用帧中执行查找,从当前的一个回到之前的一个,直到顶层。这意味着即使我们进入Lua脚本中的嵌套函数,我们仍然可以使用​​print foo​​​查看​​foo​​​调用函数的上下文中的值。在没有变量名称的情况下调用时,​​print​​将打印所有变量及其各自的值。

    该​​eval(232, 232, 232); background: rgb(249, 249, 249);">

    lua debugger> e redis.sha1hex('foo')
    <retval> "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"

    调试客户端

    LDB使用客户端 - 服务器模型,其中Redis服务器充当使用RESP进行​通信的调试服务器。虽然​​redis-cli​​​是默认的调试客户端,但只要满足以下条件之一,任何客户端都可用于调试:

    1. 客户端提供本机接口,用于设置调试模式和控制调试会话。
    2. 客户端提供了一个通过RESP发送任意命令的接口。
    3. 客户端允许将原始消息发送到Redis服务器。

    例如,Redis的插件​用于ZeroBrane工作室​使用LDB集成Redis的-卢阿。以下Lua代码是插件如何实现的简化示例:

    local redis = require 'redis'

    -- add LDB's Continue command
    redis.commands['ldbcontinue'] = redis.command('C')

    -- script to be debugged
    local script = [[
    local x, y = tonumber(ARGV[1]), tonumber(ARGV[2])
    local result = x * y
    return result
    ]]

    local client = redis.connect('127.0.0.1', 6379)
    client:script("DEBUG", "YES")
    print(unpack(client:eval(script, 0, 6, 9)))
    client:ldbcontinue()

     

    ​​​​

    作者:​​sunsky303​​​


举报

相关推荐

0 条评论