Apache APISIX ext-plugin-post-resp 插件:在响应阶段运行外部插件(External Plugin Runner)的完整实践指南
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
ext-plugin-post-resp是 Apache APISIX 中用于在请求获得上游响应之后、将响应交还给客户端之前,调度外部插件(External Plugin)执行的官方插件。它依托 Plugin Runner(进程外插件运行器)机制,让开发者可以用 Go、Java、Python 等非 Lua 语言编写的插件处理并改写上游返回的状态码、响应头和响应体。阅读本文后,你将掌握该插件的执行时机、完整配置属性、启用与删除方式、降级策略,以及其底层基于 Unix Socket 的 RPC 通信原理。
一、功能概述与执行时机
ext-plugin-post-resp的核心定位是:在请求从上游(upstream)拿到响应之后,执行配置好的外部插件,并允许这些插件影响当前请求的最终响应。官方文档明确提示:外部插件的执行会影响当前请求的响应结果(文档原文)。
与之相对的是ext-plugin-pre-req插件:后者在请求阶段(rewrite 阶段)执行外部插件,用于改写请求本身。二者共同构成 APISIX 在请求前、响应后两个关键节点上挂载外部插件能力的基础设施。
从源码实现看,apisix/plugins/ext-plugin-post-resp.lua 中该插件的定义如下:
local name = "ext-plugin-post-resp" local _M = { version = 0.1, priority = -4000, name = name, schema = ext.schema, }priority = -4000意味着它在所有插件中属于极低优先级,从而保证在正常请求/响应流程完成之后才介入处理,符合"响应后处理"的语义。插件通过before_proxy钩子完成响应获取、RPC 调用与响应回写(源码)。
二、工作原理:从上游取响应到 Plugin Runner 的 RPC 调用
要理解ext-plugin-post-resp,需要先理解外部插件(External Plugin)与插件运行器(Plugin Runner)的概念:APISIX 将外部插件作为子进程(sidecar)运行,这些子进程被称为 Plugin Runner(外部插件文档)。
ext-plugin-post-resp的完整执行链路如下(依据 apisix/plugins/ext-plugin-post-resp.lua 源码):
- 建立上游连接并取回响应:
get_response函数使用lua-resty-http库(local http = require("resty.http"))直接与 APISIX 已选中的上游节点(ctx.picked_server.host/port)建立 TCP 连接,携带原始请求的方法、路径、查询参数、请求头和请求体发起请求,得到响应对象res。 - 发送响应调用 RPC:将上游响应(状态码 + 响应头)通过
ext.communicate(conf, ctx, name, constants.RPC_HTTP_RESP_CALL)发给 Plugin Runner,其中RPC_HTTP_RESP_CALL即"响应阶段调用"的 RPC 类型(见 apisix/constants.lua 中定义的RPC_PREPARE_CONF=1、RPC_HTTP_REQ_CALL=2、RPC_EXTRA_INFO=3、RPC_HTTP_RESP_CALL=4)。 - 外部插件返回结果:Plugin Runner 执行外部插件后返回状态码(
code)和响应体(body)。若body非空,说明外部插件改写了响应体,直接以返回的code、body作为最终响应返回(源码)。 - 回写原始响应:若外部插件未改动响应体,则
send_response会通过ngx.print/ngx.flush将上游响应体分块写给客户端,同时应用外部插件可能修改的状态码(源码)。
通信本身基于Unix Socket + FlatBuffers 二进制协议:APISIX 与 Plugin Runner 之间按"1 字节类型 + 3 字节大端长度 + 数据体"的帧格式收发消息,并支持通过RPC_EXTRA_INFO交互式地按需传递请求/响应变量、请求体、响应体等额外信息(详见 apisix/plugins/ext-plugin/init.lua 的send/receive与handle_extra_info)。
值得注意的实现细节是:ext-plugin-post-resp 是通过 APISIX 主动向上游再发起一次 HTTP 请求来获取响应内容(而非直接复用 Nginx 内置的代理响应流),这正是文档中"该插件使用 lua-resty-http 库向上游发送请求"(文档原文)的技术背景。
三、属性(Attributes)说明
插件仅有两个配置属性,官方文档给出的属性表如下:
| Name | Type | Required | Default | Valid values | Description |
|---|---|---|---|---|---|
| conf | array | False | [{"name": "ext-plugin-A", "value": "{"enable":"feature"}"}] | List of Plugins and their configurations to be executed on the Plugin Runner. | |
| allow_degradation | boolean | False | false | Sets Plugin degradation when the Plugin Runner is not available. When set totrue, requests are allowed to continue. |
结合 apisix/plugins/ext-plugin/init.lua 中的 JSON Schema 定义,可以补充更多校验细节:
- conf:类型为数组,
minItems = 1(至少配置一个外部插件)。每个元素为对象,必填字段为name与value:name:外部插件名,字符串,minLength = 1、maxLength = 128;value:传给该外部插件的配置,字符串类型,典型用法是传入一段 JSON 字符串(如{"enable":"feature"})。Plugin Runner 会收到这些 name/value 对,并据此加载并初始化对应的外部插件。
- allow_degradation:布尔值,默认
false。控制当 Plugin Runner 不可用时是否允许降级放行(详见下文"降级策略"小节)。
四、配置 Plugin Runner
在启用ext-plugin-*插件之前,必须先在conf/config.yaml中配置 Plugin Runner 的启动命令(外部插件文档):
ext-plugin: cmd: ["blah"] # 替换为所选 Runner 的实际可执行文件,例如 Go Runner 的二进制路径APISIX 会将 Runner 作为自己的子进程管理:重启或 reload APISIX 时,Runner 也会随之重启;Runner 意外退出后 APISIX 会等待 3 秒自动重新拉起(源码 的setup_runner与重试逻辑)。
开发调试阶段,可以通过环境变量APISIX_LISTEN_ADDRESS让 Runner 监听固定地址,并在 APISIX 配置中通过path_for_test指向它,从而无需重启 APISIX 即可单独重启 Runner:
APISIX_LISTEN_ADDRESS=unix:/tmp/x.sock ./the_runnerext-plugin: # cmd: ["blah"] # 开发模式不要配置可执行文件! path_for_test: "/tmp/x.sock" # 不带 'unix:' 前缀生产环境不应使用path_for_test,Unix Socket 路径会由 APISIX 动态生成(默认形如./conf/apisix-<master_pid>.sock,见 apisix/plugins/ext-plugin/helper.lua 的get_path)。
五、启用插件
以下示例在一条 Route 上启用ext-plugin-post-resp插件,并配置一个名为ext-plugin-A的外部插件(文档原文)。
首先,可从conf/config.yaml中取出admin_key并保存为环境变量:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')然后通过 Admin API 创建/更新路由:
curl -i http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "uri": "/index.html", "plugins": { "ext-plugin-post-resp": { "conf" : [ {"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"} ] } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'说明:
- Admin API 默认监听
9180端口;/apisix/admin/routes/1表示创建 ID 为 1 的路由; conf中的每一项对应一个要执行的外部插件及其配置;- 请求到达该路由后,APISIX 会向上游
127.0.0.1:1980发请求,并把拿到的响应交给 Plugin Runner 中的ext-plugin-A处理。
六、示例使用与验证
配置完成后,直接请求网关即可触发外部插件执行(文档原文):
curl -i http://127.0.0.1:9080/index.html请求会到达已配置的 Plugin Runner,ext-plugin-A随即被执行。
仓库中的测试用例 t/plugin/ext-plugin/response.t 验证了该插件在响应阶段的能力矩阵:
- 修改响应体:
modify_body = true时,上游返回的hello world被外部插件改写为cat,客户端最终收到200与改写后的响应体(TEST 3); - 修改响应头:
modify_header = true时,外部插件设置X-Runner: Test-Runner等响应头(TEST 4);同一响应头出现多次时会以逗号合并(TEST 5,X-Same: one, two); - 修改状态码:
modify_status = true时,外部插件将状态码改为304(TEST 6)。
此外,t/plugin/ext-plugin/extra-info.t 中同样包含基于ext-plugin-post-resp的用例(TEST 6),验证外部插件按需读取请求变量、请求体与响应体等附加信息的 RPC 交互路径。
七、降级策略(allow_degradation)
当 Plugin Runner 不可用(进程未启动、Socket 连接失败、RPC 返回错误等)时,插件默认会让请求失败。从 apisix/plugins/ext-plugin/init.lua 的communicate实现可以看到完整逻辑:
- 每次调用最多重试3 次;如果错误是"conf token not found"(配置令牌缓存失效),会先刷新缓存再重试;
- 若重试后仍失败:
allow_degradation = false(默认):返回503,请求失败;allow_degradation = true:打印Plugin Runner is wrong, allow degradation警告后直接放行,请求继续正常处理,响应不再经过外部插件。
在 Route 上启用降级的配置示例:
{ "uri": "/index.html", "plugins": { "ext-plugin-post-resp": { "allow_degradation": true, "conf": [ {"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"} ] } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }降级模式适合将外部插件视为"尽力而为"增强能力的场景:Runner 故障时宁可牺牲插件功能,也要保证业务请求不中断。对应地,t/plugin/ext-plugin/response.t 中的 TEST 7 及后续用例专门覆盖了allow_degradation默认值及降级行为。
八、与其他插件的兼容性限制
由于ext-plugin-post-resp使用lua-resty-http自行向上游发起请求(文档原文),以下能力无法与它同时使用:
- proxy-control:控制代理行为的插件;
- proxy-mirror:请求镜像插件;
- proxy-cache:代理缓存插件;
- APISIX 与上游之间的 mTLS(APISIX 与上游的 mTLS)暂不支持。
原因在于:上述插件依赖 Nginx 内置的代理/缓存管线,而ext-plugin-post-resp绕开了这条管线、改由 Lua 层的 HTTP 客户端直接访问上游,因此相关能力无法生效。在规划使用该插件时,应避免在同一条路由上同时配置上述插件。
另外需要留意:外部插件的执行会直接影响当前请求的响应(文档中以:::note特别提示),因此建议仅在确实需要对响应做二次加工的场景(如响应体脱敏、响应头注入、灰度标记、内容改写)中使用。
九、删除插件
移除ext-plugin-post-resp插件只需把 Route 配置中的plugins字段删掉即可,APISIX 会自动热加载生效,无需重启(文档原文):
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "uri": "/index.html", "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'删除后,请求将直接按正常代理流程转发,不再触发 Plugin Runner 的响应阶段 RPC 调用。
十、小结
ext-plugin-post-resp是 APISIX 外部插件体系在响应方向上的关键一环:它以极低优先级在请求获得上游响应后介入,通过 Unix Socket 与进程外的 Plugin Runner 通信,让 Go、Java、Python、JavaScript 等语言编写的插件能够改写响应状态码、响应头与响应体。配套的allow_degradation属性提供了 Runner 故障时的优雅降级能力。建议在路由规划时注意其与 proxy-control / proxy-mirror / proxy-cache 及上游 mTLS 的兼容性边界,并参考 外部插件文档 与仓库测试用例 t/plugin/ext-plugin/response.t 理解其行为细节。
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考