Kong插件的目录结构

一个基本的Kong插件目录结构如下:

simple-plugin
├── handler.lua --通过实现```init_worker()```、```access()```等方法来实现自己的功能
└── schema.lua --存储插件配置的数据结构
```lua
local BasePlugin = require "kong.plugins.base_plugin"
local CustomHandler = BasePlugin:extend()
function CustomHandler:new()
  CustomHandler.super.new(self, "my-custom-plugin")
end
function CustomHandler:init_worker()
  CustomHandler.super.init_worker(self)
end
function CustomHandler:certificate(config)
  CustomHandler.super.certificate(self)
end
function CustomHandler:rewrite(config)
  CustomHandler.super.rewrite(self)
end
function CustomHandler:access(config)
  CustomHandler.super.access(self)
end
function CustomHandler:header_filter(config)
  CustomHandler.super.header_filter(self)
end
function CustomHandler:body_filter(config)
  CustomHandler.super.body_filter(self)
end
function CustomHandler:log(config)
  CustomHandler.super.log(self)
end
CustomHandler.PRIORITY = 10 --控制插件优先级
return CustomHandler

插件的加载执行流程

整个插件的加载执行流程主要集中在kong/init.luakong/core/plugins_iterator这两个文件中

插件的加载

整个加载过程在init.lua文件的load_plugins(kong_conf, dao)方法中。首先校验数据库正在使用的插件,配置文件中是否启用了;然后利用lua动态脚本的特性,将启用的插件元数据保存起来;最后根据优先级对插件进行排序,存储到全局的缓存中

for plugin in pairs(kong_conf.plugins) do
  local ok, handler = utils.load_module_if_exists("kong.plugins." .. plugin .. ".handler")
  if not ok then
    return nil, plugin .. " plugin is enabled but not installed;\n" .. handler
  end
  local ok, schema = utils.load_module_if_exists("kong.plugins." .. plugin .. ".schema")
  if not ok then
    return nil, "no configuration schema found for plugin: " .. plugin
  end
  sorted_plugins[#sorted_plugins+1] = {
    name = plugin,
    handler = handler(),
    schema = schema
  }
end
function _M.load_module_if_exists(module_name)
  local status, res = pcall(require, module_name)
  ---省略加载结果校验
end

插件的执行

kong插件的执行比较简单,主要在nginx请求的各个阶段循环调用插件的相应方法即可

for plugin, plugin_conf in plugins_iterator(singletons.loaded_plugins, true) do
  plugin.handler:rewrite(plugin_conf)
end


需要注意的点是,插件的实例在plugins_iterator中加载的。插件有全局插件,也有API插件和Consumer插件,而API和ConsumerId是需要在access阶段(access阶段才根据host、method、uri等信息获取到api)才能获取到,因此access阶段之前只能执行全局的插件。

小结

总的来说,Kong的插件还是比较简单和优雅的,基于nginx提供的执行阶段稍作封装就实现了一套灵活的插件系统。