自定义插件
概述
自定义插件(Custom Plugin)允许用户为某个网关实例上传并接入自己的 ExtProc(External Processing)插件。其运行机制是:Envoy 在请求/响应处理链中,通过 gRPC 将请求/响应转发给用户自建的 ExtProc 服务处理。
关键约束:
- 当前仅支持 ExtProc 类型(
custom_extproc)。custom_wasm暂无实现,调用创建/更新接口会报「操作不支持」。 - 自定义插件一定归属某个网关实例(
instanceId),只能被其归属实例使用,不能跨实例。 - 创建后系统自动为该实例建立一条实例级绑定,默认 Disable(禁用),需显式启用后才生效。
- 归属校验:更新 / 绑定路由时,会校验调用账号与插件所属账号一致,且路由必须属于插件所属实例,否则拒绝。
接口清单
所有接口均为 POST,通过 URL 参数 ?action=Xxx 路由,请求体为 JSON,账号身份取自 ReqContext.accountId。
| action | 说明 | 适用于自定义插件 |
|---|---|---|
CreateCustomPlugin |
创建自定义插件 | ✅ 专用 |
UpdateCustomPlugin |
编辑自定义插件 | ✅ 专用 |
DeletePluginBinding |
卸载插件绑定(卸载实例级绑定时级联删除自定义插件定义) | ✅ 复用 |
UpdatePluginBinding |
编辑插件绑定配置(pluginConfig / status) | ✅ 复用 |
UpdatePluginBindingStatus |
启用 / 禁用插件绑定 | ✅ 复用 |
InstallPluginToApis |
将插件绑定到路由(API 级生效) | ✅ 复用 |
InstallPluginToInstances |
将插件绑定到实例(实例级生效) | ⚠️ 自定义 ExtProc 不支持,创建时已自动建实例级绑定,调用会报错 |
DescribePlugin / DescribePlugins / DescribePluginsByInstanceId / DescribePluginBinding 等 |
查询类接口 | ✅ 复用 |
说明:没有独立的「删除自定义插件」接口。删除通过卸载其实例级绑定(
DeletePluginBinding)触发——当被卸载绑定为实例级且插件类型为custom_extproc时,系统级联删除插件定义并从 Envoy 移除。
创建自定义插件 — CreateCustomPlugin
请求参数
| 字段 | 类型 | 必填 | 说明 | 默认值 |
|---|---|---|---|---|
name |
String | 是 | 插件名称 | — |
instanceId |
String | 是 | 所属网关实例 ID(须存在且属于当前账号) | — |
config |
String | 是 | 插件配置,YAML 格式字符串(详见第 5 节) | — |
pluginPhase |
String | 否 | 插件执行阶段,取值 AUTHN / AUTHZ / STATS(大小写不敏感) |
AUTHN |
executionPriority |
Integer | 否 | 执行优先级,范围 0~9999,数字越小越先执行 | 0 |
description |
String | 否 | 插件描述 | 空串 |
version |
String | 否 | 版本号 | 空串 |
versionDescription |
String | 否 | 版本描述 | 空串 |
响应
1{ "pluginReleaseId": "pls-xxxxxxxx" }
行为
- 校验实例存在且归属当前账号。
- 对
config反序列化 YAML → 补默认值 → 完整校验(校验失败抛AiGwParamCheckException)。 - 持久化插件定义:
type=Custom、pluginType=custom_extproc、pluginImage=custom-extproc-plugin、displayStatus=visible。 - 自动创建一条实例级绑定(
effectiveLevel=Instance,status=Disable),绑定配置由插件 config 中的typed_config派生。
编辑自定义插件 — UpdateCustomPlugin
请求参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
pluginReleaseId |
String | 是 | 插件发布 ID |
name |
String | 否 | 插件名称,非空才更新 |
config |
String | 否 | 插件配置 YAML,传了才更新并重新下发,结构同创建 |
pluginPhase |
String | 否 | AUTHN / AUTHZ / STATS |
executionPriority |
Integer | 否 | 0~9999 |
description |
String | 否 | 非空才更新 |
version |
String | 否 | 非空才更新 |
versionDescription |
String | 否 | 非空才更新 |
行为
- 所有字段均为空(name/description/version/versionDescription 全空,且 config/pluginPhase/executionPriority 均为 null)时,直接返回,不做任何操作。
- 校验插件类型必须为
custom_extproc,且调用账号 == 插件所属账号,否则抛异常。 - 仅当
config/pluginPhase/executionPriority任一非 null 时,才在更新后重新下发到 Envoy(reconcile)。 - 传入
config时会先补默认值再完整校验(规则同创建)。
注意:
config是整体替换,不是字段级增量合并。要改任何一项,需提交完整 YAML。
config YAML 参数详解(重点)
config 字段是一段 YAML 字符串,反序列化为结构化配置,最终转换为 Envoy ExtProc 的 Cluster + HTTP Filter 配置下发。字段名在 YAML 中均为 snake_case。
顶层字段
| YAML 字段 | 类型 | 必填 | 语义 | 取值范围 / 校验 | 默认值 |
|---|---|---|---|---|---|
endpoints |
列表 | 是 | ExtProc 上游服务地址列表(gRPC 目标)。列表第一个 endpoint 的 port 作为 gRPC 端口 | 非空;每项 address、port 必填 | — |
typed_config |
对象 | 是 | ExtProc 处理配置 | 见 5.3 | — |
cluster_type |
String | 否 | Envoy cluster 类型 | STATIC / STRICT_DNS |
STATIC |
connect_timeout |
String | 否 | 连接超时(duration) | 正则 ^[1-9]\d*(ms|s|m|h)$,如 5s、200ms |
5s |
lb_policy |
String | 否 | 负载均衡策略 | ROUND_ROBIN / LEAST_REQUEST / RING_HASH / RANDOM / MAGLEV / CLUSTER_PROVIDED |
ROUND_ROBIN |
dns_lookup_family |
String | 否 | DNS 解析 IP 族 | AUTO / V4_ONLY / V6_ONLY / V4_PREFERRED / ALL |
不填 |
dns_resolvers |
列表 | 否 | 自定义 DNS resolver 列表 | 每项 address、port 必填、port 1~65535 | 不填 |
endpoints[] / dns_resolvers[] 元素
| YAML 字段 | 类型 | 必填 | 说明 | 校验 |
|---|---|---|---|---|
address |
String | 是 | 上游服务地址(IP 或域名) | 非空 |
port |
Integer | 是 | 上游服务端口 | 1~65535 |
typed_config
| YAML 字段 | 类型 | 必填 | 语义 | 取值 / 校验 | 默认值 |
|---|---|---|---|---|---|
processing_mode |
对象 | 是 | 请求/响应处理模式,见 5.4 | 6 个子字段全必填 | — |
failure_mode_allow |
Boolean | 否 | ExtProc 处理失败时是否放行请求 | true / false | false |
message_timeout |
String | 否 | 单条消息处理超时(duration) | 正则同 connect_timeout,如 10s |
10s |
grpc_service |
对象 | 否 | gRPC 服务配置,含 timeout(duration) |
timeout 满足 duration 校验 | timeout 10s(自动补齐) |
grpc_service:
| YAML 字段 | 类型 | 必填 | 说明 | 默认值 |
|---|---|---|---|---|
timeout |
String | 否 | gRPC 调用超时(duration) | 10s |
processing_mode(6 个子字段全部必填)
控制 Envoy 是否将请求/响应的各部分发送给 ExtProc 服务处理。
| YAML 字段 | 必填 | 取值 | 说明 |
|---|---|---|---|
request_header_mode |
是 | SEND / SKIP |
请求头是否发送给 ExtProc |
response_header_mode |
是 | SEND / SKIP |
响应头是否发送 |
request_body_mode |
是 | NONE / STREAMED / BUFFERED / BUFFERED_PARTIAL |
请求体发送模式 |
response_body_mode |
是 | NONE / STREAMED / BUFFERED / BUFFERED_PARTIAL |
响应体发送模式 |
request_trailer_mode |
是 | SEND / SKIP |
请求 trailer 是否发送 |
response_trailer_mode |
是 | SEND / SKIP |
响应 trailer 是否发送 |
这 6 个字段若缺任一,创建/更新会报
... is required。
完整 config YAML 示例
1# 上游 ExtProc 服务地址(必填),第一个 endpoint 的 port 即 gRPC 端口
2endpoints:
3 - address: 10.0.0.10
4 port: 50055
5
6# 可选:cluster 类型(默认 STATIC;域名场景用 STRICT_DNS)
7cluster_type: STATIC
8
9# 可选:连接超时(默认 5s)
10connect_timeout: 5s
11
12# 可选:负载均衡策略(默认 ROUND_ROBIN)
13lb_policy: ROUND_ROBIN
14
15# 可选:DNS 解析族(STRICT_DNS 场景可用)
16dns_lookup_family: V4_ONLY
17
18# 可选:自定义 DNS resolver
19dns_resolvers:
20 - address: 10.0.0.2
21 port: 53
22
23# ExtProc 处理配置(必填)
24typed_config:
25
26 # 失败是否放行(默认 false)
27 failure_mode_allow: false
28
29 # 消息处理超时(默认 10s)
30 message_timeout: 10s
31
32 # gRPC 服务配置(可选,默认 timeout 10s)
33 grpc_service:
34 timeout: 10s
35
36 # 处理模式(必填,6 个子字段全必填)
37 processing_mode:
38 request_header_mode: SEND
39 request_body_mode: NONE
40 request_trailer_mode: SKIP
41 response_header_mode: SEND
42 response_body_mode: NONE
43 response_trailer_mode: SKIP
生效层级与优先级
- 创建后仅有一条实例级绑定(Instance)且默认 Disable,需通过
UpdatePluginBindingStatus启用。 - 可通过
InstallPluginToApis追加 API 级绑定,实现按路由精细生效。 executionPriority+pluginPhase共同决定插件在 Envoy filter chain 中的顺序(先按 phase:AUTHN→AUTHZ→STATS,再按 priority 升序)。
绑定配置(pluginConfig)参数
InstallPluginToApis / UpdatePluginBinding 中的 pluginConfig 也是 YAML,用于覆盖某个绑定层级的处理行为。结构按绑定层级不同:
实例级绑定(HttpFilter)
1typed_config:
2 failure_mode_allow: false # 必填
3 message_timeout: 10s # 可选,duration
4 grpc_service: # 必填
5 timeout: 10s # 可选,duration
6 processing_mode: # 必填,6 个子字段规则同 5.4
7 request_header_mode: SEND
8 response_header_mode: SEND
9 request_body_mode: NONE
10 response_body_mode: NONE
11 request_trailer_mode: SKIP
12 response_trailer_mode: SKIP
| 字段 | 必填 | 说明 |
|---|---|---|
typed_config |
是 | — |
typed_config.failure_mode_allow |
是 | 失败是否放行 |
typed_config.grpc_service |
是 | gRPC 配置(timeout 可选,duration 校验) |
typed_config.message_timeout |
否 | duration 校验 |
typed_config.processing_mode |
是 | 6 个子字段全必填,取值同 5.4 |
API 级绑定(HttpRoute overrides)
1overrides:
2 processing_mode: # 必填
3 request_header_mode: SEND
4 response_header_mode: SEND
5 request_body_mode: NONE
6 response_body_mode: NONE
7 request_trailer_mode: SKIP
8 response_trailer_mode: SKIP
9 grpc_initial_metadata: # 可选
10 - key: x-custom-key
11 value: custom-value
| 字段 | 必填 | 说明 |
|---|---|---|
overrides |
是 | — |
overrides.processing_mode |
是 | 处理模式,6 个子字段规则同 5.4 |
overrides.grpc_initial_metadata[] |
否 | gRPC 初始元数据;每项 key、value 均必填 |
常见错误与排查
| 现象 | 原因 |
|---|---|
Invalid plugin config format, must be valid YAML |
config 非合法 YAML 或为空 |
config.endpoints is required and must not be empty |
缺 endpoints |
config.endpoints[i].port must be between 1 and 65535 |
端口越界 |
config.typed_config.processing_mode.xxx_mode is required |
processing_mode 缺子字段 |
config.cluster_type is invalid / lb_policy is invalid |
取值不在允许集合 |
config.connect_timeout is invalid |
duration 格式不符合 ^[1-9]\d*(ms|s|m|h)$ |
AiGwOperationNotSupportedException |
对非 custom_extproc 插件调用创建/更新,或对自定义 ExtProc 调用 InstallPluginToInstances |
AiGwResourcePermissionDenyException |
调用账号非插件所属账号 |
| 插件不生效 | 检查实例级/API 级绑定 status 是否 Enable;ExtProc 上游服务是否可达(安全组默认全拒绝,需放行 gRPC 端口) |
接口调用示例
所有接口统一:POST,路径为根路径 /?action=Xxx,Content-Type: application/json,账号身份由 BCE 鉴权头解析到 ReqContext,请求体不含 accountId。以下示例省略鉴权头,只展示 action 与请求体。
状态值大小写与后端
AiGwPluginBindStatusEnum保持一致:启用Enable、禁用Disable。
写接口(CmdApi)
CreateCustomPlugin — 创建自定义插件
1POST /?action=CreateCustomPlugin
1{
2 "name": "my-extproc-plugin",
3 "instanceId": "aigw-instabc123",
4 "pluginPhase": "AUTHN",
5 "executionPriority": 100,
6 "description": "自建鉴权 ExtProc 插件",
7 "version": "1.0.0",
8 "versionDescription": "首次发布",
9 "config": "endpoints:\n - address: 10.0.0.10\n port: 50055\ncluster_type: STATIC\nconnect_timeout: 5s\nlb_policy: ROUND_ROBIN\ntyped_config:\n failure_mode_allow: false\n message_timeout: 10s\n grpc_service:\n timeout: 10s\n processing_mode:\n request_header_mode: SEND\n request_body_mode: NONE\n request_trailer_mode: SKIP\n response_header_mode: SEND\n response_body_mode: NONE\n response_trailer_mode: SKIP\n"
10}
响应:
1{ "pluginReleaseId": "pls-tk7mv3qw" }
config为 YAML 字符串,JSON 里需转义换行符。其可读结构见第 5 节。
UpdateCustomPlugin — 编辑自定义插件
1POST /?action=UpdateCustomPlugin
1{
2 "pluginReleaseId": "pls-tk7mv3qw",
3 "name": "my-extproc-plugin-v2",
4 "executionPriority": 200,
5 "description": "调整优先级",
6 "config": "endpoints:\n - address: 10.0.0.11\n port: 50055\ntyped_config:\n failure_mode_allow: true\n processing_mode:\n request_header_mode: SEND\n request_body_mode: STREAMED\n request_trailer_mode: SKIP\n response_header_mode: SEND\n response_body_mode: NONE\n response_trailer_mode: SKIP\n"
7}
响应(UpdateAiGwPluginResp):
1{ "pluginReleaseId": "pls-tk7mv3qw" }
只想改名字/描述时,可只传
pluginReleaseId+ 目标字段;不传config则不重新下发。
InstallPluginToApis — 安装插件到路由(API 级生效)
1POST /?action=InstallPluginToApis
1{
2 "pluginId": "pls-tk7mv3qw",
3 "apiIds": ["api-abc123", "api-def456"],
4 "status": "Enable",
5 "pluginConfig": "overrides:\n processing_mode:\n request_header_mode: SEND\n request_body_mode: NONE\n request_trailer_mode: SKIP\n response_header_mode: SEND\n response_body_mode: NONE\n response_trailer_mode: SKIP\n grpc_initial_metadata:\n - key: x-custom-key\n value: custom-value\n"
6}
响应(CreateAiGwPluginReleaseBindingResp):绑定创建结果。
pluginConfig可选,不传则沿用插件默认配置;pluginId传插件 releaseId。
InstallPluginToInstances — 安装插件到实例(自定义 ExtProc 不适用)
1POST /?action=InstallPluginToInstances
1{
2 "pluginId": "pls-tk7mv3qw",
3 "instanceIds": ["aigw-instabc123"],
4 "status": "Enable",
5 "pluginConfig": ""
6}
⚠️ 对自定义 ExtProc 插件调用会抛
AiGwOperationNotSupportedException——创建时已自动建实例级绑定,实例级请改用UpdatePluginBindingStatus启用。此示例仅用于系统插件。
UpdatePluginBinding — 编辑插件绑定
1POST /?action=UpdatePluginBinding
1{
2 "pluginBindingId": "plb-9x8y7z6w",
3 "status": "Enable",
4 "pluginConfig": "overrides:\n processing_mode:\n request_header_mode: SEND\n request_body_mode: BUFFERED\n request_trailer_mode: SKIP\n response_header_mode: SEND\n response_body_mode: NONE\n response_trailer_mode: SKIP\n"
5}
响应(UpdateAiGwPluginResp)。
pluginBindingId是绑定记录 ID(非插件 releaseId);status必填。
UpdatePluginBindingStatus — 启用/禁用插件绑定
1POST /?action=UpdatePluginBindingStatus
1{
2 "pluginBindingId": "plb-9x8y7z6w",
3 "status": "Enable"
4}
启用创建后默认 Disable 的实例级绑定,即用此接口,
status传Enable/Disable。
DeletePluginBinding — 卸载插件绑定
1POST /?action=DeletePluginBinding
1{
2 "pluginBindingId": "plb-9x8y7z6w"
3}
若被卸载的是自定义 ExtProc 的实例级绑定,会级联删除插件定义并从 Envoy 移除;卸载 API 级绑定只移除该路由绑定。
读接口(QryApi)
DescribePlugins — 所有插件列表
1POST /?action=DescribePlugins
1{ "name": "my-extproc" }
name可选,按名称模糊过滤,不传返回全部。
DescribePlugin — 插件详情
1POST /?action=DescribePlugin
1{ "pluginId": "pls-tk7mv3qw" }
DescribePluginsByInstanceId — 按实例查询已绑定插件列表
1POST /?action=DescribePluginsByInstanceId
1{ "instanceId": "aigw-instabc123" }
DescribePluginBinding — 按绑定 ID 查询绑定详情
1POST /?action=DescribePluginBinding
1{ "pluginBindingId": "plb-9x8y7z6w" }
DescribeInstallableInstances — 查询可安装实例列表
1POST /?action=DescribeInstallableInstances
1{
2 "pluginId": "pls-tk7mv3qw",
3 "instanceIdOrName": "aigw-inst"
4}
instanceIdOrName可选,按实例 ID 或名称过滤。
DescribeInstallableApisByPage — 分页查询可安装路由列表
1POST /?action=DescribeInstallableApisByPage
1{
2 "pluginId": "pls-tk7mv3qw",
3 "serviceId": "svc-abc123",
4 "apiName": "user",
5 "pageNo": 1,
6 "pageSize": 100
7}
pageNo/pageSize为通用分页字段(pageNo≥1,pageSize 1~1000,默认 1/1000);apiName可选。
DescribeInstalledInstancesByPage — 分页查询已安装实例列表
1POST /?action=DescribeInstalledInstancesByPage
1{
2 "pluginId": "pls-tk7mv3qw",
3 "instanceId": "aigw-instabc123",
4 "pageNo": 1,
5 "pageSize": 100
6}
instanceId可选,用于精确过滤。
DescribeInstalledApisByPage — 分页查询绑定的路由列表
1POST /?action=DescribeInstalledApisByPage
1{
2 "pluginBindingId": "plb-9x8y7z6w",
3 "pageNo": 1,
4 "pageSize": 100
5}
无业务入参,仅需鉴权上下文。
评价此篇文章
