简介
API 网关作为管理 API 流量的中心枢纽,提供诸如身份验证、限流、可观测性和流量整形等功能。然而,开箱即用的功能可能无法覆盖所有的应用场景。这就是自定义插件发挥作用的地方。
自定义插件使开发者能够根据特定的业务或技术需求扩展 API 网关的功能。本文将探讨 API 网关中自定义插件的架构、开发、部署以及最佳实践。
为什么自定义插件很重要
应用场景
- 业务逻辑执行:将特定的规则、策略或元数据注入到请求流程中。
- 自定义身份验证:与内部身份验证系统或特定令牌格式进行集成。
- 数据转换:修改请求/响应头或数据负载格式。
- 监控:输出自定义指标或日志以实现可观测性。
优势
- 更高的灵活性
- 增强的可重用性
- 模块化且易于维护的架构
API 网关中的插件架构
大多数 API 网关支持模块化的插件架构。这些插件作为请求生命周期的一部分执行,通常支持各个阶段:
- 重写阶段 (Rewrite phase):在路由之前修改请求。
- 访问阶段 (Access phase):执行身份验证或限流。
- 负载均衡阶段 (Balancer phase):处理上游节点选择。
- 头部过滤阶段 (Header filter phase):在发送响应前修改响应头。
- 请求体过滤阶段 (Body filter phase):修改响应体。
- 日志阶段 (Log phase):在请求结束后输出日志或指标。
插件生命周期
1sequenceDiagram
2 participant Client
3 participant Gateway
4 participant Plugin
5 participant Upstream
6 Client->>Gateway: 发送 API 请求
7 Gateway->>Plugin: 执行重写阶段
8 Gateway->>Plugin: 执行访问阶段
9 Gateway->>Upstream: 转发请求
10 Upstream-->>Gateway: 返回响应
11 Gateway->>Plugin: 执行头部过滤
12 Gateway->>Plugin: 执行请求体过滤
13 Gateway->>Plugin: 执行日志阶段
14 Gateway-->>Client: 返回最终响应插件开发模型
Lua 插件(例如:Apache APISIX、Kong)
- 优点:启动快、内存占用低、原生集成
- 缺点:需要学习 Lua 语言
- 示例:Apache APISIX
1-- sample.lua
2function _M.access(conf, ctx)
3 core.log.info("Custom plugin executing")
4 if ctx.var.uri == "/blocked" then
5 return 403, "Forbidden"
6 end
7endGo 插件(例如:通过 Wasm 或 RPC 扩展的 APISIX)
- 优点:强类型、生态丰富
- 缺点:构建流水线较为复杂
- 示例:
1func Access(ctx context.Context, request *apisix.Request) (*apisix.Response, error) {
2 if request.Path == "/custom" {
3 return apisix.Response{Status: 200, Body: "Handled by Go plugin"}, nil
4 }
5 return nil, nil
6}JavaScript/Node.js 插件(例如:KrakenD、Express Gateway)
- 优点:熟悉的语言、迭代速度快
- 缺点:资源消耗较高
Wasm 插件(例如:Envoy、APISIX)
- 优点:语言无关、沙盒化运行
- 缺点:在某些网关中仍处于实验阶段
插件部署模型
进程内执行
插件在网关进程内执行,以实现低延迟。常见于 Lua 或嵌入式的 JavaScript 引擎。
外部执行 (通过 RPC)
插件作为外部服务运行,通过 gRPC、HTTP 或 Unix 域套接字进行通信。
- 优点:隔离性好、支持多语言
- 缺点:延迟略高、部署较为复杂
RPC 插件执行模型
1sequenceDiagram
2 participant Client
3 participant APISIX
4 participant PluginRunner
5 participant Java|Go|Node|Python
6
7 Client->>APISIX: 发送 HTTP/gRPC/MQTT 请求
8 APISIX->>APISIX: 应用全局规则(如:限流、安全策略)
9 APISIX->>APISIX: 路由匹配
10
11 Note over APISIX: 请求前置阶段
12 APISIX->>PluginRunner: ext-plugin-pre-req (RPC)
13 PluginRunner->>Java|Go|Node|Python: 执行 validator / rewrite-header / rewrite-args
14 Java|Go|Node|Python-->>PluginRunner: 返回结果
15 PluginRunner-->>APISIX: 返回预处理结果
16
17 APISIX->>APISIX: 应用路由级插件
18
19 Note over APISIX: 请求后置阶段
20 APISIX->>PluginRunner: ext-plugin-post-req (RPC)
21 PluginRunner->>Java|Go|Node|Python: 执行 rewrite-status / rewrite-header / rewrite-body
22 Java|Go|Node|Python-->>PluginRunner: 返回结果
23 PluginRunner-->>APISIX: 返回后置处理结果
24
25 APISIX->>Client: 返回最终响应插件开发的最佳实践
设计指南
- 保持插件无状态
- 使用配置来实现逻辑分支
- 限制在请求路径中进行密集的计算操作
测试
- 独立地对核心逻辑进行单元测试
- 使用网关的插件测试框架或基于 Docker 的测试工具
版本控制与兼容性
- 锁定插件版本以避免破坏性变更
- 遵循语义化版本控制并记录更新日志
安全
- 严格验证输入参数
- 避免任意代码执行漏洞
- 利用网关的安全上下文(例如:沙盒机制)
总结
自定义插件对于根据特定需求量身定制 API 网关的行为至关重要。无论你是需要强制执行业务逻辑、扩展可观测性,还是与自定义的身份验证系统集成,编写和部署插件都能为你解锁强大的功能。
通过了解插件的生命周期、开发模型以及部署选项,工程师可以在保持性能和可靠性的同时,安全地扩展网关的功能。
常见问题 (FAQ)
1. 我应该使用哪种语言来编写自定义插件?
这取决于你的网关。Lua 在 APISIX 和 Kong 中非常流行,Envoy 支持 Wasm,而 KrakenD 支持 Go 和 JavaScript。
2. 自定义插件可以在不同网关之间移植吗?
通常不可以。每个网关都有自己特定的插件 API 和生命周期管理机制。
3. 插件会影响性能吗?
是的。编写不当的插件会拖慢请求处理速度。请务必测试并监控性能。
4. 如何调试自定义插件?
使用内置的日志记录、结构化的输出,以及网关提供的本地测试框架。
