This is an automated email from the ASF dual-hosted git repository. wenming pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-apisix.git
The following commit(s) were added to refs/heads/master by this push: new b119a5c bugfix: support more built-in parameters when set chash balancer (#775) b119a5c is described below commit b119a5c94b6ea59eb263f5f353944d3ebf00e81c Author: Lien <lilien1...@users.noreply.github.com> AuthorDate: Sun Nov 3 18:25:51 2019 +0800 bugfix: support more built-in parameters when set chash balancer (#775) --- doc/architecture-design-cn.md | 2 +- doc/architecture-design.md | 2 +- lua/apisix/schema_def.lua | 3 +- t/admin/upstream.t | 182 ++++++++++++++++++++++++++++++++++++++++++ t/node/chash-balance.t | 156 ++++++++++++++++++++++++++++++++++++ 5 files changed, 342 insertions(+), 3 deletions(-) diff --git a/doc/architecture-design-cn.md b/doc/architecture-design-cn.md index fcf4670..552bdd0 100644 --- a/doc/architecture-design-cn.md +++ b/doc/architecture-design-cn.md @@ -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 重试机制将请求传递给下一个上游,默认不启用重试机制| diff --git a/doc/architecture-design.md b/doc/architecture-design.md index a4aaf6c..c225322 100644 --- a/doc/architecture-design.md +++ b/doc/architecture-design.md @@ -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.| diff --git a/lua/apisix/schema_def.lua b/lua/apisix/schema_def.lua index 6784ced..fc70770 100644 --- a/lua/apisix/schema_def.lua +++ b/lua/apisix/schema_def.lua @@ -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 diff --git a/t/admin/upstream.t b/t/admin/upstream.t index b5bc94b..1453275 100644 --- a/t/admin/upstream.t +++ b/t/admin/upstream.t @@ -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] diff --git a/t/node/chash-balance.t b/t/node/chash-balance.t index f012bbb..247fc37 100644 --- a/t/node/chash-balance.t +++ b/t/node/chash-balance.t @@ -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]