跳到主要内容
版本:3.10.x

Serverless 函数还是自定义插件

在 API7 网关中,你有两种方式在请求生命周期里运行自己的 Lua 逻辑:内置的 serverless 函数插件,以及自定义 Lua 插件。两者都执行你编写的 Lua,对网关运行时拥有相同的访问权限;区别在于代码的封装、配置、治理和复用方式。

一条经验法则:小而一次性的片段用 serverless 函数,任何可复用或复杂的逻辑都用自定义插件

Serverless 函数

serverless-pre-functionserverless-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 —— 运行所在的单个阶段:rewriteaccessheader_filterbody_filterlogbefore_proxy,默认 access
  • functions —— Lua 源码字符串数组,每个返回一个 function(conf, ctx)。它们按顺序执行;若某个返回了状态码或 body,请求即短路。

serverless-pre-function 运行较早(优先级 10000),serverless-post-function 运行较晚(优先级 -2000),因此它们通常用于在某个阶段的最开始或最末尾注入逻辑。

自定义插件

自定义插件是你从 Dashboard 上传一次的 Lua 模块。控制面会解析它的 nameversionschema,把它下发到你选择的网关组,之后它就像内置插件一样出现在插件选择器中。你通过带类型的字段来配置它,而不是粘贴代码。

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 校验、带默认值的类型化字段
名称与版本会被解析并强制(nameversion
优先级固定:10000(较早)或 -2000(较晚)由插件设定(priority 字段)
阶段每个实例一个 phase一个模块内可覆盖全部六个阶段
发布控制跟随它所附着的资源下发到所选的网关组;可从测试推广到生产
评审与治理在每个被粘贴处分别评审集中上传并版本化,受 RBAC 治理
可测试性困难——逻辑是配置里的转义字符串可作为模块测试(luac -p、路由测试)
运行时权限完整网关运行时完整网关运行时
最佳适用小而一次性的片段和快速试验可复用、复杂的生产逻辑

两种机制运行时的权限相同——都没有沙箱。无论选择哪种,都要以同样的谨慎评审代码。

何时使用 Serverless 函数

以下情况适合 serverless 函数:

  • 小而自包含 —— 几行代码用于设置 header、调整变量或输出一条日志。
  • 只针对单个资源 —— 你不打算在别处复用它。
  • 快速试验 —— 想先试一下,而不走上传并注册的流程。

何时使用自定义插件

只要逻辑不再是琐碎的,就选自定义插件。特别是当你需要:

  • 配置。 带默认值、类型化并经校验的 schema,让运维通过字段而非编辑 Lua 来配置插件。这是两者最大的实际差异。
  • 复用。 一个命名插件应用到许多路由和服务,只在一处更新。
  • 可控发布。 部署到测试网关组,验证后再加入生产网关组。见在网关组之间灰度发布
  • 多阶段或特定优先级。 例如同时跨越 accesslog,或必须相对其他插件运行在某个特定位置的逻辑。
  • 依赖。 位于包搜索路径上的共享辅助模块或原生库。见添加依赖库
  • 评审与生命周期管理。 集中上传、版本化和 RBAC,而不是散落在各资源配置里的代码。

对于复杂场景,推荐使用自定义插件:它们让逻辑保持可复用、可测试、可配置且受治理,而 serverless 函数则让小片段贴近它所作用的资源。

相关文档