添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
空虚的梨子  ·  Django REST ...·  1 年前    · 
俊逸的围巾  ·  java中两个map比较 - ...·  2 年前    · 
没人理的红茶  ·  python - TypeError: ...·  2 年前    · 
坚韧的哑铃  ·  C++ 直接内存管理 new / ...·  2 年前    · 

上一篇我们学到,一个插件必须包含两个文件:

  • handler.lua
  • schema.lua

handler.lua主要负责业务逻辑功能编写,schema.lua主要负责插件参数定制,类似将handler.lua中写死的变量抽出来写到schema.lua中,变为动态注入。

既然是自定义插件,又想应用到kong中,这期间肯定会有一些规范做适配,kong也不例外,在kong中,我们可以通过继承一个base插件:
kong.plugins.base_plugin

这个base插件提供了一些方法待实现,这些方法是基于 openresty的http模块 来定义的

函数名 LUA-NGINX-MODULE Context 描述
:init_worker() init_worker_by_lua 在每个 Nginx 工作进程启动时执行
:certificate() ssl_certificate_by_lua 在SSL握手阶段的SSL证书服务阶段执行
:rewrite() rewrite_by_lua 从客户端接收作为重写阶段处理程序的每个请求执行。在这个阶段,无论是API还是消费者都没有被识别,因此这个处理器只在插件被配置为全局插件时执行
:access() access_by_lua 为客户的每一个请求而执行,并在它被代理到上游服务之前执行(路由)
:header_filter() header_filter_by_lua 从上游服务接收到所有响应头字节时执行
:body_filter() body_filter_by_lua 从上游服务接收的响应体的每个块时执行。由于响应流回客户端,它可以超过缓冲区大小,因此,如果响应较大,该方法可以被多次调用
:log() log_by_lua 当最后一个响应字节已经发送到客户端时执行

在各个阶段分别可以处理响应的业务。

另外,kong自身也提供了一些方法,有上下文的,有request、response等等的工具类,便于我们使用

PDK名称 功能描述
kong.client 提供客户端的ip, 端口等信息
kong.ctx 提供了插件之间共享并传递参数的桥梁
kong.ip 提供了 kong.ip.is_trusted(address) IP白名单检测方法
kong.log 日志方法
kong.node 返回此插件的UUID信息
kong.request 提供request信息的读取功能, access() 中可读
kong.response 提供response信息的读写功能, access() 中不可用
kong.router 返回此请求关联的router信息
kong.service 返回此请求关联的service,可以 动态修改 后端服务信息
kong.service.request 仅用于 access() 方法中,可以 读写 请求信息
kong.service.response 仅可用于 header_filter(), body_filter() 方法中,只提供 header 信息的读取功能
kong.table kong提供的一套数据结构功能

更多方法参考: PDK - v2.5.x | Kong Docs (konghq.com)

插件名称为: demo

  • handler.lua

    local BasePlugin = require "kong.plugins.base_plugin"
    local DemoHandler = BasePlugin:extend()
    DemoHandler.VERSION = "0.1.0"
    DemoHandler.PRIORITY = 2000
    -- 在 'init_by_lua_block' 中运行
    function DemoHandler:new()
        DemoHandler.super.new(self, "demo")
    -- 在 'init_worker_by_lua_block' 中运行
    function DemoHandler:init_worker()
        DemoHandler.super.init_worker(self)
    -- 在 'ssl_certificate_by_lua_block' 中运行
    function DemoHandler:certificate(conf)
        DemoHandler.super.certificate(self)
    -- 在 'rewrite_by_lua_block' 中运行
    function DemoHandler:rewrite(conf)
        DemoHandler.super.rewrite(self)
    -- 在 'access_by_lua_block' 中运行
    function DemoHandler:access(conf)
        DemoHandler.super.access(self)
        return kong.response.exit(200,conf.content)
    -- 在 'header_filter_by_lua_block' 中运行
    function DemoHandler:header_filter(conf)
        DemoHandler.super.header_filter(self)
    -- 在 'body_filter_by_lua_block' 中运行
    function DemoHandler:body_filter(conf)
        DemoHandler.super.body_filter(self)
    -- 在 'log_by_lua_block' 中运行
    function DemoHandler:log(conf)
        DemoHandler.super.log(self)
    return DemoHandler
    

    可以看到, 主要内容就是实现一个 BasePlugin:extend() ,实现一系统生命周期对应的方法后,返回这个实例即可.

    注意看一下 access() 方法那块,我添加了一些内容!!!!

    而且, 所有方法在实现的时候, 都要先调用一下父类方法.

  • schema.lua

    return {
        no_consumer = true,
        fields = {
            content = { type = "string", default = "success!!" },
    
    1. 先把代码放置到/opt/share/kong/plugins/demo位置

    2. 修改/etc/kong/kong.conf文件,加载插件

      lua_package_path = /opt/share/?.lua;;
      plugins = bundled,demo
      
    3. 重启Kong服务: kong restart

    1. 打开konga,可以看到插件的部署情况:

      image-20220113113639368.png

      底色是绿色的代表正在使用的插件!

    2. 添加插件

      在这里我需要说明一下,kong插件的作用域范围有4个

      • Consumer作用域

      • route作用域
        仅针对route配置起作用(一个route可以配置多个path,也就是说这个route下的所有path插件都生效,不是这个route的path不生效)

      • service作用域

        针对service配置起作用(一个service有多个route,只要是属于这个service下的route,插件都生效)

      • global作用域

        所有请求,插件都生效

      目前我们演示route作用域,其他感兴趣的自己可以尝试一下:

      • 添加插件

        image-20220113114746442.png

        参数详情:

        image-20220113114835770.png

      • 访问结果

        image-20220113114934463.png

    上述的实战比较简单,相信你也可以顺利完成,接下来我来说几个参坑点吧

    1. lua库扫描以及引入第三方lua库

      • 在我们编写lua脚本的时候,可能需要引入其他的库(kong自身没有提供的库),这个时候当我们在handler.lua中require的时候,则需要在kong.conf配置untrusted_lua_sandbox_requires 属性,例如:

        在handler.lua中

        local template = require "resty.template"
        local split = require "kong.tools.utils".split
        

        那么我们必须在kong.conf中配置

        untrusted_lua_sandbox_requires = resty.template, kong.tools.utils
        
      • 还有一种方案是,直接将自己写好的库跟handler.lua,schema.lua放到一个文件夹里面,然后通过下列方式来引入

        local xxx     = require "kong.plugins.插件名称.自己库的名称"
        

        例如:插件名称叫demo,自己写的库名称是redisUtil.lua,那么引入方式为:

        local redisUtil     = require "kong.plugins.demo.redisUtil"
        

        这种方案有个前提是!

        你的redisutil 库必须有返回,如果是函数,想做到通用,就必须return,并且函数不要用local修饰,例如:我想让get,put,incr这三个函数通用,我的redisutil就必须返回这三个函数

        return {
            get = get;
            put = put;
            incr = incr;
        

        如果全部封装到table里面,在最后返回时,也可以这么写:

        return _M
        
    2. schema文件拓展内容

      在schema文件中,支持的类型共有下面这些类型

       map, number, array, foreign, function, integer, boolean, record, string, set
      

      其中,在type=record的时候,这种就必须定义field,类似java中的JavaBean一样,定义属性,以及属性值

      比如:

      return {
        name = "ip-restriction",
        fields = {
          { protocols = typedefs.protocols_http },
          { config = {
            type = "record",
            fields = {
              { allow = { type = "array", elements = typedefs.ip_or_cidr, }, },
              { deny = { type = "array", elements = typedefs.ip_or_cidr, }, },
      

      这种就相当与是有一个config对象,包含两个属性,这个两个属性都是数组类型,数组内部的元素必须符合ip校验规则,其数据结构对应为:

      {
          "name": "ip-restriction",
          "config": {
              "allow": [
                  "127.0.0.1"
              "deny": [
                  "127.0.0.1"
      

      在type=map的时候

      比如:

      return {
        name = "response-ratelimiting",
        fields = {
          { protocols = typedefs.protocols_http },
          { config = {
              type = "record",
              fields = {
                { limits = {
                    type = "map",
                    required = true,
                    len_min = 1,
                    keys = { type = "string" },
                    values = {
                      type = "record",
                      required = true,
                      fields = {
                        { second = { type = "number", gt = 0 }, },
                        { minute = { type = "number", gt = 0 }, },
                        { hour = { type = "number", gt = 0 }, },
                        { day = { type = "number", gt = 0 }, },
                        { month = { type = "number", gt = 0 }, },
                        { year = { type = "number", gt = 0 }, },
      

      这种就相当于是JsonObject结构,其数据结构为:

      {
          "name": "response-ratelimiting",
          "config": {
              "limits": {
                  "second":1,
                  "minute":1,
                  "hour":1,
                  "day":1,
                  "month":1,
                  "year":1
      

      还有一些比较复杂的嵌套对象,比如:

      return {
          name = "demo-ip-restriction",
          fields = {
              { protocols = typedefs.protocols_http },
              { config = {
                  type = "record",
                  fields = {
                      { rfilter = {
                          type = "array",
                          elements = {
                              type = "record",
                              fields = {
                                  { appkey = { type = "string", required = true }, },
                                  { deny = { type = "array", elements = typedefs.ip_or_cidr, }, },
      

      这种对应的数据结构为:

      {
          "name": "demo-ip-restriction",
          "config": {
              "rfilter": [
                      "appkey": "1111",
                      "deny": [
                          "142.0.0.1/24"
                      "appkey": "2222",
                      "deny": [
                          "132.0.0.1/32"
      
    3. 部署中常见的问题
      在部署插件中我们可能会遇到这么几个问题“

      • plugin is in use but not enabled;

        这个是因为你之前配置了一个自定义插件,并在某种场合使用了该插件,后来由于某种原因,你下架了该自定义插件,重启kong后,就会报该错误;

        想解决很简单,在数据库plugins中删除使用添加该插件的节点

      • plugin is enabled but not installed;

        这个是因为你在kong.conf中配置了自定义插件(plugins = bundled,demo),没有配置lua源(lua_package_path = /opt/share/?.lua;;)

    4. 多个作用域插件的执行顺序

      在上文我们提到插件可以作用域在consumer,route,service,global

      当多个作用域的插件出现在一个场景时,他们的优先级法则为:一个插件相对于它所配置的实体数量越具体,它的优先级就越高。

      多次配置插件时的完整优先级顺序为:

      1. 在以下组合上配置的插件:route、service和consumer。(consumer表示请求必须经过身份验证)。
      2. 在route和consumer的组合上配置的插件。(consumer表示请求必须经过身份验证)。
      3. 在service和consumer的组合上配置的插件。(consumer表示请求必须经过身份验证)。
      4. 在route和service的组合上配置的插件。
      5. 在consumer上配置的插件。(consumer表示请求必须经过身份验证)。
      6. 在route上配置的插件。
      7. 在service上配置的插件。
      8. 配置为global运行的插件。

      示例:如果插件应用两次(具有不同的配置):对于service(插件配置 A)和对于consumer(插件配置 B),则验证此consumer身份的请求将运行插件配置 B 并忽略 A。但是,不验证此consumer身份的请求将回退到运行插件配置 A。请注意,如果禁用了配置 B(其标志设置为 ),则配置 A 将应用于与配置 B 匹配的请求。rate-limiting``enabled``false

      官网解释:Admin API - v2.7.x | Kong Docs (konghq.com)

    5. 多个插件的执行顺序

      当一个路径涉及到多个插件时,有时候后一个插件需要依赖前一个插件的数据,所以出现了优先级问题,这个时候你可以通过

      CustomHandler.PRIORITY = 10
      

      来设置你的插件优先级

      优先级越高,相对于其他插件的阶段(如:access()、:log()等),插件的阶段执行得越快。 已有捆绑插件的当前执行顺序为:

      插件优先级
      pre-function+inf
      zipkin100000
      ip-restriction3000
      bot-detection2500
      cors25000
      jwt1005
      oauth21004
      key-auth1003
      ldap-auth1002
      basic-auth1001
      hmac-auth1000
      request-size-limiting951
      acl950
      rate-limiting901
      response-ratelimiting900
      request-transformer801
      response-transformer800
      aws-lambda750
      azure-functions749
      prometheus13
      http-log12
      statsd11
      datadog10
      file-log9
      udp-log8
      tcp-log7
      loggly6
      syslog4
      galileo3
      request-termination2
      correlation-id1
      post-function-1000
    本文通过一个自定义插件的编写,来了解Kong的插件机制插件功能:原本返回百度首页,在添加插件之后,返回插件自定义的内容;前提使用kong代理百度的接口,配置route路径;了解kong 插件的目录结构及部署(上一篇说到的)实战上一篇我们学到,一个插件必须包含两个文件:handler.luaschema.luahandler.lua主要负责业务逻辑功能编写,schema.lua主要负责插件参数定制,类似将handler.lua中写死的变量抽出来写到schema.lua中,变为动 Kong是一个api网关,在客户端和服务间转发API进行通信,支持自定义插件来扩展功能。 Kong 是在Nginx基础上构建的,更确切地说,kong是在Nginx中运行的Lua应用程序,由 lua-nginx-module实现。 Kong和OpenResty一起发行的,其中已经包含了lua-nginx=module。其中OpenResty不是Nginx的分支,而是...
    kong插件安装参考:https://blog.csdn.net/luanpeng825485697/article/details/85287291 kong官方插件的使用参考:https://blog.csdn.net/luanpeng825485697/article/details/85326831 自定义插件: Kong 插件的文件结构分基本插件模块和完整插件模块两种,基本插...
    插件概述 插件之于kong,就像Spring中的aop功能。 在请求到达kong之后,转发给后端应用之前,你可以应用kong自带的插件对请求进行处理,合法认证,限流控制,黑白名单校验,日志采集等等。同时,你也可以按照kong的教程文档,定制开发属于自己的插件kong插件分为开源版和社区版,社区版还有更多的定制功能,但是社区版是要收费的。 目前,KONG开源版本一共开放28个插件,如下: ac...
    ==========================================Installation guide for Kong custom plugins Kong version 0.10.0| Latest revision | 2017/03/24 |Custom plugins for Kong consist of Lua source files that need
    此文章记录一下Kong自定义插件初次编写的过程,如果文章中有错误请及时提醒并帮忙更正 安装开发环境 这里使用Pongo 来进行插件的测试,而Pongo的使用需要具备docker-compose和curl,再这些都具备之后就可以安装Pongo CLI了 PATH=$PATH:~/.local/bingit clone https://github.com/Kong/kong-pongo.gitmkdir -p ~/.local/binln -s $
    按照公司要求编写的一个kong自定义插件,实现解析jwt,然后查询redis数据库验证身份,最后将解析参数放入header传入上游服务器 项目已托管GitHubhttps://github.com/MyRong12138/my-jwt/ local BasePlugin = require "kong.plugins.base_plugin" -- 调用模块 local zhttp = require "resty.http" #一个resty 包,需要下载到指定目录 local XngAuthHandler = BasePlugin:extend() local timeout = 5000 local uri = "http://localhost:8500/auth/v1/api/va...
    好的,kong 的配置文件使用的是 TOML 格式,TOML 是一种用于配置文件的简单语法,它很容易被人类阅读和写入。 kong 配置文件中包含了各种设置选项,可以用来配置 Kong 的运行参数,例如数据库连接、插件设置、日志级别等。 一个基本的 kong 配置文件看起来像这样: # 配置数据库连接 [database] type = "postgres" host = "localhost" port = 5432 database = "kong" user = "kong" password = "kong" # 配置日志级别 [log] level = "info" 在配置文件中,每个设置选项都包含在一个方括号内,方括号内的名称是设置选项的类别,比如上面的 `[database]` 和 `[log]` 就是两个设置选项的类别。每个设置选项都是一个名称-值对,名称和值之间用等号 (=) 分隔。 如果你想了解更多关于 kong 配置文件的信息,可以参考 kong 的官方文档:https://docs.konghq.com/1.5.x/configuration/ 。