Skip to content

Commit

Permalink
bugfix: support more built-in parameters when set chash balancer (#775)
Browse files Browse the repository at this point in the history
  • Loading branch information
lilien1010 authored and moonming committed Nov 3, 2019
1 parent df3576d commit b119a5c
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 3 deletions.
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的请求参数,[Nginx 变量列表](http://nginx.org/en/docs/varindex.html)|
|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, [Nginx variables list](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]

0 comments on commit b119a5c

Please sign in to comment.