当前位置:   article > 正文

kong自定义插件(修改官方插件)

kong自定义插件

全栈工程师开发手册 (作者:栾鹏)
架构系列文章


kong的插件安装参考:https://blog.csdn.net/luanpeng825485697/article/details/85287291

kong官方插件的使用参考:https://blog.csdn.net/luanpeng825485697/article/details/85326831

Kong 的插件使用了一个叫 Classic 的 class 机制。所有的插件都是从 base_plugin.lua 基类上继承而来。base_plugin.lua 定义了插件在各个阶段被执行的方法名:

每个Nginx worker 进程启动时执行。
function BasePlugin:init_worker()     
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": init_worker")
end
  • 1
  • 2
  • 3
  • 4
在SSL握手的SSL证书服务阶段执行。
function BasePlugin:certificate()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": certificate")
end
  • 1
  • 2
  • 3
  • 4
在作为重写阶段处理程序从客户端接收时针对每个请求执行。
function BasePlugin:rewrite()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": rewrite")
end
  • 1
  • 2
  • 3
  • 4
针对客户端的每个请求执行,并在代理上游服务之前执行。
function BasePlugin:access()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": access")
end
  • 1
  • 2
  • 3
  • 4
从上游服务接收到所有响应头字节时执行。
function BasePlugin:header_filter()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": header_filter")
end
  • 1
  • 2
  • 3
  • 4
从上游服务接收到的响应体的每个块执行。由于响应被流回到客户端,所以它可以超过缓冲区大小,并且通过块被流传输块。因此如果响应大,可以多次调用该方法。
function BasePlugin:body_filter()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": body_filter")
end
  • 1
  • 2
  • 3
  • 4
最后一个响应字节发送到客户端时被执行。
function BasePlugin:log()
  ngx_log(DEBUG, "executing plugin \"", self._name, "\": log")
end
  • 1
  • 2
  • 3
  • 4

根据方法名也可以看出,这 7 个方法对应于 OpenResty 的不同执行阶段。也就是说插件只能对外暴露出这 7 个方法名中的一个或多个才能被 Kong 的插件机制执行,接下来 Kong 会在 OpenResty 不同的执行阶段,执行插件对应的方法。

自定义插件:

文件结构
Kong 插件的文件结构分基本插件模块和完整插件模块两种,基本插件模块结构如下:

simple-plugin
├── handler.lua
└── schema.lua

其中,handler.lua 是插件核心,它是一个接口实现,其中每个函数将在请求生命周期中的期望时刻运行。schema.lua 用于定义插件配置

完整插件模块结构如下:

complete-plugin
├── api.lua
├── daos.lua
├── handler.lua
├── migrations
│ ├── cassandra.lua
│ └── postgres.lua
└── schema.lua

其各个模块的功能如下:

Module nameRequiredDescription
api.luaNo插件需要向 Admin API 暴露接口时使用
daos.luaNo数据层相关,当插件需要访问数据库时配置
handler.luaYes插件的主要逻辑,这个将会被 Kong 在不同阶段执行其对应的 handler
migrations/*.luaNo插件依赖的数据表结构,启用了 daos.lua 时需要定义
schema.luaYes插件的配置参数定义,主要用于 Kong 参数验证

其中,api.lua 定义管理API操作接口;daos.lua 定义插件需要并且存储在数据库的实体的DAOs列表;migrations/*.lua 定义了给定数据存储的相应迁移,通常只有当插件必须在数据库中存储自定义实体并通过daos.lua定义的DAO进行交互时,迁移才是必要的。

其中 handler.lua 和 schema.lua 是必需的,上面提到的插件需要暴露出来的方法就定义在 handler.lua 中。

具体关于文件结构的描述参见Plugin Development - File Structure

逻辑实现

这里以request-termination熔断为例,这是一个最简单的示例.在kong/kong/plugins/request-termination文件夹里面.

该检查就是为选定的服务或路由返回指定的响应消息.响应消息包含状态码status_code, 消息类型content_type,文本消息message,消息体body

逻辑实现

实现逻辑在handler.lua中实现


-- 执行函数. 按照配置返回固定的响应


-- 引入模块(引入基类)
local BasePlugin = require "kong.plugins.base_plugin"
local singletons = require "kong.singletons"
local constants = require "kong.constants"
local meta = require "kong.meta"

-- 局部变量
local kong = kong
local server_header = meta._SERVER_TOKENS

-- 默认的response
local DEFAULT_RESPONSE = {
  [401] = "Unauthorized",
  [404] = "Not found",
  [405] = "Method not allowed",
  [500] = "An unexpected error occurred",
  [502] = "Bad Gateway",
  [503] = "Service unavailable",
}

-- 扩展模块(派生子类),其实这里是为了继承来自 Classic 的 __call 元方法,方便 Kong 在 init 阶段预加载插件的时候执行构造函数 new()
local RequestTerminationHandler = BasePlugin:extend()

-- 设置插件的优先级,Kong 将按照插件的优先级来确定其执行顺序(越大越优先)
-- 需要注意的是应用于 Consumer 的插件因为依赖于 Auth,所以 Auth 类插件优先级普遍比较高
RequestTerminationHandler.PRIORITY = 2
RequestTerminationHandler.VERSION = "1.0.0"

-- 插件的构造函数,用于初始化插件的 _name 属性,后面会根据这个属性打印插件名
-- 其实这个方法不是必须的,只是用于插件调试
function RequestTerminationHandler:new()
  RequestTerminationHandler.super.new(self, "request-termination")
end

-- 表明需要在 access 阶段执行此插件. 也就是在接入上游服务前就直接生成响应数据.
function RequestTerminationHandler:access(conf)   -- conf就是schema.lua中的config,也就是插件安装时的配置页面
  -- 执行父类的 access 方法,其实就是为了调试时输出日志用的
  RequestTerminationHandler.super.access(self)

  -- 接下来的就是插件的主要逻辑
  local status  = conf.status_code
  local content = conf.body
 -- 如果配置了content参数
  if content then
    local headers = {
      ["Content-Type"] = conf.content_type
    }

    if singletons.configuration.enabled_headers[constants.HEADERS.SERVER] then
      headers[constants.HEADERS.SERVER] = server_header
    end

    return kong.response.exit(status, content, headers)
  end
  -- 如果没有配置content参数,就直接生成message的消息体
  return kong.response.exit(status, { message = conf.message or DEFAULT_RESPONSE[status] })
end


return RequestTerminationHandler

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

参数定义

Kong 插件通过schema.lua文件定义配置。类似于 JSON Schema,主要用于描述插件参数的数据格式。

schema.lua 返回一个Table类型,包含no_consumer、fields、self_check三个属性:

属性名Lua 类型默认值描述
no_consumerBooleanfalse如果为true将不能应用此插件至指定消费者,只能被应用到 Services 或者 Routes, 例如:认证插件
filedsTable{}插件的 schema,使用一个键值对定义可用属性和他们的规则
self_checkFunctionnil如果在接受插件配置之前需要进行自定义验证,需要实现此函数

schema.lua 文件样本如下:

--主要用于描述插件参数的数据格式.
-- bashboard页面上创建时需要填写的内容和添加插件时进行的校验


-- 引入模块,赋值table给typedefs变量
local typedefs = require "kong.db.schema.typedefs"

-- 自定义局部函数
local is_present = function(v)
  return type(v) == "string" and #v > 0   -- # 表示取长度
end


return {
  -- 插件名称
  name = "request-termination",

  fields = {
    { run_on = typedefs.run_on_first },
    { config = {
        type = "record",
      -- 描述插件参数的数据格式,用于 Kong 验证参数
        fields = {
          { status_code = {
            type = "integer",
            default = 503,
            between = { 100, 599 },
          }, },
          { message = { type = "string" }, },
          { content_type = { type = "string" }, },
          { body = { type = "string" }, },
        },
      -- 自定义更为细粒度的参数校验
        custom_validator = function(config)
          if is_present(config.message) and (is_present(config.content_type) or is_present(config.body))
          then
            return nil, "message cannot be used with content_type or body"
          end
          if is_present(config.content_type) and not is_present(config.body)
          then
            return nil, "content_type requires a body"
          end
          return true
        end,
      },
    },
  },
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

修改自带插件(prometheus插件为例)

这里以prometheus插件为例, 因为kong自带的prometheus插件支持的度量比较少,我们需要增加一些度量内容,
kong自带的插件在/usr/local/share/lua/5.1/kong/plugins/prometheus目录下. 我们将该文件夹copy出来,

api.lua和serv.lua文件中定义了暴露的接口, 接口中将匹配/metrics是返回收集的度量数据.

schema.lua定义的启动插件时的参数检查, 其实启动这个参数不需要任何参数, 所以这个文件的内容很少.

prometheus.lua文件中定义了基础类Metric和派生类Counter,Gauge,Histogram以及类的方法和属性

handler.lua文件定义了各阶段执行的函数, 分别调用的是exporter.lua文件中定义的各个函数.

exporter.lua文件中主要报错处理函数, 主要为init定义度量的函数, log 设置度量的值的函数 collect 搜索度量返回的函数.

我们关心的是log函数,这个函数是message为参数, 如果需要我们可以添加config也作为参数, 那就需要在调用这个函数的时候将conf参数传入.

message包含了我们需要的所有内容, 下面是他的消息体格式

message{
 latencies{
   request 103
   kong 99
   proxy 3
 }


 service{
   host license-service.cloudai-2
   created_at 1547185659
   connect_timeout 60000
   id 2641f0cb-c604-48e2-9d5a-0dbe13fd5274
   protocol http
   name license
   read_timeout 60000
   port 8080
   updated_at 1547185659
   retries 5
   write_timeout 60000
 }


 request{
   querystring{
   }
   size 351
   uri /license/sign
   url http://192.168.11.127:8443/license/sign
   headers{
     host 192.168.11.127:32443
     content-type application/json
     postman-token 00f49660-5920-432d-adcc-e98721e27e8b
     accept */*
     x-b3-parentspanid 0f64da2e24dd4ac6
     cache-control no-cache
     content-length 57
     accept-encoding gzip, deflate
     user-agent PostmanRuntime/7.4.0
     x-b3-traceid 2aa7eebf34ea4f44ea201f10ff36da94
     x-lantern-version 5.2.0
     x-b3-spanid 096d7f0b78a0926a
     x-b3-sampled 1
   }
   method POST
 }


 tries{
   {
     balancer_latency 0
     port 8080
     balancer_start 1547452777442
     ip 10.233.56.89
   }
 }


 client_ip 10.233.65.0

 api{
 }

 upstream_uri /sign

 response{
   headers{
     content-type application/json; charset=utf-8
     date Mon, 14 Jan 2019 07:59:37 GMT
     connection close
     x-ratelimit-limit-second 1
     via kong/0.14.1
     x-kong-proxy-latency 100
     content-length 225
     x-kong-upstream-latency 3
     x-ratelimit-remaining-second 0
     server Python/3.6 aiohttp/3.5.1
     cookie --cookie aicloud-cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZXNpb25ib29rIiwiaWF0IjoxNTQ3NDUyNzc3LCJuYmYiOjE1NDc0NTI3NzcsImV4cCI6MTU0NzQ1NjM3N30.fpaSSLuh7TSN9igMzMyIMLXPeAXo3aYBNhq67i3UV2E
     access-control-allow-methods GET,POST
     access-control-allow-origin *
   }
   status 200
   size 824
 }


 route{
   created_at 1547185719
   strip_path true
   hosts{
   }
   preserve_host false
   regex_priority 0
   updated_at 1547185719
   paths{
     1 /license
   }
   service{
     id 2641f0cb-c604-48e2-9d5a-0dbe13fd5274
   }
   methods{
     1 GET
     2 POST
   }
   protocols{
     1 http
   }
   id 24be26d7-40e1-487d-9f84-8cca937a02b6
 }



 consumer{
   custom_id vesionbook
   created_at 1547185392
   username vesionbook
   id 711171ba-817e-4a9a-8b93-2c089e5a52b0
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119

我们按照官方的样子,模拟就可以添加自己的度量了.

修改rate-limiting插件 添加限速同时能指定黑白名单

有时我们需要对黑白名单进行限速, 而官方的插件中黑白名单, 为 完全拒绝访问的形式, 而 限速插件中, 又统一对所有的选定访问客户端, 但是有时我们想对某些ip进行限速. 因此我们需要一个有黑白名单的限速插件.

这个以后有时间再弄吧

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/956463
推荐阅读
相关标签
  

闽ICP备14008679号