插件开发最佳实践
本指南汇总了让自定义 Lua 插件具备内置插件同等级 行为的编码实践,包括可预测的配置、正确的请求和响应处理,以及负载下的良好性能。
最重要的习惯是基于 apisix.core 库构建插件,而不是直接调用原始 OpenResty(ngx.*)API。所有内置插件都采用这种方式。apisix.core 是网关的插件工具箱,它在底层 OpenResty 原语之上提供请求级缓存、一致的错误处理和辅助函数,并已处理许多容易遗漏的边界情况。
local core = require("apisix.core")
使用 apisix.core,不要直接使用原始 ngx API
原始 ngx.* 调用在快速测试中通常可用,但在生产环境可能产生隐蔽问题。使用 core.* 封装的原因包括:
- 缓存请求范围的读取。
core.request.headers(ctx)和ctx.var.<name>只计算一次并在当前请求中复用,而重复调用ngx.req.get_headers()每次都会重新解析。 - **与网关其他部分保持一致。**插件通过
core.request.set_header设置请求头时,其他插件读取的缓存视图会保持正确;混用原始ngx.req.set_header可能留下过时缓存值。 - **处理不便的默认行为。**例如,
core.request.get_uri_args读取参数时不设截断限制,而ngx.req.get_uri_args()会静默限制为 100 个参数。 - **集中行为。**日志、JSON 编码和 Schema 校验通过统一入口执行,使级别过滤、错误处理和格式保持一致。
ngx 到 core 的映射
应优先使用对应的 core API。下表列出最常见的替代关系。
原始 ngx API | 推荐的 core API | 说明 |
|---|---|---|
ngx.log(ngx.ERR, ...) | core.log.error(...) (.warn, .info, .debug) | 自动按级别过滤;传入多个参数,不要拼接。 |
ngx.req.get_headers() | core.request.headers(ctx) | 缓存在 ctx 中;键转换为小写,以便不区分大小写地查找。 |
ngx.req.get_headers()[name] | core.request.header(ctx, name) | 不区分大小写;解包多值请求头。 |
ngx.req.set_header(k, v) | core.request.set_header(ctx, k, v) | 同时让缓存的请求头视图失效。 |
ngx.req.get_uri_args() | core.request.get_uri_args(ctx) | 无截断限制;结果会被缓存。 |
ngx.req.set_uri_args(a) | core.request.set_uri_args(ctx, a) | 接受 Table 或字符串。 |
ngx.req.read_body() + ngx.req.get_body_data() | core.request.get_body() | 也可读取缓冲到临时文件的请求体;仍可能返回 nil。 |
ngx.req.get_method() | core.request.get_method() | |
ngx.var.<name> | ctx.var.<name> | 延迟加载并缓存;还提供 route_id、consumer_name 等网关变量。 |
ngx.header[k] = v | core.response.set_header(k, v) | 防止在响应头已发送后继续设置。 |
ngx.say / ngx.print / ngx.exit | core.response.exit(code, body) | Table 响应体会编码为 JSON;必须返回调 用结果。 |
cjson.encode / cjson.decode | core.json.encode / core.json.decode | 输入无效时,decode 返回 nil, err。 |
日志行中的 cjson.encode | core.json.delay_encode(data) | 仅在实际输出日志行时编码。 |
| JSON Schema 校验 | core.schema.check(schema, conf) | 缓存已编译的校验器。 |
table.insert / table.new / table.clear | core.table.insert / core.table.new / core.table.clear | core.table 也代理标准 table 库。 |
ngx.timer.every(周期任务) | core.timer.new(name, cb, opts) | 在 pcall 下运行回调,以隔离错误。 |
核心库概览
core 将辅助函数分组到多个子模块中。插件开发者最常使用以下模块:
| 子模块 | 用途 |
|---|---|
core.request | 读取和修改传入的客户端请求。 |
core.response |