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

bugfix: support more built-in parameters when set chash balancer #775

Merged
merged 3 commits into from
Nov 3, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion doc/architecture-design-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ APISIX 的 Upstream 除了基本的复杂均衡算法选择外,还支持对上
|------- |-----|------|
|type |必需|`roundrobin` 支持权重的负载,`chash` 一致性哈希,两者是二选一的|
|nodes |必需|哈希表,内部元素的 key 是上游机器地址列表,格式为`地址 + Port`,其中地址部分可以是 IP 也可以是域名,比如 `192.168.1.100:80`、`foo.com:80`等。value 则是节点的权重,特别的,当权重值为 `0` 有特殊含义,通常代表该上游节点失效,永远不希望被选中。|
|key |必需|该选项只有类型是 `chash` 才有效。根据 `key` 来查找对应的 node `id`,相同的 `key` 在同一个对象中,永远返回相同 id|
|key |必需|该选项只有类型是 `chash` 才有效。根据 `key` 来查找对应的 node `id`,相同的 `key` 在同一个对象中,永远返回相同 id,目前支持nginx内置变量有 `uri, server_name, server_addr, request_uri, remote_port, remote_addr, query_string, host, hostname, arg_***`,其中 `arg_***` 是来自URL的请求参数,[内置变量说明](http://nginx.org/en/docs/varindex.html)|
lilien1010 marked this conversation as resolved.
Show resolved Hide resolved
|checks |可选|配置健康检查的参数,详细可参考[health-check](health-check.md)|
|retries |可选|使用底层的 Nginx 重试机制将请求传递给下一个上游,默认不启用重试机制|

Expand Down
2 changes: 1 addition & 1 deletion doc/architecture-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ In addition to the basic complex equalization algorithm selection, APISIX's Upst
|------- |-----|------|
|type |required|`roundrobin` supports the weight of the load, `chash` consistency hash, pick one of them.|
|nodes |required|Hash table, the key of the internal element is the upstream machine address list, the format is `Address + Port`, where the address part can be IP or domain name, such as `192.168.1.100:80`, `foo.com:80`, etc. Value is the weight of the node. In particular, when the weight value is `0`, it has a special meaning, which usually means that the upstream node is invalid and never wants to be selected.|
|key |required|This option is only valid if the type is `chash`. Find the corresponding node `id` according to `key`, the same `key` in the same object, always return the same id.|
|key |required|This option is only valid if the type is `chash`. Find the corresponding node `id` according to `key`, the same `key` in the same object, always return the same id. For now, it support nginx built-in variables like `uri, server_name, server_addr, request_uri, remote_port, remote_addr, query_string, host, hostname, arg_***`, `arg_***` is arguments in the request line, [Index of Nginx variables](http://nginx.org/en/docs/varindex.html)|
|checks |optional|Configure the parameters of the health check. For details, refer to [health-check](health-check.md).|
|retries |optional|Pass the request to the next upstream using the underlying Nginx retry mechanism, the retry mechanism is not enabled by default.|

Expand Down
3 changes: 2 additions & 1 deletion lua/apisix/schema_def.lua
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ local upstream_schema = {
key = {
description = "the key of chash for dynamic load balancing",
type = "string",
enum = {"remote_addr"},
pattern = [[^((uri|server_name|server_addr|request_uri|remote_port]]
.. [[|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$]],
},
desc = {type = "string", maxLength = 256},
id = id_schema
Expand Down
182 changes: 182 additions & 0 deletions t/admin/upstream.t
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,185 @@ GET /t
passed
--- no_error_log
[error]



=== TEST 24: set upstream(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "server_name",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]]
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 25: wrong upstream key
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 2
},
"type": "chash",
"key": "not_support",
"desc": "new upstream"
}]]
)

ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"key\" validation failed: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""}
--- no_error_log
[error]



=== TEST 24: set upstream with args(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "arg_device_id",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"desc": "new chash upstream"
}]]
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 24: set upstream(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "server_name",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash"
}]]
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 25: wrong upstream key
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"nodes": {
"127.0.0.1:8080": 1,
"127.0.0.1:8081": 2
},
"type": "chash",
"key": "not_support",
"desc": "new upstream"
}]]
)

ngx.status = code
ngx.print(body)
}
}
--- request
GET /t
--- error_code: 400
--- response_body
{"error_msg":"invalid configuration: property \"key\" validation failed: failed to match pattern \"^((uri|server_name|server_addr|request_uri|remote_port|remote_addr|query_string|host|hostname)|arg_[0-9a-zA-z_-]+)$\" with \"not_support\""}
--- no_error_log
[error]



=== TEST 24: set upstream with args(type: chash)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/upstreams/1',
ngx.HTTP_PUT,
[[{
"key": "arg_device_id",
"nodes": {
"127.0.0.1:8080": 1
},
"type": "chash",
"desc": "new chash upstream"
}]]
)

ngx.status = code
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]
156 changes: 156 additions & 0 deletions t/node/chash-balance.t
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,159 @@ GET /t
[{"count":12,"port":"1980"}]
--- no_error_log
[error]



=== TEST 7 set route(three upstream node with querystring)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/server_port",
"upstream": {
"key": "query_string",
"type": "chash",
"nodes": {
"127.0.0.1:1980": 1,
"127.0.0.1:1981": 1,
"127.0.0.1:1982": 1
}
}
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 8: hit routes with querystring
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local uri = "http://127.0.0.1:" .. ngx.var.server_port
.. "/server_port?var=2&var2="

local ports_count = {}
for i = 1, 18 do
local httpc = http.new()
local res, err = httpc:request_uri(uri..i, {method = "GET"})
if not res then
ngx.say(err)
return
end
ports_count[res.body] = (ports_count[res.body] or 0) + 1
end

local ports_arr = {}
for port, count in pairs(ports_count) do
table.insert(ports_arr, {port = port, count = count})
end

local function cmd(a, b)
return a.port > b.port
end
table.sort(ports_arr, cmd)

ngx.say(require("cjson").encode(ports_arr))
ngx.exit(200)
}
}
--- request
GET /t
--- response_body
[{"count":5,"port":"1982"},{"count":8,"port":"1981"},{"count":5,"port":"1980"}]
--- no_error_log
[error]



=== TEST 9: set route(three upstream node with arg_xxx)
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"uri": "/server_port",
"upstream": {
"key": "arg_device_id",
"type": "chash",
"nodes": {
"127.0.0.1:1980": 1,
"127.0.0.1:1981": 1,
"127.0.0.1:1982": 1
}
}
}]]
)

if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
--- no_error_log
[error]



=== TEST 10: hit routes with args
--- config
location /t {
content_by_lua_block {
local http = require "resty.http"
local uri = "http://127.0.0.1:" .. ngx.var.server_port
.. "/server_port?device_id="

local ports_count = {}
for i = 1, 18 do
local httpc = http.new()
local res, err = httpc:request_uri(uri..i, {method = "GET"})
if not res then
ngx.say(err)
return
end
ports_count[res.body] = (ports_count[res.body] or 0) + 1
end

local ports_arr = {}
for port, count in pairs(ports_count) do
table.insert(ports_arr, {port = port, count = count})
end

local function cmd(a, b)
return a.port > b.port
end
table.sort(ports_arr, cmd)

ngx.say(require("cjson").encode(ports_arr))
ngx.exit(200)
}
}
--- request
GET /t
--- response_body
[{"count":5,"port":"1982"},{"count":7,"port":"1981"},{"count":6,"port":"1980"}]
--- no_error_log
[error]