详解 APISIX Lua 动态调试插件 inspect

本文介绍了APISIX中用于Lua动态调试的inspect插件,详细阐述了为何需要这样的插件,其工作原理,以及如何安装、配置和使用。插件允许在不修改源代码的情况下进行动态调试,避免生产环境的影响,支持定制断点,能有效处理复杂调试需求。
摘要由CSDN通过智能技术生成

作者罗锦华,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。

关于 API7.ai 与 APISIX

API7.ai 是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 – APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。

原文链接

为什么需要 Lua 动态调试插件?

Apache APISIX 有很多 Lua 代码,如何在运行时不触碰源代码的情况下,检查代码里面的变量值?

修改 Lua 源码来调试有如下缺点:

  • 生产环境不允许也不应该修改源码
  • 修改源码需要 reload,使得业务功能失效
  • 容器环境难以修改源码
  • 产生的临时代码容易忘记回滚,导致维护问题

很多时候我们不仅仅需要在函数开始或结束的时候去检查变量,而且需要在满足一定条件,例如某个循环体被循环到了一定次数,
或者某个条件判断为真的时候我们才查看变量值,并且也不仅仅是简单打印变量值,有时候还可能需要将相关信息发送到外围系统。
并且,这个过程如何做到动态化呢?而且,开启调试后,能否不影响程序运行的性能呢?

Lua 动态调试插件就是辅助你完成以上需求的插件,该插件被命名为 inspect 插件。

  • 断点处理可定制
  • 断点设置动态化
  • 多个断点
  • 断点可被定义为只生效一次
  • 可控制性能影响范围

插件原理

它充分利用了 Lua 提供的 Debug API 来实现功能。解释器模式执行的每一个字节码都可以对应到它所属的文件以及行号,我们只需要判断行号是否等于期望值,然后执行我们定义的断点函数,对该行对应的上下文信息,包括 upvalue ,局部变量,还有一些元信息,例如堆栈,进行处理即可。

APISIX 使用的是 Lua 的 JIT 实现:LuaJIT,很多热点代码路径会被编译成机器码执行,而它们是不受 Debug API 的影响的,所以我们需要在开启断点前清空 JIT 缓存。关键就在这里了,我们可以选择只清空某个具体 Lua 函数的 JIT 缓存,减小对全局性能的影响。一个程序运行起来,会有很多 JIT 编译代码块,在 LuaJIT 里被称为 trace,这些 trace 跟 Lua 函数是关联起来的,一个 Lua 函数可能包括多个 trace ,指代函数内不同的热点路径。

对于全局函数、模块级别的函数,我们可以指定它们的函数对象,清空它们的 JIT 缓存。但是如果某行号对应的是其他函数类型,例如匿名函数,我们无法在全局获取函数的对象,那么只能清空所有 JIT 缓存了。在调试开启期间,新的 trace 无法被生成,但是已有的未被清理的 trace 还继续运行,所以只要控制的好,程序性能不会受到影响,因为一个已经运行很久的线上系统,基本不会有新 trace 的生成。当调试结束后,也就是所有断点都被撤销后,系统会恢复正常的 JIT 模式,被清理掉的 JIT 缓存,一旦重新进入热点,会被重新生成 trace。

安装与配置

该插件默认被启用。

配置好 conf/confg.yaml 启用插件:

plugins:
...
  - inspect

plugin_attr:
  inspect:
    delay: 3
    hooks_file: "/usr/local/apisix/plugin_inspect_hooks.lua"

插件默认每隔3秒从文件 /usr/local/apisix/plugin_inspect_hooks.lua 读取断点定义,想调试就编辑该文件即可。

建议创建软链接到该路径,这样比较方便地存档不同历史版本的断点文件。

注意每次该文件的更改时间有变,插件会清空所有旧的断点,并且启用断点文件所定义的所有新断点。断点将在所有工作进程生效。

一般情况下不需要删除该文件,因为定义断点的时候,可以定义什么时候撤销断点。

删除文件会取消所有工作进程的所有断点。

断点的启停都会通过 WARN 日志级别打印日志。

定义断点

require("apisix.inspect.dbg").set_hook(file, line, func, filter_func)
  • file 文件名,可以是任何无歧义的文件名部分,可包含路径
  • line 文件的行号,注意断点跟行号是密切挂钩的,所以如果代码变了,行号就得跟着变。
  • func 要清除哪个函数的 trace,如果为 nil,则清除 luajit vm 里面所有 trace
  • filter_func 处理该断点的自定义 Lua 函数
    • 函数的入参为一个 table,包含以下内容
      • finfo: debug.getinfo(level, "nSlf")的返回值
      • uv: upvalues hash table
      • vals: local variables hash table
    • 函数的返回值为 true,则该断点自动注销,返回为 false,则该断点继续生效

例子:

local dbg = require "apisix.inspect.dbg"

dbg.set_hook("limit-req.lua", 88, require("apisix.plugins.limit-req").access,
function
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

API7.ai 技术团队

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值