APISIX自定义插件
APISIX自定义插件
插件存放位置
APISIX会默认加载/apisix/plugins
下的插件,插件可以直接放在这里
如果是第三方的插件目录,例如目录为/xx
,那么APISIX启动时,实际是扫描/xx/apisix/plugins
目录
APISIX要求插件父目录必须包含apisix/plugins
子目录
并且第三方目录需要再配置文件中config.yaml
指定:
apisix:
#....
extra_lua_path: "/xx/?.lua"
编写插件
参考示例: 可以参考源码中的
example-plugin.lua
插件,这是一个示例代码,默认也启用了,可以直接复制这个进行修改
创建 example-plugin2.lua
local ngx = ngx
local core = require("apisix.core")
local plugin = require("apisix.plugin")
local upstream = require("apisix.upstream")
local plugin_name = "example-plugin2"
-- 插件基础信息
local _M = {
version = 0.1,
-- 优先级,apisix 要求每个插件的优先级都不同,这里需要注意
priority = 1,
-- type 如果是 auth, 则表明是认证插件,就需要配合consumer一起使用了
-- type = 'auth',
name = plugin_name,
schema = schema,
-- metadata_schema = metadata_schema,
}
-- 插件配置参数
local schema = {
type = "object",
properties = {
i = {type = "number", minimum = 0},
s = {type = "string"},
t = {type = "array", minItems = 1},
ip = {type = "string"},
port = {type = "integer"},
},
required = {"i"},
}
-- consumer 配置,可以参考 jwt-auth.lua
--local consumer_schema = {
-- type = "object",
-- properties = {
-- ak = { type = "string" },
-- sk = { type = "string" },
-- },
-- required = { "ak", "sk" },
--}
-- 元数据配置, 可以通过/apisix/admin/plugin_metadata/{pluginName}进行元数据的配置
--local metadata_schema = {
-- type = "object",
-- properties = {
-- ikey = {type = "number", minimum = 0},
-- skey = {type = "string"},
-- },
-- required = {"ikey", "skey"},
--}
-- 校验配置合法性
function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_METADATA then
return core.schema.check(metadata_schema, conf)
end
return core.schema.check(schema, conf)
end
function _M.init()
-- call this function when plugin is loaded
local attr = plugin.plugin_attr(plugin_name)
if attr then
core.log.info(plugin_name, " get plugin attr val: ", attr.val)
end
end
-- 销毁方法,一般用于有元数据的情况,在这里面把资源是放掉
function _M.destroy()
-- call this function when plugin is unloaded
end
function _M.rewrite(conf, ctx)
core.log.warn("plugin rewrite phase, conf: ", core.json.encode(conf))
core.log.warn("conf_type: ", ctx.conf_type)
core.log.warn("conf_id: ", ctx.conf_id)
core.log.warn("conf_version: ", ctx.conf_version)
end
--[[
阶段方法:这里写自己的逻辑
conf 参数是插件的相关配置信息
ctx 参数缓存了请求相关的数据信息
]]
function _M.access(conf, ctx)
core.log.warn("plugin access phase, conf: ", core.json.encode(conf))
-- return 200, {message = "hit example plugin"}
if not conf.ip then
return
end
local up_conf = {
type = "roundrobin",
nodes = {
{host = conf.ip, port = conf.port, weight = 1}
}
}
local ok, err = upstream.check_schema(up_conf)
if not ok then
return 500, err
end
local matched_route = ctx.matched_route
upstream.set(ctx, up_conf.type .. "#route_" .. matched_route.value.id,
ctx.conf_version, up_conf)
return
end
function _M.body_filter(conf, ctx)
core.log.warn("plugin body_filter phase, eof: ", ngx.arg[2],
", conf: ", core.json.encode(conf))
end
function _M.delayed_body_filter(conf, ctx)
core.log.warn("plugin delayed_body_filter phase, eof: ", ngx.arg[2],
", conf: ", core.json.encode(conf))
end
local function hello()
local args = ngx.req.get_uri_args()
if args["json"] then
return 200, {msg = "world"}
else
return 200, "world\n"
end
end
-- 这里和官网的略有不同,因为我想暴露端口访问 hello方法,所以这里改成了api
function _M.api()
return {
{
methods = {"GET"},
uri = "/apisix/plugin/example-plugin2/hello",
handler = hello,
}
}
end
return _M
plugin_name
plugin_name 是插件名
_M
这部分是插件基本信息,包含了版本,优先级,类型等等
local _M = {
version = 0.1,
priority = 1,
-- type = 'auth',
name = plugin_name,
schema = schema,
metadata_schema = metadata_schema,
}
需要注意的是,当Type类型为auth
时,则表示这是个认证插件,需要和Consumer_schema 连用,以指定Consumer的参数
例如:我希望创建一个认证插件,Consumer访问路由的时候,需要带上ak和sk作为鉴权,就可以在插件中加入如下配置
local _M = {
...
type = 'auth',
...
}
local consumer_schema = {
type = "object",
properties = {
ak = { type = "string" },
sk = { type = "string" },
},
required = { "ak", "sk" },
}
schema
schema 是创建插件的时候指定参数
local schema = {
type = "object",
properties = {
i = {type = "number", minimum = 0},
s = {type = "string"},
t = {type = "array", minItems = 1},
ip = {type = "string"},
port = {type = "integer"},
},
required = {"i"},
}
这段代码中的意思是,在配置这个插件的时候,可以给定多种参数,但是i是必须要的,如果不指定i,创建时会报错。
curl -X PUT 127.0.0.1:9180/apisix/admin/routes/example_plugin2_01 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -d '
{
"plugins": {
"example-plugin2": {
"name": "example2",
"i": 1
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"xx.xx.xx.xx:8200": 1
}
},
"uri": "/weave/*"
}'
consumer_schema
用于指定创建 consumer 时的参数。
metadata_schema
元数据配置, 可以通过/apisix/admin/plugin_metadata/{pluginName} 接口进行元数据的配置,数据会存放到 apisix 的 etcd 数据库。
生命周期方法
通常来说,校验逻辑,可以写在rewrite阶段或者access阶段
- plugin
- init
- check_schema
- rewrite
- access
- before_proxy
- header_filter
- body_filter
- log
_M.api
local function hello()
local args = ngx.req.get_uri_args()
if args["json"] then
return 200, {msg = "world"}
else
return 200, "world\n"
end
end
function _M.api()
return {
{
methods = {"GET"},
uri = "/apisix/plugin/example-plugin2/hello",
handler = hello,
}
}
end
这里和官网的略有不同,官网中的代码是 _M.control_api()。
_M.api 可以参照官网的 public api 文档,主要作用是对外暴露接口,以供访问,我这里的返回简单写了个 hello 方法,主要用于测试。
_M.control_api() 主要作用是内部访问。