Kong 限流插件

限流的場景:服務提供的能力是有限的。爲了防止大量的請求將服務拖垮,能夠經過網關對服務的請求作限流。例如:某個服務1s只能處理100個請求,超過限流閾值的請求丟棄。nginx

1、rate-limiting

Kong網關提供的限流插件:rate-limitingredis

1.1 配置的參數

從插件參數中能夠得到這些信息:算法

  1. 限流支持的粒度有三種:consumercredentialip,缺省是consumer
  2. 存儲策略支持三種:localclusterredis,缺省是cluster。存儲的內容是:以 fmt("ratelimit:%s:%s:%s:%s", api_id, identifier, period_date, name) 爲key,持續訪問的次數爲value。
  • local:存儲在Nginx共享內容 - 支持多work進程間訪問
  • cluster:數據庫中rate-limiting表中 - 支持集羣訪問
  • redis:插件配置的Redis中 - 支持集羣訪問
  1. 支持各類時間單位的限流
fields = {
    second = { type = "number" },
    minute = { type = "number" },
    hour = { type = "number" },
    day = { type = "number" },
    month = { type = "number" },
    year = { type = "number" },
    limit_by = { type = "string", enum = {"consumer", "credential", "ip"}, default = "consumer" },
    policy = { type = "string", enum = {"local", "cluster", REDIS}, default = "cluster" },
    fault_tolerant = { type = "boolean", default = true },
    redis_host = { type = "string" },
    redis_port = { type = "number", default = 6379 },
    redis_password = { type = "string" },
    redis_timeout = { type = "number", default = 2000 },
    redis_database = { type = "number", default = 0 },
    hide_client_headers = { type = "boolean", default = false },
  },
複製代碼

1.2 限流粒度

當限制類型(限制粒度)爲consumeridentifier的時候,限流插件須要一些會在nginx.ctx中注入認證信息的插件,好比:jwt、key-auth、oauth2數據庫

if conf.limit_by == "consumer" then
    identifier = ngx.ctx.authenticated_consumer and ngx.ctx.authenticated_consumer.id
    if not identifier and ngx.ctx.authenticated_credential then -- Fallback on credential
      identifier = ngx.ctx.authenticated_credential.id
    end
  elseif conf.limit_by == "credential" then
    identifier = ngx.ctx.authenticated_credential and ngx.ctx.authenticated_credential.id
  end
  # 若是請求中不帶認證,粒度會退化到IP限流
  if not identifier then
    identifier = ngx.var.remote_addr
  end
  
複製代碼

1.3 限流算法

一般的限流的作法是一段時間內的請求次數。但若是隻經過一段時間來限時會遇到流量分佈不均的問題。例如:某個服務限制一小時的請求次數是3600次,那麼若是在某一刻一會兒來了一堆請求超過的閾值。爲了解決這個問題,kong將一段時間拆細,將閾值設成每分鐘60每秒鐘1次。api

local limits = {
    second = conf.second,
    minute = conf.minute,
    hour = conf.hour,
    day = conf.day,
    month = conf.month,
    year = conf.year
  }
複製代碼

計算remaining。每次請求進來,插件從存儲中獲取到當前服務remaining(剩餘)的訪問次數,返回的usage的格式(其中name是上面提到的limits定義的Metric):併發

-- Recording usage
    usage[name] = {
      limit = limit,
      remaining = remaining
    }
    -- 標記那個Metric中止了
    if remaining <= 0 then
      stop = name
    end
複製代碼

而後減一ide

if usage then
    -- Adding headers
    if not conf.hide_client_headers then
      for k, v in pairs(usage) do
        ngx.header[RATELIMIT_LIMIT .. "-" .. k] = v.limit
        -- -increment_value for this current request
        ngx.header[RATELIMIT_REMAINING .. "-" .. k] = math.max(0, (stop == nil or stop == k) and v.remaining - 1 or v.remaining) 
      end
    end
複製代碼

2、談限流

參考:工具

  1. 談談服務限流算法的幾種實現
  2. Java併發工具類(信號量Semaphore)
  3. Redis 深度歷險:核心原理與應用實踐
相關文章
相關標籤/搜索