Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve rate limit policy #704

Merged
merged 4 commits into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed

- `THREESCALE_PORTAL_ENDPOINT` and `THREESCALE_CONFIG_FILE` are not required anymore [PR #702](https://github.com/3scale/apicast/pull/702)
- The `scope` of the Rate Limit policy is `service` by default [PR #704](https://github.com/3scale/apicast/pull/704)

## [3.2.0-rc2] - 2018-05-11

Expand Down
142 changes: 43 additions & 99 deletions gateway/src/apicast/policy/rate_limit/apicast-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,52 @@
"version": "builtin",
"configuration": {
"type": "object",
"definitions": {
"key": {
"$id": "#/definitions/key",
"description": "The key corresponding to the limiter object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the key, must be unique in the scope"
},
"scope": {
"type": "string",
"description": "Scope of the key",
"default": "service",
"oneOf": [{
"enum": ["global"],
"description": "Global scope, affecting to all services"
}, {
"enum": ["service"],
"description": "Service scope, affecting to one service"
}]
}
}
},
"error_handling": {
"$id": "#/definitions/error_handling",
"type": "string",
"description": "How to handle an error",
"default": "exit",
"oneOf": [{
"enum": ["exit"],
"description": "Respond with an error"
}, {
"enum": ["log"],
"description": "Let the request go through and only output logs"
}]
}
},
"properties": {
"connection_limiters": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"description": "The key corresponding to the limiter object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the key, must be unique in the scope"
},
"scope": {
"type": "string",
"description": "Scope of the key",
"default": "global",
"oneOf": [{
"enum": ["global"],
"description": "Global scope, affecting to all services"
}, {
"enum": ["service"],
"description": "Service scope, affecting to one service"
}]
},
"service_name": {
"type": "string",
"description": "Name of service, necessary for service scope"
}
}
"$ref": "#/definitions/key"
},
"conn": {
"type": "integer",
Expand All @@ -62,30 +77,7 @@
"type": "object",
"properties": {
"key": {
"description": "The key corresponding to the limiter object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the key, must be unique in the scope"
},
"scope": {
"type": "string",
"description": "Scope of the key",
"default": "global",
"oneOf": [{
"enum": ["global"],
"description": "Global scope, affecting to all services"
}, {
"enum": ["service"],
"description": "Service scope, affecting to one service"
}]
},
"service_name": {
"type": "string",
"description": "Name of service, necessary for service scope"
}
}
"$ref": "#/definitions/key"
},
"rate": {
"type": "integer",
Expand All @@ -106,30 +98,7 @@
"type": "object",
"properties": {
"key": {
"description": "The key corresponding to the limiter object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the key, must be unique in the scope"
},
"scope": {
"type": "string",
"description": "Scope of the key",
"default": "global",
"oneOf": [{
"enum": ["global"],
"description": "Global scope, affecting to all services"
}, {
"enum": ["service"],
"description": "Service scope, affecting to one service"
}]
},
"service_name": {
"type": "string",
"description": "Name of service, necessary for service scope"
}
}
"$ref": "#/definitions/key"
},
"count": {
"type": "integer",
Expand Down Expand Up @@ -157,16 +126,7 @@
"default": 429
},
"error_handling": {
"type": "string",
"description": "How to handle an error",
"default": "exit",
"oneOf": [{
"enum": ["exit"],
"description": "Respond with an error"
}, {
"enum": ["log"],
"description": "Let the request go through and only output logs"
}]
"$ref": "#/definitions/error_handling"
}
}
},
Expand All @@ -179,23 +139,7 @@
"default": 500
},
"error_handling": {
"type": "string",
"description": "How to handle an error",
"default": "exit",
"oneOf": [
{
"enum": [
"exit"
],
"description": "Respond with an error"
},
{
"enum": [
"log"
],
"description": "Let the request go through and only output logs"
}
]
"$ref": "#/definitions/error_handling"
}
}
}
Expand Down
24 changes: 13 additions & 11 deletions gateway/src/apicast/policy/rate_limit/rate_limit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ local shdict_key = 'limiter'
local insert = table.insert
local ipairs = ipairs
local unpack = table.unpack
local format = string.format
local concat = table.concat

local new = _M.new

Expand Down Expand Up @@ -108,7 +110,7 @@ local function init_error_settings(limits_exceeded_error, configuration_error)
return error_settings
end

local function build_limiters_and_keys(type, limiters, redis, error_settings)
local function build_limiters_and_keys(type, limiters, redis, error_settings, service_id)
local res_limiters = {}
local res_keys = {}

Expand All @@ -125,10 +127,10 @@ local function build_limiters_and_keys(type, limiters, redis, error_settings)
insert(res_limiters, lim)

local key
if limiter.key.scope == "service" then
key = limiter.key.service_name.."_"..type.."_"..limiter.key.name
if limiter.key.scope == "global" then
key = format("%s_%s", type, limiter.key.name)
else
key = type.."_"..limiter.key.name
key = format("%s_%s_%s", service_id, type, limiter.key.name)
end

insert(res_keys, key)
Expand All @@ -150,7 +152,7 @@ function _M.new(config)
return self
end

function _M:access()
function _M:access(context)
local red
if self.redis_url then
local rederr
Expand All @@ -163,13 +165,13 @@ function _M:access()
end

local conn_limiters, conn_keys = build_limiters_and_keys(
'connections', self.connection_limiters, red, self.error_settings)
'connections', self.connection_limiters, red, self.error_settings, context.service.id)

local leaky_bucket_limiters, leaky_bucket_keys = build_limiters_and_keys(
'leaky_bucket', self.leaky_bucket_limiters, red, self.error_settings)
'leaky_bucket', self.leaky_bucket_limiters, red, self.error_settings, context.service.id)

local fixed_window_limiters, fixed_window_keys = build_limiters_and_keys(
'fixed_window', self.fixed_window_limiters, red, self.error_settings)
'fixed_window', self.fixed_window_limiters, red, self.error_settings, context.service.id)

local limiters = {}
local limiter_groups = { conn_limiters, leaky_bucket_limiters, fixed_window_limiters }
Expand Down Expand Up @@ -205,8 +207,8 @@ function _M:access()

for i, lim in ipairs(limiters) do
if lim.is_committed and lim:is_committed() then
table.insert(connections_committed, lim)
table.insert(keys_committed, keys[i])
insert(connections_committed, lim)
insert(keys_committed, keys[i])
end
end

Expand All @@ -217,7 +219,7 @@ function _M:access()
end

if delay > 0 then
ngx.log(ngx.WARN, 'need to delay by: ', delay, 's, states: ', table.concat(states, ", "))
ngx.log(ngx.WARN, 'need to delay by: ', delay, 's, states: ', concat(states, ", "))
ngx.sleep(delay)
end

Expand Down
Loading