跳到主要内容

使用 Lua 创建自定义插件

APISIX 的主要功能之一是其通过 插件 的可扩展性。除了各种现有插件外,APISIX 还允许你构建自定义插件以添加额外功能并使用自定义流程管理 API 流量。通常,你使用 Lua 编程语言来实现新插件。APISIX 分 阶段 处理请求,相关的插件逻辑在请求路由期间的每个阶段执行。

本指南将引导你完成为 APISIX 开发一个新的自定义 Lua 插件的示例过程。

前置条件

  • 安装 Docker
  • 安装 cURL 以向服务发送请求进行验证。
  • 按照 快速入门教程 在 Docker 或 Kubernetes 中启动一个新的 APISIX 实例。

开发文件代理插件

在本节中,你将使用 Lua 为 APISIX 创建一个名为 file-proxy 的新自定义插件。此插件将用于通过 API 公开静态文件(YAML、JSON、JavaScript、CSS 或图像文件)并从指定的 URL 获取文件。 例如,API 用户可以通过指定的 URL http://127.0.0.1:9080/openapi.yaml 访问 openapi.yaml 文件。

创建 Lua 文件

为插件源代码创建一个名为 file-proxy.lua 的新 Lua 文件。

导入模块

file-proxy 插件导入必要的模块:

local core = require("apisix.core")
local io = require("io")
local ngx = ngx

定义插件名称

为插件声明一个唯一的名称:

local plugin_name = "file-proxy"

定义插件架构

为插件参数创建一个架构。架构定义了可用参数及其数据类型、属性、默认值、有效值等。

file-proxy 插件需要有一个 文件路径 参数,以便 APISIX 知道在响应中返回其内容之前从哪里读取文件。

local plugin_schema = {
type = "object",
properties = {
# highlight-start
path = {
// Annotate 1
type = "string"
},
# highlight-end
},
# highlight-next-line
// Annotate 2
required = {"path"}
}

❶ 要服务的文件的路径。

❷ 路径设置为必需参数。

定义 Lua 模块表

为插件定义属性 versionprioritynameschemanameschema 是之前定义的插件名称和架构。versionpriority 由 APISIX 用于管理插件。

local _M = {
# highlight-start
// Annotate 1
version = 1.0,
// Annotate 2
priority = 1000,
// Annotate 3
name = plugin_name,
// Annotate 4
schema = plugin_schema
# highlight-end
}

version:该字段通常是指当前正在使用的版本。如果你发布并更新你的插件逻辑,它将是 1.1(你可以设置你希望的任何版本)。

priority:该字段用于在执行插件的每个阶段之前对插件进行排序。具有较高优先级的插件首先执行。确保自定义插件的优先级适合现有插件的优先级。现有插件的优先级记录在 config.yaml.example 文件中。

name:插件的名称。

schema:插件的架构。

定义架构检查函数

定义一个架构检查器以根据定义的架构验证用户输入的参数:

function _M.check_schema(conf)
local ok, err = core.schema.check(plugin_schema, conf)
if not ok then
return false, err
end
return true
end

定义自定义逻辑

APISIX 允许你在各种 阶段 中注入自定义逻辑。请参阅 Lua NGINX 模块指令 以了解有关不同阶段的更多信息。

access 函数中实现 file-proxy 插件的自定义逻辑,该函数将在 access 阶段执行。逻辑应该打开插件配置中指定的文件,读取其内容,并将内容作为响应返回。如果文件无法打开,它应该记录错误并返回 404 Not Found

function _M.access(conf, ctx)
local fd = io.open(conf.path, "rb")
if fd then
local content = fd:read("*all")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error("File is not found: ", conf.path, ", error info: ", err)
end
end

定义日志逻辑

log 函数在 log 阶段执行。日志记录是可选的,但有助于调试和检查插件是否正常工作。

实现日志逻辑以记录插件配置、对插件的请求和响应:

function _M.log(conf, ctx)
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end

组合所有内容

当你组合上面的所有代码时,file-proxy.lua 应该如下所示:

-- 引入该插件所需的模块/库
local core = require("apisix.core")
local io = require("io")
local ngx = ngx

-- 声明插件名称
local plugin_name = "file-proxy"

-- 定义插件的 schema 格式
local plugin_schema = {
type = "object",
properties = {
path = {
type = "string" -- 要提供服务的文件路径
},
},
required = {"path"} -- path 为必填字段
}

-- 定义插件,包括版本号、优先级、名称和 schema
local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = plugin_schema
}

-- 校验插件配置是否正确的函数
function _M.check_schema(conf)
-- 根据 schema 校验配置
local ok, err = core.schema.check(plugin_schema, conf)
-- 如果校验失败,返回 false 和错误信息
if not ok then
return false, err
end
-- 如果校验成功,返回 true
return true
end

-- 在 access 阶段调用的函数
function _M.access(conf, ctx)
-- 打开配置中指定的文件
local fd = io.open(conf.path, "rb")
-- 如果文件成功打开,读取内容并作为响应返回
if fd then
local content = fd:read("*all")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
-- 如果文件无法打开,记录错误并返回 404 Not Found 状态
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error("File is not found: ", conf.path, ", error info: ", err)
end
end


-- 在 log 阶段调用的函数
function _M.log(conf, ctx)
-- 记录插件配置和请求上下文
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end

-- 返回插件对象,使其可被 APISIX 使用
return _M

加载自定义插件

有两种方法可以将自定义插件代码加载到 APISIX 中:

  1. 将插件源代码放在默认的 APISIX 插件目录 /apisix/plugins 中,与其他 APISIX 插件放在一起。
  2. 将插件源代码放在单独的目录中,并在 config.yaml 中的 extra_lua_path 中指定搜索路径。

以下部分提供了第二种方法的说明,因为建议在单独的目录中管理自定义代码。

创建自定义插件文件

APISIX 在你的自定义目录中的 /apisix/plugins 中查找 L7 插件,在 /apisix/stream/plugins 中查找 L4 插件。

对于自定义插件,创建一个具有自定义名称的目录,并在该目录中创建 /apisix/plugins

docker exec apisix-quickstart /bin/sh -c "mkdir -p custom-plugin/apisix/plugins"

将插件文件保存到该目录:

docker exec apisix-quickstart /bin/sh -c "echo '
local core = require(\"apisix.core\")
local io = require(\"io\")
local ngx = ngx

local plugin_name = \"file-proxy\"

local plugin_schema = {
type = \"object\",
properties = {
path = {
type = \"string\"
},
},
required = {\"path\"}
}

local _M = {
version = 1.0,
priority = 1000,
name = plugin_name,
schema = plugin_schema
}

function _M.check_schema(conf)
local ok, err = core.schema.check(plugin_schema, conf)
if not ok then
return false, err
end
return true
end

function _M.access(conf, ctx)
local fd = io.open(conf.path,\"rb\")
if fd then
local content = fd:read(\"*all\")
fd:close()
ngx.header.content_length = #content
ngx.say(content)
ngx.exit(ngx.OK)
else
ngx.exit(ngx.HTTP_NOT_FOUND)
core.log.error(\"File is not found: \", conf.path, \", error info: \", err)
end
end

function _M.log(conf, ctx)
core.log.warn(\"conf: \", core.json.encode(conf))
core.log.warn(\"ctx: \", core.json.encode(ctx, true))
end

return _M
' > /usr/local/apisix/custom-plugin/apisix/plugins/file-proxy.lua"

更新 APISIX 配置文件

更新 APISIX 配置文件,在 extra_lua_path 中指定自定义插件的搜索路径,并将插件名称添加到插件列表中:

docker exec apisix-quickstart /bin/sh -c "echo '
apisix:
// Annotate 1
extra_lua_path: "/usr/local/apisix/custom-plugin/\?.lua"
enable_control: true
control:
ip: 0.0.0.0
port: 9092
deployment:
role: traditional
role_traditional:
config_provider: etcd
admin:
admin_key_required: false
allow_admin:
- 0.0.0.0/0
plugin_attr:
prometheus:
export_addr:
ip: 0.0.0.0
port: 9091
plugins:
- file-proxy
' > /usr/local/apisix/conf/config.yaml"

extra_lua_path:自定义插件的搜索路径。

警告

请注意,仅将 file-proxy 插件添加到插件列表将覆盖所有现有的默认插件。要使 file-proxy 插件成为现有插件的补充,你应该复制现有插件的名称并将它们添加到列表中。

重新加载 APISIX 以使配置更改生效:

docker exec apisix-quickstart apisix reload

测试自定义插件

存储测试文件

在 APISIX 实例中存储一个静态 openapi.yaml 文件:

docker exec apisix-quickstart /bin/sh -c "echo '
openapi: 3.0.1
info:
title: OpenAPI Spec
description: OpenAPI Spec file description.
' > /usr/local/apisix/openapi.yaml"

创建路由

要使用 file-proxy 自定义插件,你需要在 APISIX 中创建一个使用该插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id":"openapi-file-proxy",
"uri":"/openapi.yaml",
"plugins":{
"file-proxy":{
"path":"/usr/local/apisix/openapi.yaml"
}
}
}'

验证插件

向路由发送请求:

curl "http://127.0.0.1:9080/openapi.yaml"

响应应该是 openapi.yaml 文件的内容:

openapi: 3.0.1
info:
title: OpenAPI Spec
description: OpenAPI Spec file description.

下一步

使用 Lua 为 APISIX 开发自定义插件是扩展 API 网关功能的强大方法。在插件运行器的支持下,你还可以使用 Java、Go 和 Python 等其他编程语言开发插件(即将推出)。