Apache APISIX ext-plugin-post-req 插件详解:在内置 Lua 插件之后、代理上游之前运行外部插件

发布时间:2026/9/15 4:17:26
Apache APISIX ext-plugin-post-req 插件详解:在内置 Lua 插件之后、代理上游之前运行外部插件 Apache APISIX ext-plugin-post-req 插件详解在内置 Lua 插件之后、代理上游之前运行外部插件【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisixext-plugin-post-req是 Apache APISIX 外部插件External Plugin体系中的核心成员它允许以 Go、Java、Python 等语言开发的插件在内置 Lua 插件执行完毕之后、请求被代理到 Upstream 之前介入请求处理。本文以仓库文档 docs/en/latest/plugins/ext-plugin-post-req.md 为骨架结合插件源码 apisix/plugins/ext-plugin-post-req.lua、外部插件运行时实现 apisix/plugins/ext-plugin/init.lua 与测试用例 t/plugin/ext-plugin/http-req-call.t完整讲解它的执行时机、配置方式、Plugin Runner 通信原理、降级容错与实战验证方法帮助你准确选择ext-plugin-pre-req/ext-plugin-post-req/ext-plugin-post-resp的组合时机。一、插件定位与执行时机官方文档对ext-plugin-post-req的定位非常明确ext-plugin-post-req与ext-plugin-pre-req插件的区别在于它在内置 Lua 插件执行之后、代理到 Upstream 之前运行。这意味着在一个请求的完整生命周期中外部插件的执行点分布如下以 HTTP 子系统为例优先级定义见 conf/config.yaml.example插件优先级执行时机ext-plugin-pre-req12000在内置 Lua 插件之前执行可抢先修改请求各类内置 Lua 插件11000 ~ -2000按各自 priority 依次执行ext-plugin-post-req-3000在内置 Lua 插件之后、代理到 Upstream之前执行ext-plugin-post-resp-4000在收到 Upstream 响应之后执行处理响应可以看到ext-plugin-post-req与ext-plugin-pre-req形成了内置插件两侧的对称结构。选择哪个取决于你的业务诉求需要抢在内置插件如认证、限流之前执行例如自定义鉴权、请求拦截选择ext-plugin-pre-req需要在内置插件处理完成之后、最终确认请求内容再发给上游例如基于内置插件改写结果的二次处理、请求签名选择ext-plugin-post-req需要处理上游响应例如响应改写、日志增强选择ext-plugin-post-resp。关于配置方式官方文档明确说明可以完整参考ext-plugin-pre-req插件的文档即 docs/en/latest/plugins/ext-plugin-pre-req.md下文第二节将完整展开其参数与启用步骤。二、配置参数详解ext-plugin-post-req与ext-plugin-pre-req共用同一套 schema——两者在源码中都直接复用apisix.plugins.ext-plugin.init暴露的schema见 apisix/plugins/ext-plugin-post-req.lua 中的schema ext.schema。参数定义如下名称类型必填默认值合法取值描述confarray否[{name: ext-plugin-A, value: {\enable\:\feature\}}]需要在 Plugin Runner 上执行的外部插件及其配置列表allow_degradationboolean否false当 Plugin Runner 不可用时是否允许插件降级。设为true时请求被放行继续处理conf数组中的每一项都是一个对象必须同时包含name与value两个字段。从底层 schema 实现见 apisix/plugins/ext-plugin/init.lua可以看到更精确的约束name字符串minLength 1、maxLength 128对应 Plugin Runner 中注册的外部插件名value字符串通常是一段 JSON 序列化后的插件配置如{enable:feature}conf数组本身要求minItems 1。仓库测试 t/plugin/ext-plugin/sanity.t 中专门有一条 schema 校验失败的用例错误信息为failed to check the configuration of plugin ext-plugin-post-req err: property conf validation failed: failed to validate item 1: property value is required这说明如果conf中的某一项缺少value字段Admin API 会直接拒绝该配置——这是可以依托源码验证的硬性约束。allow_degradation的底层行为由 apisix/plugins/ext-plugin/init.lua 的_M.communicate函数实现当 RPC 调用失败且该字段为false时请求直接返回503为true时则记录告警日志并放行请求详见第五节。三、启用插件ext-plugin-post-req与其它 APISIX 插件一样通过 Admin API 在 Route 上启用并且支持热更新无需重启 APISIX。3.1 获取 Admin Key在配置前先从本地配置文件中取出admin_key配置文件路径为 conf/config.yaml示例模板见 conf/config.yaml.exampleadmin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g)3.2 在 Route 上启用下面的示例在 Route/index.html上启用ext-plugin-post-req要求 Plugin Runner 执行名为ext-plugin-A的外部插件并传入配置{enable:feature}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-req: { conf : [ {name: ext-plugin-A, value: {\enable\:\feature\}} ] } }, upstream: { type: roundrobin, nodes: { 127.0.0.1:1980: 1 } } }要点说明Admin API 默认监听9180端口客户端请求默认走9080端口conf数组可同时列出多个外部插件Plugin Runner 将按数组顺序执行若省略conf只配置空对象{}插件仍然可用——测试用例 t/plugin/ext-plugin/http-req-call.t 中就是这样与proxy-rewrite组合使用的用于验证 post-req 阶段能够拿到内置插件改写后的请求状态。3.3 发送请求验证配置完成后直接请求即可触发外部插件执行curl -i http://127.0.0.1:9080/index.html请求到达 APISIX 后会经过内置 Lua 插件阶段随后在access阶段触发ext-plugin-post-req通过 Unix Socket 向 Plugin Runner 发起 RPC 调用ext-plugin-A随即被执执行执行结果返回给 APISIX 后再继续代理到 Upstream。四、工作原理外部插件与 Plugin Runner4.1 为什么需要外部插件APISIX 的原生插件使用 Lua 编写并运行在 APISIX 进程内部。当开发者希望使用其它语言Go、Java、Python、JavaScript 等实现业务逻辑时APISIX 提供了**Plugin Runner插件运行器**这种 sidecar 形态的进程Runner 加载你的外部插件当请求命中配置了ext-plugin-*插件的 Route 时APISIX 通过 Unix Socket 向其发起 RPC 调用Runner 在自身一侧构造出虚拟请求执行外部插件后把结果返回给 APISIX详见 docs/en/latest/external-plugin.md。4.2 配置 Plugin Runner要让ext-plugin-post-req真正工作首先需要在 conf/config.yaml 中声明 Runner 的启动命令ext-plugin: cmd: [blah] # 替换为所选 Runner 的实际可执行文件与参数APISIX 会将 Runner 作为自己的子进程启动与 APISIX 同属一个用户并在 APISIX 重启/热加载时同步重启 Runner。配置模板中的示例为cmd: [ls, -l]见 conf/config.yaml.example。在开发调试场景下你可能希望 Runner 独立运行、独立重启。此时可以通过环境变量APISIX_LISTEN_ADDRESS固定 Runner 的监听地址APISIX_LISTEN_ADDRESSunix:/tmp/x.sock ./the_runner在 APISIX 配置中指向该固定地址此时不要再配置cmd避免 APISIX 再次拉起 Runnerext-plugin: # cmd: [blah] # 不要配置可执行命令 path_for_test: /tmp/x.sock # 不含 unix: 前缀从源码 apisix/plugins/ext-plugin/helper.lua 可以看到Unix Socket 路径的解析逻辑为优先读取本地配置中的ext-plugin.path_for_test否则动态生成为./conf/apisix-master_pid.sock。path_for_test仅用于开发/测试环境生产环境不配置它由 APISIX 动态生成 socket 路径。4.3 RPC 通信协议APISIX 与 Runner 之间采用 FlatBuffers 序列化的二进制 RPC 协议RPC 类型常量定义在 apisix/constants.lua常量值含义RPC_ERROR0错误响应RPC_PREPARE_CONF1下发插件配置换取ConfTokenRPC_HTTP_REQ_CALL2请求阶段 RPC 调用RPC_EXTRA_INFO3附加信息交互如读取请求体、响应体、Nginx 变量RPC_HTTP_RESP_CALL4响应阶段 RPC 调用一次典型的ext-plugin-post-req交互流程为对应 apisix/plugins/ext-plugin/init.lua 中的rpc_call与rpc_handlersPrepareConfAPISIX 将conf数组外部插件名 配置发送给 RunnerRunner 返回一个ConfTokenAPISIX 将其缓存在共享内存ext-pluginshdict与插件级 LRU 缓存中token 缓存时间默认 3600 秒见 apisix/plugins/ext-plugin/helper.lua后续请求直接复用减少 RPC 次数HTTPReqCallAPISIX 把请求的方法、URI、Args、Headers、客户端 IP 等封装后发给 RunnerExtraInfo 往返如果 Runner 需要读取请求体、响应体或 Nginx 变量会通过RPC_EXTRA_INFO向 APISIX 查询APISIX 通过handle_extra_info返回读取请求体走core.request.get_body()读 Nginx 变量走ctx.var[var_name]返回结果Runner 返回Stop直接终止请求可自定义状态码与响应体或Rewrite改写路径、请求头、请求体、URI 参数、响应头等 ActionAPISIX 据此落地修改。需要说明的是ext-plugin-post-req与ext-plugin-pre-req的底层 RPC 类型都是RPC_HTTP_REQ_CALL区别仅在于挂载的执行阶段不同pre-req在内置插件前触发post-req在内置插件后触发——因此 post-req 阶段看到的请求状态例如被proxy-rewrite改写后的upstream_uri、upstream_host已经包含内置插件的处理结果。五、降级与容错机制外部插件体系对 Runner 故障有完善的容错设计核心逻辑在 apisix/plugins/ext-plugin/init.lua 的communicate失败重试每次 RPC 调用最多尝试 3 次当错误信息包含conf token not found时说明配置 token 缓存失效APISIX 会刷新 LRU 缓存与共享内存中的 token 后重试recreate_lrucache会调用shdict:flush_all()降级开关重试仍失败时若allow_degradation true则记录告警日志并放行请求降级否则直接返回503Runner 生命周期Runner 由 privileged agent 启动_M.init_worker进程退出后会通过事件机制触发缓存清理并在 3 秒后自动重新拉起APISIX 退出时先向 Runner 发送SIGTERM等待 1 秒后仍未退出再SIGKILL见 apisix/plugins/ext-plugin/init.lua。因此在实际生产部署中如果外部插件只是增强性逻辑如附加观测数据建议开启allow_degradation避免 Runner 故障拖垮主链路如果外部插件是强依赖如强制鉴权则应保持默认false让请求在 Runner 不可用时快速失败。六、删除插件删除ext-plugin-post-req只需从 Route 的plugins配置中移除对应 JSON 块。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 } } }七、源码实现速览ext-plugin-post-req的完整实现非常精简主体只有 40 行apisix/plugins/ext-plugin-post-req.luaversion 0.1priority -3000决定其在内置插件之后执行schema ext.schema复用外部插件通用 schemacheck_schema通过core.schema.check做配置校验access(conf, ctx)在access阶段调用ext.communicate(conf, ctx, name)name 即ext-plugin-post-req作为本次 RPC 的配置唯一标识用于ConfToken的缓存 key。仓库测试 t/plugin/ext-plugin/http-req-call.t 提供了可复现的验证场景在 Route 上同时配置proxy-rewrite把 host 改写为test.com与ext-plugin-post-req空配置随后请求/hello用于验证 post-req 阶段 Runner 收到的请求已经携带内置插件改写后的 host 信息——这正是内置插件之后这一时序特性的直接证据。八、常见问题Q1APISIX 托管的 Runner 访问不到我的环境变量Nginx 默认会隐藏所有环境变量需要在 conf/config.yaml 中显式声明后 APISIX 才能传给 Runnernginx_config: envs: - MY_ENV_VARQ2为什么 Runner 有时收到 SIGKILL 而不是 SIGTERMAPISIX 会先向 Runner 发送SIGTERM请求优雅退出等待 1 秒后若进程仍在运行为保证进程组资源被释放再发送SIGKILL。Q3ext-plugin-post-req与ext-plugin-post-resp有什么区别前者在代理到 Upstream 之前处理请求后者在收到 Upstream 响应之后处理响应。两者优先级分别为-3000与-4000前者先执行。Q4可以同时配置多个外部插件吗可以。conf是数组可列出多个{name, value}项Plugin Runner 按数组顺序依次执行。九、小结ext-plugin-post-req以priority -3000的挂载点让非 Lua 语言编写的插件在内置 Lua 插件处理完成之后、请求代理到上游之前介入与ext-plugin-pre-req、ext-plugin-post-resp一起构成完整的外部插件三段式执行模型。配置上它复用了外部插件统一的conf/allow_degradation参数体系运行时依赖 Plugin Runner 进程与基于 FlatBuffers 的 Unix Socket RPC 协议并内置了 token 缓存、失败重试与降级放行等容错机制。需要进一步深入时建议按以下路径阅读仓库插件入口与实现apisix/plugins/ext-plugin-post-req.lua外部插件运行时RPC、token、Runner 生命周期apisix/plugins/ext-plugin/init.luaSocket 路径与缓存策略apisix/plugins/ext-plugin/helper.luaRPC 类型常量apisix/constants.lua外部插件整体概念docs/en/latest/external-plugin.md前置插件配置同源docs/en/latest/plugins/ext-plugin-pre-req.md验证用例t/plugin/ext-plugin/http-req-call.t【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考