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 在SSL握手的SSL證書服務階段執行。 function BasePlugin:certificate()ngx_log(DEBUG, "executing plugin \"", self._name, "\": certificate") end 在作為重寫階段處理程序從客戶端接收時針對每個請求執行。 function BasePlugin:rewrite()ngx_log(DEBUG, "executing plugin \"", self._name, "\": rewrite") end 針對客戶端的每個請求執行,并在代理上游服務之前執行。 function BasePlugin:access()ngx_log(DEBUG, "executing plugin \"", self._name, "\": access") end 從上游服務接收到所有響應頭字節時執行。 function BasePlugin:header_filter()ngx_log(DEBUG, "executing plugin \"", self._name, "\": header_filter") end 從上游服務接收到的響應體的每個塊執行。由于響應被流回到客戶端,所以它可以超過緩沖區大小,并且通過塊被流傳輸塊。因此如果響應大,可以多次調用該方法。 function BasePlugin:body_filter()ngx_log(DEBUG, "executing plugin \"", self._name, "\": body_filter") end 最后一個響應字節發送到客戶端時被執行。 function BasePlugin:log()ngx_log(DEBUG, "executing plugin \"", self._name, "\": log") end根據方法名也可以看出,這 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
其各個模塊的功能如下:
| api.lua | No | 插件需要向 Admin API 暴露接口時使用 |
| daos.lua | No | 數據層相關,當插件需要訪問數據庫時配置 |
| handler.lua | Yes | 插件的主要邏輯,這個將會被 Kong 在不同階段執行其對應的 handler |
| migrations/*.lua | No | 插件依賴的數據表結構,啟用了 daos.lua 時需要定義 |
| schema.lua | Yes | 插件的配置參數定義,主要用于 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_codelocal content = conf.body-- 如果配置了content參數if content thenlocal headers = {["Content-Type"] = conf.content_type}if singletons.configuration.enabled_headers[constants.HEADERS.SERVER] thenheaders[constants.HEADERS.SERVER] = server_headerendreturn kong.response.exit(status, content, headers)end-- 如果沒有配置content參數,就直接生成message的消息體return kong.response.exit(status, { message = conf.message or DEFAULT_RESPONSE[status] }) endreturn RequestTerminationHandler參數定義
Kong 插件通過schema.lua文件定義配置。類似于 JSON Schema,主要用于描述插件參數的數據格式。
schema.lua 返回一個Table類型,包含no_consumer、fields、self_check三個屬性:
| no_consumer | Boolean | false | 如果為true將不能應用此插件至指定消費者,只能被應用到 Services 或者 Routes, 例如:認證插件 |
| fileds | Table | {} | 插件的 schema,使用一個鍵值對定義可用屬性和他們的規則 |
| self_check | Function | nil | 如果在接受插件配置之前需要進行自定義驗證,需要實現此函數 |
schema.lua 文件樣本如下:
--主要用于描述插件參數的數據格式. -- bashboard頁面上創建時需要填寫的內容和添加插件時進行的校驗-- 引入模塊,賦值table給typedefs變量 local typedefs = require "kong.db.schema.typedefs"-- 自定義局部函數 local is_present = function(v)return type(v) == "string" and #v > 0 -- # 表示取長度 endreturn {-- 插件名稱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))thenreturn nil, "message cannot be used with content_type or body"endif is_present(config.content_type) and not is_present(config.body)thenreturn nil, "content_type requires a body"endreturn trueend,},},}, }修改自帶插件(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 103kong 99proxy 3}service{host license-service.cloudai-2created_at 1547185659connect_timeout 60000id 2641f0cb-c604-48e2-9d5a-0dbe13fd5274protocol httpname licenseread_timeout 60000port 8080updated_at 1547185659retries 5write_timeout 60000}request{querystring{}size 351uri /license/signurl http://192.168.11.127:8443/license/signheaders{host 192.168.11.127:32443content-type application/jsonpostman-token 00f49660-5920-432d-adcc-e98721e27e8baccept */*x-b3-parentspanid 0f64da2e24dd4ac6cache-control no-cachecontent-length 57accept-encoding gzip, deflateuser-agent PostmanRuntime/7.4.0x-b3-traceid 2aa7eebf34ea4f44ea201f10ff36da94x-lantern-version 5.2.0x-b3-spanid 096d7f0b78a0926ax-b3-sampled 1}method POST}tries{{balancer_latency 0port 8080balancer_start 1547452777442ip 10.233.56.89}}client_ip 10.233.65.0api{}upstream_uri /signresponse{headers{content-type application/json; charset=utf-8date Mon, 14 Jan 2019 07:59:37 GMTconnection closex-ratelimit-limit-second 1via kong/0.14.1x-kong-proxy-latency 100content-length 225x-kong-upstream-latency 3x-ratelimit-remaining-second 0server Python/3.6 aiohttp/3.5.1cookie --cookie aicloud-cookie=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZXNpb25ib29rIiwiaWF0IjoxNTQ3NDUyNzc3LCJuYmYiOjE1NDc0NTI3NzcsImV4cCI6MTU0NzQ1NjM3N30.fpaSSLuh7TSN9igMzMyIMLXPeAXo3aYBNhq67i3UV2Eaccess-control-allow-methods GET,POSTaccess-control-allow-origin *}status 200size 824}route{created_at 1547185719strip_path truehosts{}preserve_host falseregex_priority 0updated_at 1547185719paths{1 /license}service{id 2641f0cb-c604-48e2-9d5a-0dbe13fd5274}methods{1 GET2 POST}protocols{1 http}id 24be26d7-40e1-487d-9f84-8cca937a02b6}consumer{custom_id vesionbookcreated_at 1547185392username vesionbookid 711171ba-817e-4a9a-8b93-2c089e5a52b0} }我們按照官方的樣子,模擬就可以添加自己的度量了.
修改rate-limiting插件 添加限速同時能指定黑白名單
有時我們需要對黑白名單進行限速, 而官方的插件中黑白名單, 為 完全拒絕訪問的形式, 而 限速插件中, 又統一對所有的選定訪問客戶端, 但是有時我們想對某些ip進行限速. 因此我們需要一個有黑白名單的限速插件.
這個以后有時間再弄吧
總結
以上是生活随笔為你收集整理的kong自定义插件(修改官方插件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动端阻止body左右偏移
- 下一篇: 华强北耳机为啥老是有人翻车?