Serverless 函数还是自定义插件
API7 网关提供两种在请求生命周期中运行自有 Lua 逻辑的方式:内置的 serverless-function 插件 和 自定义 Lua 插件。两者都会执行你编写的 Lua,并具有相同的网关运行时访问权限;区别在于代码的打包、配置、治理和复用方式。
经验法则是:小型、一次性代码片段使用 Serverless 函数;任何需要复用或较复杂的逻辑使用开发自定义 Lua 插件。
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)。它们按顺序运行;如果某个函数返回状态码或响应体,请求会提前结束。
serverless-pre-function 较早运行(优先级 10000),serverless-post-function 较晚运行(优先级 -2000),因此它们通常用于在某个阶段的最开始或最末尾注入逻辑。
自定义插件
开发自定义 Lua 插件是通过控制台上传到某个网关组的 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 函数适用于以下逻辑:
- 小且自包含——用几行代码设置请求头、调整变量或输出一行日志。
- 特定于一个资源——预计不会在其他位置复用。
- 快速实验——希望跳过上传和注册流程来试验某项功能。
何时使用自定义插件
只要逻辑不再简单,就应选择自定义插件,尤其是在需要以下能力时:
- **配置。**使用带默认值且经过类型验证的
schema,让运维人员通过字段配置插件而非编辑 Lua。这是两种方式最大的实际差异。 - **复用。**将一个具名插件应用到多个路由和服务,并在一个位置更新。
- **受控上线。**先部署到测试网关组,验证后再添加生产网关组。参阅开发自定义 Lua 插件。
- **多个阶段或特定优先级。**例如同时跨越
access和log的逻辑,或必须相对于其他插件在特定位置运行的逻辑。 - **依赖项。**包路径中的共享辅助模块或原生库。参阅开发自定义 Lua 插件。
- **审查和生命周期管理。**使用集中上传、版本管理和 RBAC,避免代码散落在资源配置中。
复杂场景推荐使用自定义插件。它使逻辑更易复用、测试、配置和治理,而 Serverless 函数则让小型代码片段贴近其作用的资源。
后续步骤
- 开发自定义 Lua 插件——构建、注册和测试自定义插件。
- 插件开发最佳实践——编写正确且高效的插件代码。
- Serverless Functions——serverless-function 插件参考。