kong自定义插件(修改官方插件)
全棧工程師開發(fā)手冊(cè) (作者:欒鵬)
架構(gòu)系列文章
kong的插件安裝參考:https://blog.csdn.net/luanpeng825485697/article/details/85287291
kong官方插件的使用參考:https://blog.csdn.net/luanpeng825485697/article/details/85326831
Kong 的插件使用了一個(gè)叫 Classic 的 class 機(jī)制。所有的插件都是從 base_plugin.lua 基類上繼承而來。base_plugin.lua 定義了插件在各個(gè)階段被執(zhí)行的方法名:
每個(gè)Nginx worker 進(jìn)程啟動(dòng)時(shí)執(zhí)行。 function BasePlugin:init_worker() ngx_log(DEBUG, "executing plugin \"", self._name, "\": init_worker") end 在SSL握手的SSL證書服務(wù)階段執(zhí)行。 function BasePlugin:certificate()ngx_log(DEBUG, "executing plugin \"", self._name, "\": certificate") end 在作為重寫階段處理程序從客戶端接收時(shí)針對(duì)每個(gè)請(qǐng)求執(zhí)行。 function BasePlugin:rewrite()ngx_log(DEBUG, "executing plugin \"", self._name, "\": rewrite") end 針對(duì)客戶端的每個(gè)請(qǐng)求執(zhí)行,并在代理上游服務(wù)之前執(zhí)行。 function BasePlugin:access()ngx_log(DEBUG, "executing plugin \"", self._name, "\": access") end 從上游服務(wù)接收到所有響應(yīng)頭字節(jié)時(shí)執(zhí)行。 function BasePlugin:header_filter()ngx_log(DEBUG, "executing plugin \"", self._name, "\": header_filter") end 從上游服務(wù)接收到的響應(yīng)體的每個(gè)塊執(zhí)行。由于響應(yīng)被流回到客戶端,所以它可以超過緩沖區(qū)大小,并且通過塊被流傳輸塊。因此如果響應(yīng)大,可以多次調(diào)用該方法。 function BasePlugin:body_filter()ngx_log(DEBUG, "executing plugin \"", self._name, "\": body_filter") end 最后一個(gè)響應(yīng)字節(jié)發(fā)送到客戶端時(shí)被執(zhí)行。 function BasePlugin:log()ngx_log(DEBUG, "executing plugin \"", self._name, "\": log") end根據(jù)方法名也可以看出,這 7 個(gè)方法對(duì)應(yīng)于 OpenResty 的不同執(zhí)行階段。也就是說插件只能對(duì)外暴露出這 7 個(gè)方法名中的一個(gè)或多個(gè)才能被 Kong 的插件機(jī)制執(zhí)行,接下來 Kong 會(huì)在 OpenResty 不同的執(zhí)行階段,執(zhí)行插件對(duì)應(yīng)的方法。
自定義插件:
文件結(jié)構(gòu)
Kong 插件的文件結(jié)構(gòu)分基本插件模塊和完整插件模塊兩種,基本插件模塊結(jié)構(gòu)如下:
simple-plugin
├── handler.lua
└── schema.lua
其中,handler.lua 是插件核心,它是一個(gè)接口實(shí)現(xiàn),其中每個(gè)函數(shù)將在請(qǐng)求生命周期中的期望時(shí)刻運(yùn)行。schema.lua 用于定義插件配置
完整插件模塊結(jié)構(gòu)如下:
complete-plugin
├── api.lua
├── daos.lua
├── handler.lua
├── migrations
│ ├── cassandra.lua
│ └── postgres.lua
└── schema.lua
其各個(gè)模塊的功能如下:
| api.lua | No | 插件需要向 Admin API 暴露接口時(shí)使用 |
| daos.lua | No | 數(shù)據(jù)層相關(guān),當(dāng)插件需要訪問數(shù)據(jù)庫時(shí)配置 |
| handler.lua | Yes | 插件的主要邏輯,這個(gè)將會(huì)被 Kong 在不同階段執(zhí)行其對(duì)應(yīng)的 handler |
| migrations/*.lua | No | 插件依賴的數(shù)據(jù)表結(jié)構(gòu),啟用了 daos.lua 時(shí)需要定義 |
| schema.lua | Yes | 插件的配置參數(shù)定義,主要用于 Kong 參數(shù)驗(yàn)證 |
其中,api.lua 定義管理API操作接口;daos.lua 定義插件需要并且存儲(chǔ)在數(shù)據(jù)庫的實(shí)體的DAOs列表;migrations/*.lua 定義了給定數(shù)據(jù)存儲(chǔ)的相應(yīng)遷移,通常只有當(dāng)插件必須在數(shù)據(jù)庫中存儲(chǔ)自定義實(shí)體并通過daos.lua定義的DAO進(jìn)行交互時(shí),遷移才是必要的。
其中 handler.lua 和 schema.lua 是必需的,上面提到的插件需要暴露出來的方法就定義在 handler.lua 中。
具體關(guān)于文件結(jié)構(gòu)的描述參見Plugin Development - File Structure
邏輯實(shí)現(xiàn)
這里以request-termination熔斷為例,這是一個(gè)最簡單的示例.在kong/kong/plugins/request-termination文件夾里面.
該檢查就是為選定的服務(wù)或路由返回指定的響應(yīng)消息.響應(yīng)消息包含狀態(tài)碼status_code, 消息類型content_type,文本消息message,消息體body
邏輯實(shí)現(xiàn)
實(shí)現(xiàn)邏輯在handler.lua中實(shí)現(xiàn)
-- 執(zhí)行函數(shù). 按照配置返回固定的響應(yīng)-- 引入模塊(引入基類) 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-- 默認(rèn)的response local DEFAULT_RESPONSE = {[401] = "Unauthorized",[404] = "Not found",[405] = "Method not allowed",[500] = "An unexpected error occurred",[502] = "Bad Gateway",[503] = "Service unavailable", }-- 擴(kuò)展模塊(派生子類),其實(shí)這里是為了繼承來自 Classic 的 __call 元方法,方便 Kong 在 init 階段預(yù)加載插件的時(shí)候執(zhí)行構(gòu)造函數(shù) new() local RequestTerminationHandler = BasePlugin:extend()-- 設(shè)置插件的優(yōu)先級(jí),Kong 將按照插件的優(yōu)先級(jí)來確定其執(zhí)行順序(越大越優(yōu)先) -- 需要注意的是應(yīng)用于 Consumer 的插件因?yàn)橐蕾囉?Auth,所以 Auth 類插件優(yōu)先級(jí)普遍比較高 RequestTerminationHandler.PRIORITY = 2 RequestTerminationHandler.VERSION = "1.0.0"-- 插件的構(gòu)造函數(shù),用于初始化插件的 _name 屬性,后面會(huì)根據(jù)這個(gè)屬性打印插件名 -- 其實(shí)這個(gè)方法不是必須的,只是用于插件調(diào)試 function RequestTerminationHandler:new()RequestTerminationHandler.super.new(self, "request-termination") end-- 表明需要在 access 階段執(zhí)行此插件. 也就是在接入上游服務(wù)前就直接生成響應(yīng)數(shù)據(jù). function RequestTerminationHandler:access(conf) -- conf就是schema.lua中的config,也就是插件安裝時(shí)的配置頁面-- 執(zhí)行父類的 access 方法,其實(shí)就是為了調(diào)試時(shí)輸出日志用的RequestTerminationHandler.super.access(self)-- 接下來的就是插件的主要邏輯local status = conf.status_codelocal content = conf.body-- 如果配置了content參數(shù)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參數(shù),就直接生成message的消息體return kong.response.exit(status, { message = conf.message or DEFAULT_RESPONSE[status] }) endreturn RequestTerminationHandler參數(shù)定義
Kong 插件通過schema.lua文件定義配置。類似于 JSON Schema,主要用于描述插件參數(shù)的數(shù)據(jù)格式。
schema.lua 返回一個(gè)Table類型,包含no_consumer、fields、self_check三個(gè)屬性:
| no_consumer | Boolean | false | 如果為true將不能應(yīng)用此插件至指定消費(fèi)者,只能被應(yīng)用到 Services 或者 Routes, 例如:認(rèn)證插件 |
| fileds | Table | {} | 插件的 schema,使用一個(gè)鍵值對(duì)定義可用屬性和他們的規(guī)則 |
| self_check | Function | nil | 如果在接受插件配置之前需要進(jìn)行自定義驗(yàn)證,需要實(shí)現(xiàn)此函數(shù) |
schema.lua 文件樣本如下:
--主要用于描述插件參數(shù)的數(shù)據(jù)格式. -- bashboard頁面上創(chuàng)建時(shí)需要填寫的內(nèi)容和添加插件時(shí)進(jìn)行的校驗(yàn)-- 引入模塊,賦值table給typedefs變量 local typedefs = require "kong.db.schema.typedefs"-- 自定義局部函數(shù) 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",-- 描述插件參數(shù)的數(shù)據(jù)格式,用于 Kong 驗(yàn)證參數(shù)fields = {{ status_code = {type = "integer",default = 503,between = { 100, 599 },}, },{ message = { type = "string" }, },{ content_type = { type = "string" }, },{ body = { type = "string" }, },},-- 自定義更為細(xì)粒度的參數(shù)校驗(yàn)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插件為例, 因?yàn)閗ong自帶的prometheus插件支持的度量比較少,我們需要增加一些度量內(nèi)容,
kong自帶的插件在/usr/local/share/lua/5.1/kong/plugins/prometheus目錄下. 我們將該文件夾copy出來,
api.lua和serv.lua文件中定義了暴露的接口, 接口中將匹配/metrics是返回收集的度量數(shù)據(jù).
schema.lua定義的啟動(dòng)插件時(shí)的參數(shù)檢查, 其實(shí)啟動(dòng)這個(gè)參數(shù)不需要任何參數(shù), 所以這個(gè)文件的內(nèi)容很少.
prometheus.lua文件中定義了基礎(chǔ)類Metric和派生類Counter,Gauge,Histogram以及類的方法和屬性
handler.lua文件定義了各階段執(zhí)行的函數(shù), 分別調(diào)用的是exporter.lua文件中定義的各個(gè)函數(shù).
exporter.lua文件中主要報(bào)錯(cuò)處理函數(shù), 主要為init定義度量的函數(shù), log 設(shè)置度量的值的函數(shù) collect 搜索度量返回的函數(shù).
我們關(guān)心的是log函數(shù),這個(gè)函數(shù)是message為參數(shù), 如果需要我們可以添加config也作為參數(shù), 那就需要在調(diào)用這個(gè)函數(shù)的時(shí)候?qū)onf參數(shù)傳入.
message包含了我們需要的所有內(nèi)容, 下面是他的消息體格式
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插件 添加限速同時(shí)能指定黑白名單
有時(shí)我們需要對(duì)黑白名單進(jìn)行限速, 而官方的插件中黑白名單, 為 完全拒絕訪問的形式, 而 限速插件中, 又統(tǒng)一對(duì)所有的選定訪問客戶端, 但是有時(shí)我們想對(duì)某些ip進(jìn)行限速. 因此我們需要一個(gè)有黑白名單的限速插件.
這個(gè)以后有時(shí)間再弄吧
總結(jié)
以上是生活随笔為你收集整理的kong自定义插件(修改官方插件)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动端阻止body左右偏移
- 下一篇: 华强北耳机为啥老是有人翻车?