Serverless 函数还是自定义插件
在 API7 网关中,你有两种方式在请求生命周期里运行自己的 Lua 逻辑:内置的 serverless 函数插件,以及自定义 Lua 插件。两者都执行你编写的 Lua,对网关运行时拥有相同的访问权限;区别在于代码的封装、配置、治理和复用方式。
一条经验法则:小而一次性的片段用 serverless 函数,任何可复用或复杂的逻辑都用自定义插件。
Serverless 函数
serverless-pre-function 和 serverless-post-function 插件运行你直接粘贴到路由、服务、消费者或全局规则里的内联 Lua。无需上传或注册——代码就是资源配置的一个字段。完整配置见 Serverless Functions 插件参考。
每个实例在单个 phase 中运行一组 functions。每个函数都是一个返回另一个函数的 Lua 字符串。返回的函数会接收插件配置和请求上下文,因此其完整签名 是 function(conf, ctx),但也可以忽略它们、不接收任何参数:
{
"plugins": {
"serverless-pre-function": {
"phase": "access",
"functions": [
"return function(conf, ctx) local core = require('apisix.core'); core.request.set_header(ctx, 'X-Trace-ID', 'demo') end"
]
}
}
}
phase—— 运行所在的单个阶段:rewrite、access、header_filter、body_filter、log或before_proxy,默认access。functions—— Lua 源码字符串数组,每个返回一个function(conf, ctx)。它们按顺序执行;若某个返回了状态码或 body,请求即短路。
serverless-pre-function 运行较早(优先级 10000),serverless-post-function 运行较晚(优先级 -2000),因此它们通常用于在某个阶段的最开始或最末尾注入逻辑。
自定义插件
自定义插件是你从 Dashboard 上传一次的 Lua 模块。控制面会解析它的 name、version 和 schema,把它下发到你选择的网关组,之后它就像内置插件一样出现在插件选择器中。你通过带类型的字段来配置它,而不是粘贴代码。
local core = require("apisix.core")
local schema = {
type = "object",
properties = {
header_name = { type = "string", default = "X-Trace-ID" },
header_value = { type = "string" },
},
required = { "header_value" },
}
local _M = {
version = 0.1,
priority = 1500,
name = "custom-header",
schema = schema,
}
function _M.check_schema(conf)
return core.schema.check(schema, conf)
end
function _M.access(conf, ctx)
core.request.set_header(ctx, conf.header_name, conf.header_value)
end
return _M
对比
| 维度 | Serverless 函数 | 自定义插件 |
|---|---|---|
| 代码所在 | 内联,位于每个资源的配置中 | 一个上传到控制面的命名模块 |
| 复用 | 复制粘贴到每个需要它的资源 | 注册一次,可在任意已部署的网关组中按名称引用 |
| 配置 | 无——参数硬编码在 Lua 字符串里 | 由 JSON schema 校验、带默认值的类型化字段 |
| 名称与版本 | 无 | 会被解析并强制(name、version) |
| 优先级 | 固定:10000(较早)或 -2000(较晚) | 由插件设定(priority 字段) |
| 阶段 | 每个实例一个 phase | 一个模块内可覆盖全部六个阶段 |
| 发布控制 | 跟随它所附着的资源 | 下发到所选的网关组;可从测试推广到生产 |
| 评审与治理 | 在每个被粘贴处分别评审 | 集中上传并版本化,受 RBAC 治理 |
| 可测试性 | 困难——逻辑是配置里的转义字符串 | 可作为模块测试(luac -p、路由测试) |
| 运行时权限 | 完整网关运行时 | 完整网关运行时 |
| 最佳适用 | 小而一次性的片段和快速试验 | 可复用、复杂的生产逻辑 |
两种机制运行时的权限相同——都没有沙箱。无论选择哪种,都要以同样的谨慎评审代码。
何时使用 Serverless 函数
以下情况适合 serverless 函数:
- 小而自包含 —— 几行代码用于设置 header、调整变量或输出一条日志。
- 只针对单个资源 —— 你不打算在别处复用它。
- 快速试验 —— 想先试一下,而不走上传并注册的流程。
何时使用自定义插件
只要逻辑不再是琐碎的,就选自定义插件。特别是当你需要:
- 配置。 带默认值、类型化并经校验的
schema,让运维通过字段而非编辑 Lua 来配置插件。这是两者最大的实际差异。 - 复用。 一个命名插件应用到许多路由和服务,只在一处更新。
- 可控发布。 部署到测试网关组,验证后再加入生产网关组。见在网关组之间灰度发布。
- 多阶段或特定优先级。 例如同时跨越
access和log,或必须相对其他插件运行在某个特定位置的逻辑。 - 依赖。 位于包搜索路径上的共享辅助模块或原生库。见添加依赖库。
- 评审与生命周期管理。 集中上传、版本化和 RBAC,而不是散落在各资源配置里的代码。
对于复杂场景,推荐使用自定义插件:它们让逻辑保持可复用、可测试、可配置且受治理,而 serverless 函数则让小片段贴近它所作用的资源。
相关文档
- 添加自定义插件 —— 构建、注册和测试自定义插件。
- 插件开发最佳实践 —— 编写正确、高性能的插件代码。
- Serverless Functions —— serverless 函数插件参考。