限流的场景:服务提供的能力是有限的。为了防止大量的请求将服务拖垮,能够经过网关对服务的请求作限流。例如:某个服务1s只能处理100个请求,超过限流阈值的请求丢弃。nginx
Kong网关提供的限流插件:rate-limiting
redis
从插件参数中能够得到这些信息:算法
consumer
、credential
、ip
,缺省是consumerlocal
、cluster
、redis
,缺省是cluster。存储的内容是:以 fmt("ratelimit:%s:%s:%s:%s", api_id, identifier, period_date, name)
为key,持续访问的次数为value。rate-limiting
表中 - 支持集群访问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 },
},
复制代码
当限制类型(限制粒度)为consumer
和identifier
的时候,限流插件须要一些会在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
复制代码
一般的限流的作法是一段时间内的请求次数。但若是只经过一段时间来限时会遇到流量分布不均的问题。例如:某个服务限制一小时的请求次数是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
复制代码
参考:工具