最新要闻
- 【新视野】半导体设备板块快速走高,金海通涨停
- 《Redfall》引发粉丝强烈不满
- 环球焦点!《名侦探柯南》新剧场版上映 柯哀亲了登顶热搜!新兰CP党表示要弃剧
- 上海车展全球首发 比亚迪B级纯电SUV官宣:命名“宋L”
- 焦点快看:女子团购住民宿 结果住到自己家的房子 网友:王思聪有这体验
- 【全球快播报】荣耀Magic5成旗舰标杆!赵明:从来没有友商敢挑战它的续航、屏幕
- 环球速看:中标 | 含塔筒3200-3438元/kW!海装、金风、电气风电预中标山东能源渤中海上风电项目
- 全球微头条丨苹果iPhone 15 Pro机型再曝光:依然实体音量键 静音拨片改为Action按钮
- 苹果和安卓折腾了5年 愣是干不过微信小程序
- 抢先看!迪士尼更新真人版《小美人鱼》预告:黑小美人鱼海下展唯美瞬间
- 世界看热讯:标致408保价期内降价遭集体投诉 车主:4S店称地方补贴不算降价
- 【全球聚看点】现场办公面对面听意见解难题 西固区定制化服务助企纾困
- 每日热门:清华、北大在校生五一淄博免费游”?官方回应:消息属实
- 快讯:巴奴“天价土豆”换名后重新上架:多地售价18元一份 北京上海22元
- 云南16岁女孩载21岁女孩无证驾驶摩托车身亡 生前网友曾劝危险
- 天天观热点:张朝阳建议年轻人多做饭、早上6点就起:外卖都是垃圾!王石唱反调
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
详解 APISIX Lua 动态调试插件 inspect
作者罗锦华,API7.ai 技术专家/技术工程师,开源项目 pgcat,lua-resty-ffi,lua-resty-inspect 的作者。
(资料图片)
原文链接
为什么需要 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:... - inspectplugin_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 里面所有 tracefilter_func
处理该断点的自定义 Lua 函数- 函数的入参为一个
table
,包含以下内容finfo
:debug.getinfo(level, "nSlf")
的返回值uv
: upvalues hash tablevals
: 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(info) ngx.log(ngx.INFO, debug.traceback("foo traceback", 3)) ngx.log(ngx.INFO, dbg.getname(info.finfo)) ngx.log(ngx.INFO, "conf_key=", info.vals.conf_key) return trueend)dbg.set_hook("t/lib/demo.lua", 31, require("t.lib.demo").hot2, function(info) if info.vals.i == 222 then ngx.timer.at(0, function(_, body) local httpc = require("resty.http").new() httpc:request_uri("http://127.0.0.1:9080/upstream1", { method = "POST", body = body, }) end, ngx.var.request_uri .. "," .. info.vals.i) return true end return falseend)--- more breakpoints ...
注意到 demo 这个断点,它将一些信息整理后发送到外部的服务器上,使用的 resty.http
库是基于 cosocket
的异步库。
凡是调用 OpenResty 的异步 API ,必须使用 timer 延迟发送,因为在断点上执行函数是同步阻塞的,不会再返回到 nginx 的主程序做异步处理,所以需要延后发送。
使用示例
根据请求体的内容来决定路由
假设我们有个需求,如何设置让某个路由仅接受请求体中携带了 APISIX: 666
的 POST 请求?
路由配置里面有个 vars
字段,是用来检查 nginx 变量的值来判断是否匹配该路由的,而 $request_body
则是 nginx 提供的变量,包含请求体的值,那我们可以利用这个变量来实现我们的需求?
让我们来尝试一下,先配置一下路由:
curl http://127.0.0.1:9180/apisix/admin/routes/var_route \-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -i -d "{ "uri": "/anything", "methods": ["POST"], "vars": [["request_body", "~~", "APISIX: 666"]], "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } }}"
然后我们尝试一下:
curl http://127.0.0.1:9080/anything{"error_msg":"404 Route Not Found"}curl -i http://127.0.0.1:9080/anything -X POST -d "hello, APISIX: 666."HTTP/1.1 404 Not FoundDate: Thu, 05 Jan 2023 03:53:35 GMTContent-Type: text/plain; charset=utf-8Transfer-Encoding: chunkedConnection: keep-aliveServer: APISIX/3.0.0{"error_msg":"404 Route Not Found"}
奇怪,为什么匹配不上这个路由呢?
我们再查看一下 NGINX 对该变量的文档说明:
The variable’s value is made available in locations processed by the proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass directives when the request body was read to a memory buffer.
也就是说,使用该变量前需要先读取 request body 。
那是不是匹配路由的时候,这个变量为空呢?我们可以使用 inspect
插件来验证一下。
我们找到了匹配路由的代码行:
apisix/init.lua
...api_ctx.var.request_uri = api_ctx.var.uri .. api_ctx.var.is_args .. (api_ctx.var.args or "")router.router_http.match(api_ctx)local route = api_ctx.matched_routeif not route then...
我们就在 515 行,也就是 router.router_http.match(api_ctx)
这行验证一下变量 request_body
吧。
设置断点
编辑文件 /usr/local/apisix/example_hooks.lua
:
local dbg = require("apisix.inspect.dbg")dbg.set_hook("apisix/init.lua", 515, require("apisix").http_access_phase, function(info) core.log.warn("request_body=", info.vals.api_ctx.var.request_body) return trueend)
创建软链接到断点文件路径:
ln -sf /usr/local/apisix/example_hooks.lua /usr/local/apisix/plugin_inspect_hooks.lua
检查日志看看确认断点生效:
2023/01/05 12:02:43 [warn] 1890559#1890559: *15736 [lua] init.lua:68: setup_hooks():set hooks: err: true, hooks: ["apisix\/init.lua#515"], context: ngx.timer
再触发一次路由匹配:
curl -i http://127.0.0.1:9080/anything -X POST -d "hello, APISIX: 666."
查看日志:
2023/01/05 12:02:59 [warn] 1890559#1890559: *16152[lua] [string "local dbg = require("apisix.inspect.dbg")..."]:39:request_body=nil, client: 127.0.0.1, server: _,request: "POST /anything HTTP/1.1", host: "127.0.0.1:9080"
果然,request_body
是空的!
解决方案
既然我们知道需要读取请求体才能用 request_body
变量,那么我们就不能通过 vars
来做了,那我们可以通过路由里面的 filter_func
字段来实现需求。
curl http://127.0.0.1:9180/apisix/admin/routes/var_route \-H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -i -d "{ "uri": "/anything", "methods": ["POST"], "filter_func": "function(_) return require(\"apisix.core\").request.get_body():find(\"APISIX: 666\") end", "upstream": { "type": "roundrobin", "nodes": { "httpbin.org": 1 } }}"
验证一下:
curl http://127.0.0.1:9080/anything -X POST -d "hello, APISIX: 666."{ "args": {}, "data": "", "files": {}, "form": { "hello, APISIX: 666.": "" }, "headers": { "Accept": "*/*", "Content-Length": "19", "Content-Type": "application/x-www-form-urlencoded", "Host": "127.0.0.1", "User-Agent": "curl/7.68.0", "X-Amzn-Trace-Id": "Root=1-63b64dbd-0354b6ed19d7e3b67013592e", "X-Forwarded-Host": "127.0.0.1" }, "json": null, "method": "POST", "origin": "127.0.0.1, xxx", "url": "http://127.0.0.1/anything"}
问题解决!
打印一些被日志级别屏蔽的日志
生产环境一般不会开启 INFO
级别的日志,但是有时候我们又需要检查一些详细信息,那怎么办呢?
我们一般不会直接设置 INFO
级别然后 reload,因为这样做有两个缺点:
- 日志太多,影响性能和加大检查难度
- reload 导致长连接被断开,影响在线流量
一般我们只需要检查具体某个点的日志,例如我们都知道 APISIX 使用 etcd 作为配置分发数据库,那么可否看看什么时候路由配置被增量更新到了数据面呢?更新了什么具体数据呢?
apisix/core/config_etcd.lua
local function sync_data(self)... log.info("waitdir key: ", self.key, " prev_index: ", self.prev_index + 1) log.info("res: ", json.delay_encode(dir_res, true), ", err: ", err)...end
增量同步的lua函数是 sync_data()
,但是它是通过 INFO
级别来打印从 etcd watch 到的增量数据的。
那么我们来试一下使用 inspect plugin 来显示一下?只显示路由资源的变化。
编辑 /usr/local/apisix/example_hooks.lua
:
local dbg = require("apisix.inspect.dbg")local core = require("apisix.core")dbg.set_hook("apisix/core/config_etcd.lua", 393, nil, function(info) local filter_res = "/routes" if info.vals.self.key:sub(-#filter_res) == filter_res and not info.vals.err then core.log.warn("etcd watch /routes response: ", core.json.encode(info.vals.dir_res, true)) return true end return falseend)
这个断点处理函数的逻辑很好表达了过滤能力,如果 watch 的 key
是 /routes
,以及 err
为空的情况下,就打印 etcd 返回的数据,并且打印一次就够了,就取消断点。
注意 sync_data()
是局部函数,所以无法获取它的引用,我们只能设置 set_hook
的第三个参数为 nil
,这样做的副作用就是它会清空所有 trace
。
上面例子我们已经创建了软链接,所以编辑后保存文件即可。等几秒钟后,断点就会被启用,可观察日志确认。
检查日志,我们可以得到我们需要的信息,而这些信息用 WARN
日志级别打印,并且也显示了我们在数据面获取到 etcd 增量数据的时间。
2023/01/05 14:33:10 [warn] 1890562#1890562: *231311[lua] [string "local dbg = require("apisix.inspect.dbg")..."]:41:etcd watch /routes response: {"headers":{"X-Etcd-Index":"24433"},"body":{"node":[{"value":{"uri":"\/anything","plugins":{"request-id":{"header_name":"X-Request-Id","include_in_response":true,"algorithm":"uuid"}},"create_time":1672898912,"status":1,"priority":0,"update_time":1672900390,"upstream":{"nodes":{"httpbin.org":1},"hash_on":"vars","type":"roundrobin","pass_host":"pass","scheme":"http"},"id":"reqid"},"key":"\/apisix\/routes\/reqid","modifiedIndex":24433,"createdIndex":24429}]}}, context: ngx.timer
结论
Lua 动态调试是很重要的辅助功能。我们可以通过 APISIX inspect
插件来做很多事情,例如:
- 排查问题,定位原因
- 打印一些被屏蔽的日志,按需获取各种信息
- 通过调试来学习 Lua 代码
更多详情请查阅相关文档介绍。
关于 API7.ai 与 APISIX
API7.ai 是一家提供 API 处理和分析的开源基础软件公司,于 2019 年开源了新一代云原生 API 网关 -- APISIX 并捐赠给 Apache 软件基金会。此后,API7.ai 一直积极投入支持 Apache APISIX 的开发、维护和社区运营。与千万贡献者、使用者、支持者一起做出世界级的开源项目,是 API7.ai 努力的目标。
关键词:
-
详解 APISIX Lua 动态调试插件 inspect
作者罗锦华,API7 ai技术专家 技术工程师,开源项目pgcat,lua-resty-ffi,lua-resty-inspect的作者。...
来源: -
四舍五入 - 逼疯全世界的开发者
一、说明我们先来看一组例子【PYTHON】Python3 6 10(default,Apr62021,21:58:27)[GCC4 8 520150623(RedHat4
来源: 当前时讯:简单的了解下 Fetch API 的工作原理
详解 APISIX Lua 动态调试插件 inspect
四舍五入 - 逼疯全世界的开发者
全球热议:NTP网络校时服务器(北斗GPS校时器)在地铁内网系统中的应用
【新视野】半导体设备板块快速走高,金海通涨停
年内公募新基金发行突破2800亿元 债券型基金占比超六成
《Redfall》引发粉丝强烈不满
环球焦点!《名侦探柯南》新剧场版上映 柯哀亲了登顶热搜!新兰CP党表示要弃剧
上海车展全球首发 比亚迪B级纯电SUV官宣:命名“宋L”
焦点快看:女子团购住民宿 结果住到自己家的房子 网友:王思聪有这体验
【全球快播报】荣耀Magic5成旗舰标杆!赵明:从来没有友商敢挑战它的续航、屏幕
环球速看:中标 | 含塔筒3200-3438元/kW!海装、金风、电气风电预中标山东能源渤中海上风电项目
Spring Boot 接口加解密,新姿势来了!
全球微头条丨苹果iPhone 15 Pro机型再曝光:依然实体音量键 静音拨片改为Action按钮
苹果和安卓折腾了5年 愣是干不过微信小程序
抢先看!迪士尼更新真人版《小美人鱼》预告:黑小美人鱼海下展唯美瞬间
世界看热讯:标致408保价期内降价遭集体投诉 车主:4S店称地方补贴不算降价
【全球聚看点】现场办公面对面听意见解难题 西固区定制化服务助企纾困
天天微动态丨【读财报】消费主题基金年报透视:2022年净值普跌 全年共亏逾280亿
每日热门:清华、北大在校生五一淄博免费游”?官方回应:消息属实
快讯:巴奴“天价土豆”换名后重新上架:多地售价18元一份 北京上海22元
云南16岁女孩载21岁女孩无证驾驶摩托车身亡 生前网友曾劝危险
天天观热点:张朝阳建议年轻人多做饭、早上6点就起:外卖都是垃圾!王石唱反调
全球微头条丨问界M5系列华为高阶智驾版来了:不惧鬼探头 实现泊车“自由”
读SQL进阶教程笔记11_关系数据库基础
天天实时:从消博会透视中国消费活力
每日快播:美股财报季拉开前夕美银泼冷水:美企盈利将从“糟糕”走向“更糟”
【天天报资讯】沙特俄罗斯产油国联手减产 国际油价飙升:美国难受 补充战略石油储备
【世界新要闻】女性同事过生日送什么花
环球快讯:ASP.NET Core MVC 从入门到精通之路由
全国4亿人集体“吃土”?沙尘暴持续下该如何保命
当前动态:都江堰商业街现多只红嘴相思鸟尸体:专家判断系撞玻璃死亡
世界聚焦:菜花蛇、眼镜王蛇、王蛇:谁才真正的毒蛇之王?
【播资讯】国产科幻FPS《边境》Steam褒贬不一:UI稀烂 错误频出
天天速递!微星RTX 4070魔龙深度测试:老黄的刀法太精准了!
环球时讯:老两口搬家花4万,这么多年过去了,搜索引擎“搬家刺客”依然存在?
废弃矿井地下水中多环芳烃的降解与迁移机理
1811 E Living Sequence 两种解法
ChatGPT进化,人性消失,究竟是好是坏?
每日聚焦:第137篇:重学ES6模块化
天天热门:容器操作,应用部署,迁移与备份,Dockerfile
焦点快播:20亿的交易被华为反对 没任何可能合作 诺基亚坚持要卖鼎桥母公司股权
黄仁勋主持 台北电脑展NVIDIA发布会敲定!RTX 4060 Ti/4060要来了
焦点消息!《塞尔达传说:王国之泪》终极预告发布:短发公主绝美 盖侬真身登场
天天微资讯!仿真-定向耦合器
今日报丨java 处理常量字符串过长 & springboot 项目读取 resouces 文件夹下的文件内容
ORACLE还原恢复启动时数据库报ORA-00704, ORA-00604, ORA-00904
世界速读:linux环境下安装Docker
世界关注:百度松果菁英班--oj赛(第二次)
面试题百日百刷-kafka篇(三)
速讯:AMD发布RDNA3架构Radeon Pro W7000系列专业显卡:AI性能提升2.7倍、满血3999美元
天天播报:比“500万内最好的SUV”更好!魏牌蓝山上市:27.38万元起
当前聚焦:纯白机身美翻了!影驰RTX 4070星曜OC图赏
为什么今年沙尘暴突然这么严重?40多年植树造林白干了?
环球播报:79888元!大疆Inspire 3发布:全画幅8K电影无人机
葵花药业实控人杀妻案4年后:两女儿接班
【世界独家】大疆悟3曝光:吊着一台摄像机 支持8K 75fps视频录制
每年减少6吨补给 我国空间站氧气100%再生:已达世界领先水平
环球今日报丨强力透气 森马旗下棉致男士冰丝休闲裤34.5元
当前热点-夜莺监控V6初探
每日关注!双碳ETF: 招商基金管理有限公司关于旗下基金投资关联方承销证券的公告
热讯:苹果寻觅中国制造之外新选择:正推动将MacBook转到泰国组装
今日看点:安卓之光未发先火!超1000位米粉下单小米13 Ultra
巴菲特对苹果推崇至极!出1万美元买不走你的iPhone但能买走你的福特车
当前快报:新能源厂商销量榜 长城、五菱、小鹏汽车全部接近腰斩
环球焦点!飞腾CPU走出国门 非洲电厂将用上“中国芯”
每日关注!Java GC基础知识
天天微资讯!KMP(字符串匹配算法)
焦点快报!银行发现7位数存款5年未动急寻人:联系电话是空号
每日热文:RTX 4090与RTX 3080 Ti移动旗舰显卡大PK:没想到性能差距巨大
自动驾驶为啥这么难!技术问题?还是方向错了?
每日视点!T-1000未来或成真!《终结者》之父:AI太强可以造成世界末日
焦点消息!被狗抓破是否会得狂犬病上热搜:博主“科普”网友纷纷不认账
当前热门:三分钟完成静态网站托管
每日视讯:ChatGPT吃我饭,还要掀我碗
cookies、sessionStorage与localStorage在Vue中的使用
有关拦截器拦截指定的url并作出处理返回数据
环球热点评!皇马这批人如果拿到6个欧冠,历史地位也没变化?受害者只有C罗?
环球短讯!华为5G回来了?余承东体验AITO问界M5智驾版:重大更新引人遐想
疯狂白菜价 DDR5正式跌破百元 国产枭鲸8GB内存只要99.9元
全球观热点:表现持平GPT-4!知乎“知海图AI”投入内测
《终结者》之父卡梅隆:AI太厉害将能造成世界末日
环球速读:云南万人大型泼水现场互送祝福:欢度泼水节
全球即时:全栈混合云综合架构方案研究和落地
环球观热点:详解GaussDB(DWS)的query_band负载识别与应用
快看:零基础快速入门WebRTC:基本概念、关键技术、与WebSocket的区别等
【世界时快讯】Java集成工作流审批机制,多个项目实际运用优化版本(干货)
世界快播:Natasha入门(一)
环球观热点:恒生指数13日收涨0.13% 生物医药行业领涨
焦点报道:许昌市东城区:普法宣传进校园 精心护苗筑平安
【全球时快讯】NVIDIA最新调查:80%的RTX 40系显卡用户会启用光追和DLSS
【天天播资讯】侧透玻璃机箱!微星海皇戟AS台式机上新:i7+RTX 4070
世界新消息丨同跑硬件杀手《赛博朋克2077》 RTX 4080与RTX 4090有多大差距?
世界今日讯!Win掌机不用指头戳屏幕了!Win12有望加入“掌机模式”
世界微动态丨下周一!国内油价预计涨0.44元/升 或创今年最大涨幅
尼尔森IQ:中国消费市场回暖 绿色变革赋能品牌可持续增长
天天速读:MegEngine 使用小技巧:使用 Optimizer 优化参数
每日动态!图片压缩&视频截取
全球观速讯丨Mac | iOS | Windows:安装Stable diffusion教程
世界短讯!科创板收盘播报:科创50指数低开低走跌2.59% 软件服务及半导体股跌幅居前