Apache APISIX ext-plugin-pre-req 插件详解:在 Plugin Runner 中于内置 Lua 插件之前执行外部插件
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
ext-plugin-pre-req 是 Apache APISIX 中负责"外部插件调度"的入口插件之一,其职责是把当前请求交给独立的 Plugin Runner 进程(如 Go / Java / Python 编写的 Runner),在内置 Lua 插件执行之前完成外部插件的处理。本文将以官方文档为主体,结合 插件实现源码、RPC 通信核心 与 测试用例,完整讲解该插件的配置属性、启用/删除方法、底层 RPC 工作原理与降级机制,帮助你快速上手并理解外部插件体系。
插件是什么:执行时机与定位
ext-plugin-pre-req用于在Plugin Runner 中运行指定的外部插件,并且这些外部插件的执行时机早于 APISIX 内置的所有 Lua 插件。
它的定位可以从源码中的两个关键字段直接读出(apisix/plugins/ext-plugin-pre-req.lua):
priority = 12000:插件执行优先级。APISIX 按 priority 从大到小执行插件,12000 高于绝大多数内置插件(例如 key-auth 为 2500),因此该插件会最先拿到请求处理权;rewrite阶段挂钩:插件在rewrite阶段调用ext.communicate(conf, ctx, name),将请求"转发"给外部 Runner 处理,并同步等待处理结果(详见下文"底层原理")。
与它配套的另外两个外部插件(conf/config.yaml.example 中可以看到三者默认注册)分别是:
| 插件 | priority | 挂载阶段 | 作用 |
|---|---|---|---|
ext-plugin-pre-req | 12000 | rewrite | 在内置 Lua 插件之前运行外部插件,可修改请求 |
ext-plugin-post-req | -3000 | access | 在请求阶段(access)之后运行外部插件,如对请求体做二次加工 |
ext-plugin-post-resp | -4000 | access | 将上游响应交给外部插件处理,可改写响应头与响应体 |
:::note 外部插件执行会直接影响当前请求的行为(如修改请求头、请求体、URI,甚至直接终止请求并返回响应),配置前请确认外部插件逻辑符合预期。 :::
更完整的 Plugin Runner 架构与生态说明,可参阅仓库内的 external-plugin 文档。
Attributes 配置属性
插件的 Schema 定义在 apisix/plugins/ext-plugin/init.lua,与官方文档一致:
| 名称 | 类型 | 必填 | 默认值 | 有效值 | 说明 |
|---|---|---|---|---|---|
conf | array | 否 | [{"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"}] | 需要在 Plugin Runner 上执行的外部插件及其配置列表 | |
allow_degradation | boolean | 否 | false | 当 Plugin Runner 不可用时是否开启降级。设为true时允许请求继续(即忽略外部插件处理直接放行) |
结合源码补充说明conf的校验细节:
- 每个数组元素是一个对象,必须同时包含
name与value两个字段(required = {"name", "value"}); name为外部插件名,字符串类型,长度限制为1~128 个字符(minLength = 1, maxLength = 128);value为外部插件的配置,字符串类型,通常以 JSON 字符串形式传入(示例中的"{\"enable\":\"feature\"}"即一个 JSON 配置串);- 整个
conf数组至少包含1 个元素(minItems = 1),即至少要指定一个要执行的外部插件; allow_degradation为布尔值,默认false。
前置条件:先让 APISIX 认识 Plugin Runner
ext-plugin-pre-req本身不包含业务逻辑,它只是 APISIX 与 Plugin Runner 之间的"信使"。要让它生效,必须先配置 Plugin Runner。
生产环境:由 APISIX 托管 Runner
在conf/config.yaml中加入以下配置,APISIX 会把 Runner 作为自己的子进程启动、管理与重启:
ext-plugin: cmd: ["/path/to/your/runner"] # 替换为实际 Runner 可执行文件路径开发调试:Runner 独立运行
开发时往往希望单独启动 Runner、避免每次重启 APISIX,可通过环境变量APISIX_LISTEN_ADDRESS强制 Runner 监听固定地址:
APISIX_LISTEN_ADDRESS=unix:/tmp/x.sock ./the_runner同时在config.yaml中让 APISIX 把 RPC 发往该固定地址:
ext-plugin: # cmd: ["blah"] # 独立运行模式下不要配置 cmd path_for_test: "/tmp/x.sock" # 注意不带 'unix:' 前缀这条路径的解析逻辑在 apisix/plugins/ext-plugin/helper.lua:若配置了path_for_test则使用unix:前缀拼接该路径;否则默认使用动态生成的./conf/apisix-<master_pid>.sock。生产环境不应使用path_for_test。
启用插件:在 Route 上绑定
下面示例为指定 Route 启用ext-plugin-pre-req插件(conf中声明了一个名为ext-plugin-A的外部插件及其 JSON 配置)。
:::note 先从config.yaml中取出admin_key并保存到环境变量,便于后续调用 Admin API:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g'):::
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-pre-req": { "conf" : [ {"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"} ] } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:1980": 1 } } }'配置完成后,访问该路由的请求即会触发 APISIX 与 Plugin Runner 之间的 RPC 调用。
示例使用与验证
启用上述配置后,发起如下请求:
curl -i http://127.0.0.1:9080/index.html该请求会到达已配置的 Plugin Runner,ext-plugin-A将在请求进入内置 Lua 插件阶段之前被依次执行,执行结果(可能包括改写后的请求头、URI,或直接生成的响应)会返回给 APISIX 继续处理。
仓库测试 t/plugin/ext-plugin/sanity.t 给出了同样的验证路径:先通过 Admin API 为/hello路由绑定带conf的ext-plugin-pre-req,再发起GET /hello请求,并校验 Runner 端收到的 conf token 符合^route#1#ext-plugin-pre-req#模式——这印证了每个路由上每个外部插件都会在 Runner 侧生成独立的配置标识。
底层原理:RPC 通信与 Runner 生命周期
ext-plugin-pre-req的rewrite阶段调用的是 apisix/plugins/ext-plugin/init.lua 中的ext.communicate,其核心流程如下:
- 建立连接:通过 Unix Socket 连接 Plugin Runner(rpc_call),设置 1s 连接超时与 60s 读写超时,成功后复用连接(keepalive 180s)。
- PrepareConf(配置协商):首次请求时发送
RPC_PREPARE_CONF,把conf数组中的外部插件名与配置逐项编码为 FlatBuffers 结构发送给 Runner;Runner 返回一个conf token。该 token 缓存在共享内存字典与 LRU 缓存中(token 缓存有效期见 helper.lua,默认 3600 秒),后续请求直接复用,避免重复协商。 - HTTPReqCall(请求调用):携带 token、方法、URI、查询参数、请求头、客户端地址等发起
RPC_HTTP_REQ_CALL(init.lua)。期间 Runner 可通过RPC_EXTRA_INFO反向向 APISIX 询问 Nginx 变量、请求体、响应体等额外信息。 - 处理 Runner 的返回动作:
- Stop:外部插件直接终止请求,按 Runner 给定的状态码与响应体生成响应返回客户端(默认状态码 200);
- Rewrite:把 Runner 改写后的 path、headers、args、body 写回当前请求(例如通过
ngx.req.set_body_data替换请求体,改写upstream_uri与upstream_host),并同步更新响应头。
- Runner 生命周期管理:Runner 由 privileged agent 进程以子进程方式拉起(init_worker),退出后 3 秒自动重新拉起,并在退出事件中清空 conf token 缓存;APISIX 退出时先发 SIGTERM、等待 1 秒后再强制结束(exit_worker)。
失败重试与降级行为
communicate内置最多 3 次重试(init.lua):
- 若因 conf token 失效导致失败,会先刷新 token 缓存后重试;
- 重试仍失败时,根据
allow_degradation决定行为:allow_degradation = true:记录 warning 并允许请求继续(跳过外部插件);allow_degradation = false(默认):返回 HTTP503 Service Unavailable。
环境变量传递
若 Runner 需要访问自定义环境变量,需先在conf/config.yaml的nginx_config.envs中显式声明(Nginx 默认隐藏全部环境变量):
nginx_config: envs: - MY_ENV_VAR删除插件
删除插件只需把对应 JSON 配置从 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 } } }'小结与注意事项
ext-plugin-pre-req(priority 12000)在 rewrite 阶段、早于内置 Lua 插件执行外部插件,与ext-plugin-post-req(-3000)、ext-plugin-post-resp(-4000)共同构成完整的外部插件处理链路;conf数组元素必须包含name(1~128 字符)与value(配置 JSON 字符串),且至少一个元素;- 未配置 Plugin Runner 或 Runner 异常时,默认返回 503;通过
allow_degradation: true可切换为放行降级模式; - 生产环境使用
ext-plugin.cmd让 APISIX 托管 Runner,开发调试可用path_for_test+APISIX_LISTEN_ADDRESS固定通信地址; - 删除插件配置后热生效,无需重启 APISIX。
若希望进一步了解外部插件体系(Runner 的生态、实现细节与 FAQ),请继续阅读仓库内的 external-plugin 文档,并结合 RPC 核心实现 与 插件测试套件 深入探索。
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考