Kong 插件,解决2.5.1不支持原生暴露url

lua脚本

建目录custom-latency,编辑文件,打zip包为

Lua 复制代码
file schema.lua

local typedefs = require "kong.db.schema.typedefs"

return {
  name = "custom-latency",
  fields = {
    { consumer = typedefs.no_consumer },
    { protocols = typedefs.protocols_http },
    { config = { type = "record", fields = {} } },
  },
}

file handler.lua

-- custom-latency 插件
-- 在 /metrics 上追加带 uri/method/status/service 标签的延迟与请求数指标
-- 复用 kong.plugins.prometheus.exporter 的 prometheus 实例(Kong 2.5.1 实测 API)

local kong_prometheus = require "kong.plugins.prometheus.exporter"

local CustomLatency = {
  PRIORITY = 899,
  VERSION = "1.0.0",
}

local pm
local uri_histogram
local uri_counter

-- 将路径中的数字段归一化为 :id,避免同类型接口因参数不同产生过多标签
-- 注意:ngx.re.* 使用 PCRE 正则,数字需用 \d 而非 Lua pattern 的 %d
local function normalize_uri(uri)
  return ngx.re.gsub(uri or "", "/\\d+", "/:id", "jo")
end

-- 取当前请求命中的 service 名({env}-{service} 命名,前缀即环境),匹配不到返回空串
local function get_service_name()
  local srvc = kong.router.get_service()
  if not srvc then
    return ""
  end
  return srvc.name or ""
end

function CustomLatency:init_worker()
  pm = kong_prometheus.get_prometheus()
  if not pm then
    ngx.log(ngx.ERR, "[custom-latency] prometheus exporter not initialized")
    return
  end

  -- 注意:exporter 会自动加 "kong_" 前缀,这里不能再带前缀
  uri_histogram = pm:histogram(
    "uri_latency_ms",
    "Request latency in milliseconds, labeled by uri, method, status and service",
    { "uri", "method", "status", "service" },
    { 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000 }
  )

  uri_counter = pm:counter(
    "uri_requests_total",
    "Total number of requests, labeled by uri, method, status and service",
    { "uri", "method", "status", "service" }
  )
end

function CustomLatency:log()
  if not pm or not uri_histogram or not uri_counter then
    return
  end

  local latency_ms = (ngx.var.request_time or 0) * 1000
  local uri = normalize_uri(kong.request.get_path() or "")
  local method = kong.request.get_method() or ""
  local status = tostring(ngx.status or 0)
  local service = get_service_name()
  local labels = { uri, method, status, service }

  uri_histogram:observe(latency_ms, labels)
  uri_counter:inc(1, labels)
end

return CustomLatency

prometheus 配置

bash 复制代码
- job_name: 'kong'
  static_configs:
    - targets: ['127.0.0.1:2001']
      labels:
        instance_service_name: kong

Grafana 配置

bash 复制代码
{
  "annotations": {
    "list": []
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 1,
  "id": null,
  "links": [],
  "panels": [
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "palette-classic" },
          "custom": {
            "drawStyle": "line",
            "fillOpacity": 8,
            "lineInterpolation": "smooth",
            "lineWidth": 1,
            "showPoints": "never",
            "spanNulls": true
          },
          "min": 0,
          "unit": "reqps"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 },
      "id": 1,
      "options": {
        "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
        "tooltip": { "mode": "multi", "sort": "desc" }
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "sum by (uri, method) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))",
          "legendFormat": "{{method}} {{uri}}",
          "refId": "A"
        }
      ],
      "title": "接口请求 QPS",
      "type": "timeseries"
    },
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "palette-classic" },
          "custom": {
            "drawStyle": "line",
            "fillOpacity": 8,
            "lineInterpolation": "smooth",
            "lineWidth": 2,
            "showPoints": "never",
            "spanNulls": true
          },
          "min": 0,
          "unit": "ms"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 },
      "id": 2,
      "options": {
        "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
        "tooltip": { "mode": "multi", "sort": "desc" }
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "histogram_quantile(0.95, sum by (uri, le) (rate(kong_uri_latency_ms_bucket{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])))",
          "legendFormat": "{{uri}}",
          "refId": "A"
        }
      ],
      "title": "接口 P95 响应时间",
      "type": "timeseries"
    },
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "palette-classic" },
          "custom": {
            "drawStyle": "line",
            "fillOpacity": 8,
            "lineInterpolation": "smooth",
            "lineWidth": 1,
            "showPoints": "never",
            "spanNulls": true
          },
          "min": 0,
          "unit": "ms"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 9 },
      "id": 4,
      "options": {
        "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
        "tooltip": { "mode": "multi", "sort": "desc" }
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "sum by (uri) (rate(kong_uri_latency_ms_sum{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])) / sum by (uri) (rate(kong_uri_latency_ms_count{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))",
          "legendFormat": "avg {{uri}}",
          "refId": "A"
        }
      ],
      "title": "接口平均响应时间",
      "type": "timeseries"
    },
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "continuous-reds" },
          "custom": {
            "drawStyle": "line",
            "fillOpacity": 8,
            "lineInterpolation": "smooth",
            "lineWidth": 1,
            "showPoints": "never",
            "spanNulls": true
          },
          "min": 0,
          "unit": "percent"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 9 },
      "id": 5,
      "options": {
        "legend": { "calcs": ["lastNotNull", "max"], "displayMode": "table", "placement": "bottom" },
        "tooltip": { "mode": "multi", "sort": "desc" }
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "100 * sum by (uri) (rate(kong_uri_requests_total{status=~\"5..\", method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])) / clamp_min(sum by (uri) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])), 0.0001)",
          "legendFormat": "{{uri}}",
          "refId": "A"
        }
      ],
      "title": "接口 5xx 错误率",
      "type": "timeseries"
    },
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "palette-classic" },
          "unit": "reqps"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 24, "x": 0, "y": 17 },
      "id": 6,
      "options": {
        "displayMode": "gradient",
        "orientation": "horizontal",
        "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
        "showUnfilled": true
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "topk(10, sum by (uri) (rate(kong_uri_requests_total{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval])))",
          "legendFormat": "{{uri}}",
          "refId": "A"
        }
      ],
      "title": "请求量 Top10",
      "type": "bargauge"
    },
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "fieldConfig": {
        "defaults": {
          "color": { "mode": "continuous-BlYlRd" },
          "unit": "ms"
        },
        "overrides": []
      },
      "gridPos": { "h": 8, "w": 24, "x": 0, "y": 25 },
      "id": 7,
      "options": {
        "displayMode": "gradient",
        "orientation": "horizontal",
        "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false },
        "showUnfilled": true
      },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "prometheus" },
          "expr": "topk(10, histogram_quantile(0.95, sum by (uri, le) (rate(kong_uri_latency_ms_bucket{method=~\"$method\", uri=~\"$uri\", service=~\"$env-.*\"}[$__rate_interval]))))",
          "legendFormat": "{{uri}}",
          "refId": "A"
        }
      ],
      "title": "P95 耗时 Top10(最慢接口)",
      "type": "bargauge"
    }
  ],
  "refresh": "10s",
  "schemaVersion": 39,
  "tags": ["kong"],
  "templating": {
    "list": [
      {
        "current": {
          "text": "dev-current",
          "value": "dev-current",
          "selected": true
        },
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "definition": "label_values(kong_uri_requests_total, service)",
        "includeAll": true,
        "allValue": ".*",
        "multi": false,
        "name": "env",
        "label": "环境",
        "options": [],
        "query": "label_values(kong_uri_requests_total, service)",
        "regex": "/(.*)-[a-z0-9]+$/",
        "refresh": 1,
        "type": "query"
      },
      {
        "current": { "text": "All", "value": "$__all", "selected": true },
        "includeAll": true,
        "allValue": ".*",
        "multi": false,
        "name": "method",
        "label": "方法",
        "options": [
          { "text": "All", "value": "$__all", "selected": true },
          { "text": "GET", "value": "GET", "selected": false },
          { "text": "POST", "value": "POST", "selected": false }
        ],
        "query": "GET,POST",
        "refresh": 1,
        "type": "custom"
      },
      {
        "current": { "text": "All", "value": "$__all" },
        "datasource": { "type": "prometheus", "uid": "prometheus" },
        "definition": "label_values(kong_uri_requests_total, uri)",
        "includeAll": true,
        "allValue": ".*",
        "multi": true,
        "name": "uri",
        "label": "接口路径",
        "options": [],
        "query": "label_values(kong_uri_requests_total, uri)",
        "refresh": 1,
        "type": "query"
      }
    ]
  },
  "time": { "from": "now-1h", "to": "now" },
  "timepicker": { "refresh_intervals": ["5s", "10s", "30s", "1m", "5m"] },
  "timezone": "browser",
  "title": "Kong 接口响应时间监控(按环境)",
  "uid": "kong-uri-metrics-cloud",
  "version": 1
}
相关推荐
heimeiyingwang13 天前
【中台·技术篇】API 网关建设:Kong 与 APISIX 选型与落地实践
kong·apisix·中台
写后端的胖头鱼15 天前
一文学会单元测试之----Junit
junit·单元测试
Java小白笔记19 天前
Java 单元测试怎么写:JUnit 5、Mockito 与 Spring Boot 方法级实战
java·junit·单元测试
盟道科技23 天前
Redis+Lua 实现滑动窗口限流:从固定窗口踩坑到生产可用的完整方案
redis·junit·lua
呆呆敲代码的小Y1 个月前
Lua 上值(UpValue)
开发语言·junit·lua·lua上值·upvalue
呆呆敲代码的小Y1 个月前
【游戏开发】xLua 性能优化技巧
junit·性能优化·lua·xlua
nvd111 个月前
实战:用 Lua 手写 Kong 安全鉴权插件并通过 ArgoCD GitOps 零编译部署到 DbGate 服务
安全·lua·kong
lhldsg1 个月前
家政派单系统开发实战:从需求分析到智能调度全流程指南
java·junit·小程序·uni-app·需求分析
肠畔码农1 个月前
深度解密 Redis 核心引擎:从 MULTI/EXEC 事务机制到 EVAL 脚本的原子性演进
数据库·redis·junit