From f843439b745e4784814921bf3bbe51c64cf3487e Mon Sep 17 00:00:00 2001 From: Firstsawyou <2226815922@qq.com> Date: Wed, 21 Apr 2021 00:00:32 +0800 Subject: [PATCH 1/4] fix(traffic-split): when there are multiple weighted_upstreams, the request is confused --- apisix/plugins/traffic-split.lua | 3 +- t/plugin/traffic-split2.t | 210 +++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 2 deletions(-) diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua index 5364438e7ab3..f7e75af88655 100644 --- a/apisix/plugins/traffic-split.lua +++ b/apisix/plugins/traffic-split.lua @@ -278,8 +278,7 @@ function _M.access(conf, ctx) return end - local rr_up, err = core.lrucache.plugin_ctx(lrucache, ctx, nil, new_rr_obj, - weighted_upstreams) + local rr_up, err = lrucache(weighted_upstreams, nil, new_rr_obj, weighted_upstreams) if not rr_up then core.log.error("lrucache roundrobin failed: ", err) return 500 diff --git a/t/plugin/traffic-split2.t b/t/plugin/traffic-split2.t index 58df05f7b1c9..3a1b864a797d 100644 --- a/t/plugin/traffic-split2.t +++ b/t/plugin/traffic-split2.t @@ -408,3 +408,213 @@ hash_on: header chash_key: "world" hash_on: header chash_key: "hello" + + +=== TEST 12: the plugin has multiple weighted_upstreams +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PATCH, + [=[{ + "uri": "/server_port", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [ + ["arg_id","==","1"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream_A", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1981":1 + } + }, + "weight": 1 + } + ] + }, + { + "match": [ + { + "vars": [ + ["arg_id","==","2"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream_B", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1982":1 + } + }, + "weight": 1 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]=] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 13: hit each upstream separately +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local bodys = {} + for i = 1, 9, 3 do + local _, _, body = t('/server_port', ngx.HTTP_GET) + local _, _, body2 = t('/server_port?id=1', ngx.HTTP_GET) + local _, _, body3 = t('/server_port?id=2', ngx.HTTP_GET) + bodys[i] = body + bodys[i+1] = body2 + bodys[i+2] = body3 + end + + ngx.say(table.concat(bodys, ", ")) + } +} +--- response_body eval +qr/1980, 1981, 1982, 1980, 1981, 1982, 1980, 1981, 1982/ +--- no_error_log +[error] + + + +=== TEST 14: the plugin has multiple weighted_upstreams and has a default routing weight in weighted_upstreams +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PATCH, + [=[{ + "uri": "/server_port", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [ + ["arg_id","==","1"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream_A", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1981":1 + } + }, + "weight": 1 + }, + { + "weight": 1 + } + ] + }, + { + "match": [ + { + "vars": [ + ["arg_id","==","2"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream_B", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1982":1 + } + }, + "weight": 1 + }, + { + "weight": 1 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]=] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 11: every weighted_upstreams in the plugin is hit +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local bodys = {} + for i = 1, 8, 2 do + local _, _, body = t('/server_port?id=1', ngx.HTTP_GET) + local _, _, body2 = t('/server_port?id=2', ngx.HTTP_GET) + bodys[i] = body + bodys[i+1] = body2 + end + + table.sort(bodys) + ngx.say(table.concat(bodys, ", ")) + } +} +--- response_body eval +qr/1980, 1980, 1980, 1980, 1981, 1981, 1982, 1982/ +--- no_error_log +[error] From 930eeeacd1f3538fbfcdeb0715b3fc99e92b3916 Mon Sep 17 00:00:00 2001 From: Firstsawyou <2226815922@qq.com> Date: Wed, 21 Apr 2021 10:35:23 +0800 Subject: [PATCH 2/4] add more test cases. --- t/plugin/traffic-split2.t | 155 ++++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 14 deletions(-) diff --git a/t/plugin/traffic-split2.t b/t/plugin/traffic-split2.t index 3a1b864a797d..78b8e433b079 100644 --- a/t/plugin/traffic-split2.t +++ b/t/plugin/traffic-split2.t @@ -410,7 +410,8 @@ hash_on: header chash_key: "hello" -=== TEST 12: the plugin has multiple weighted_upstreams + +=== TEST 12: the plugin has multiple weighted_upstreams(upstream method) --- config location /t { content_by_lua_block { @@ -425,9 +426,7 @@ chash_key: "hello" { "match": [ { - "vars": [ - ["arg_id","==","1"] - ] + "vars": [["arg_id","==","1"]] } ], "weighted_upstreams": [ @@ -446,9 +445,7 @@ chash_key: "hello" { "match": [ { - "vars": [ - ["arg_id","==","2"] - ] + "vars": [["arg_id","==","2"]] } ], "weighted_upstreams": [ @@ -528,9 +525,7 @@ qr/1980, 1981, 1982, 1980, 1981, 1982, 1980, 1981, 1982/ { "match": [ { - "vars": [ - ["arg_id","==","1"] - ] + "vars": [["arg_id","==","1"]] } ], "weighted_upstreams": [ @@ -552,9 +547,7 @@ qr/1980, 1981, 1982, 1980, 1981, 1982, 1980, 1981, 1982/ { "match": [ { - "vars": [ - ["arg_id","==","2"] - ] + "vars": [["arg_id","==","2"]] } ], "weighted_upstreams": [ @@ -597,7 +590,7 @@ passed -=== TEST 11: every weighted_upstreams in the plugin is hit +=== TEST 15: every weighted_upstreams in the plugin is hit --- config location /t { content_by_lua_block { @@ -618,3 +611,137 @@ location /t { qr/1980, 1980, 1980, 1980, 1981, 1981, 1982, 1982/ --- no_error_log [error] + + + +=== TEST 16: set upstream(upstream_id: 1, upstream_id: 2) +--- 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:1981": 1 + }, + "type": "roundrobin", + "desc": "new upstream A" + }]] + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + end + + code, body = t('/apisix/admin/upstreams/2', + ngx.HTTP_PUT, + [[{ + "nodes": { + "127.0.0.1:1982": 1 + }, + "type": "roundrobin", + "desc": "new upstream B" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 17: the plugin has multiple weighted_upstreams(upstream_id method) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PATCH, + [=[{ + "uri": "/server_port", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [["arg_id","==","1"]] + } + ], + "weighted_upstreams": [ + { + "upstream_id": 1, + "weight": 1 + } + ] + }, + { + "match": [ + { + "vars": [["arg_id","==","2"]] + } + ], + "weighted_upstreams": [ + { + "upstream_id": 2, + "weight": 1 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]=] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed +--- no_error_log +[error] + + + +=== TEST 18: hit each upstream separately +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local bodys = {} + for i = 1, 9, 3 do + local _, _, body = t('/server_port', ngx.HTTP_GET) + local _, _, body2 = t('/server_port?id=1', ngx.HTTP_GET) + local _, _, body3 = t('/server_port?id=2', ngx.HTTP_GET) + bodys[i] = body + bodys[i+1] = body2 + bodys[i+2] = body3 + end + + ngx.say(table.concat(bodys, ", ")) + } +} +--- response_body eval +qr/1980, 1981, 1982, 1980, 1981, 1982, 1980, 1981, 1982/ +--- no_error_log +[error] From 9c202ca71802551a44ee2fbc3b0423ffe0a2cf09 Mon Sep 17 00:00:00 2001 From: Firstsawyou <2226815922@qq.com> Date: Wed, 21 Apr 2021 12:08:52 +0800 Subject: [PATCH 3/4] add docs --- docs/en/latest/plugins/traffic-split.md | 93 +++++++++++++++++++++++++ docs/zh/latest/plugins/traffic-split.md | 93 +++++++++++++++++++++++++ 2 files changed, 186 insertions(+) diff --git a/docs/en/latest/plugins/traffic-split.md b/docs/en/latest/plugins/traffic-split.md index 9189a81b7fbb..d6c3e6c99480 100644 --- a/docs/en/latest/plugins/traffic-split.md +++ b/docs/en/latest/plugins/traffic-split.md @@ -30,6 +30,7 @@ title: traffic-split - [Grayscale Release](#grayscale-release) - [Blue-green Release](#blue-green-release) - [Custom Release](#custom-release) + - [Matching rules correspond to upstream](#matching-rules-correspond-to-upstream) - [Disable Plugin](#disable-plugin) ## Name @@ -482,6 +483,98 @@ Content-Type: text/html; charset=utf-8 hello 1980 ``` +### Matching rules correspond to upstream + +How to make different matching rules correspond to different upstreams. We can achieve this by configuring multiple `rules.match` + `rules.weighted_upstreams`. + +**Example:** + +When the request header `x-api-id` is equal to 1, it hits the upstream of port 1981; when `x-api-id` is equal to 2, it hits the upstream of port 1982; otherwise, it hits the upstream of port 1980 (the upstream response data is the corresponding port number). + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/hello", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [ + ["http_x-api-id","==","1"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream-A", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1981":1 + } + }, + "weight": 3 + } + ] + }, + { + "match": [ + { + "vars": [ + ["http_x-api-id","==","2"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream-B", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1982":1 + } + }, + "weight": 3 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } +}' +``` + +**Test plugin:** + +The request header `x-api-id` is equal to 1, which hits the upstream of the 1981 port. + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1' +1981 +``` + +The request header `x-api-id` is equal to 2, which hits the upstream of the 1982 port. + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2' +1982 +``` + +The request header `x-api-id` is equal to 3, the rule does not match, and it hits the upstream of the 1980 port. + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' +1980 +``` + ## Disable Plugin When you want to remove the traffic-split plugin, it's very simple, just delete the corresponding json configuration in the plugin configuration, no need to restart the service, it will take effect immediately: diff --git a/docs/zh/latest/plugins/traffic-split.md b/docs/zh/latest/plugins/traffic-split.md index 045ab54eb0d7..c59e08b44ea5 100644 --- a/docs/zh/latest/plugins/traffic-split.md +++ b/docs/zh/latest/plugins/traffic-split.md @@ -30,6 +30,7 @@ title: traffic-split - [灰度发布](#灰度发布) - [蓝绿发布](#蓝绿发布) - [自定义发布](#自定义发布) + - [匹配规则与上游对应](#匹配规则与上游对应) - [禁用插件](#禁用插件) ## 名字 @@ -493,6 +494,98 @@ Content-Type: text/html; charset=utf-8 hello 1980 ``` +### 匹配规则与上游对应 + +如何让不同的匹配规则对应到不同的上游,我们可以通过配置多个 `rules.match` + `rules.weighted_upstreams` 来实现。 + +**示例:** + +当请求头 `x-api-id` 等于 1 时,命中 1981 端口的上游;当 `x-api-id` 等于 2 时,命中 1982 端口的上游;否则,命中 1980 端口的上游(上游响应数据为对应的端口号)。 + +```shell +curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/hello", + "plugins": { + "traffic-split": { + "rules": [ + { + "match": [ + { + "vars": [ + ["http_x-api-id","==","1"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream-A", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1981":1 + } + }, + "weight": 3 + } + ] + }, + { + "match": [ + { + "vars": [ + ["http_x-api-id","==","2"] + ] + } + ], + "weighted_upstreams": [ + { + "upstream": { + "name": "upstream-B", + "type": "roundrobin", + "nodes": { + "127.0.0.1:1982":1 + } + }, + "weight": 3 + } + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } +}' +``` + +**测试插件:** + +请求头 `x-api-id` 等于 1,命中 1981 端口的上游。 + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1' +1981 +``` + +请求头 `x-api-id` 等于 2,命中 1982 端口的上游。 + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2' +1982 +``` + +请求头 `x-api-id` 等于 3,规则不匹配,命中 1980 端口的上游。 + +```shell +$ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' +1980 +``` + ## 禁用插件 当你想去掉 traffic-split 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效: From fd889d1d7f398d83d9def9b611722fe581d6691f Mon Sep 17 00:00:00 2001 From: Firstsawyou <2226815922@qq.com> Date: Wed, 21 Apr 2021 17:58:08 +0800 Subject: [PATCH 4/4] update docs and merge test case --- docs/en/latest/plugins/traffic-split.md | 10 +++++----- docs/zh/latest/plugins/traffic-split.md | 8 ++++---- t/plugin/traffic-split2.t | 25 ++++++------------------- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/docs/en/latest/plugins/traffic-split.md b/docs/en/latest/plugins/traffic-split.md index d6c3e6c99480..4882da1e123f 100644 --- a/docs/en/latest/plugins/traffic-split.md +++ b/docs/en/latest/plugins/traffic-split.md @@ -485,11 +485,11 @@ hello 1980 ### Matching rules correspond to upstream -How to make different matching rules correspond to different upstreams. We can achieve this by configuring multiple `rules.match` + `rules.weighted_upstreams`. +By configuring multiple `rules`, we can achieve one-to-one correspondence between different matching rules and upstream. **Example:** -When the request header `x-api-id` is equal to 1, it hits the upstream of port 1981; when `x-api-id` is equal to 2, it hits the upstream of port 1982; otherwise, it hits the upstream of port 1980 (the upstream response data is the corresponding port number). +When the request header `x-api-id` is equal to 1, it hits the upstream with port 1981; when `x-api-id` is equal to 2, it hits the upstream with port 1982; otherwise, it hits the upstream with port 1980 (the upstream response data is the corresponding port number). ```shell curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' @@ -554,21 +554,21 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 **Test plugin:** -The request header `x-api-id` is equal to 1, which hits the upstream of the 1981 port. +The request header `x-api-id` is equal to 1, hitting the upstream with the 1981 port. ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1' 1981 ``` -The request header `x-api-id` is equal to 2, which hits the upstream of the 1982 port. +The request header `x-api-id` is equal to 2, hitting the upstream with the 1982 port. ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2' 1982 ``` -The request header `x-api-id` is equal to 3, the rule does not match, and it hits the upstream of the 1980 port. +The request header `x-api-id` is equal to 3, the rule does not match, and it hits the upstream with port 1980. ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' diff --git a/docs/zh/latest/plugins/traffic-split.md b/docs/zh/latest/plugins/traffic-split.md index c59e08b44ea5..86e576d06f84 100644 --- a/docs/zh/latest/plugins/traffic-split.md +++ b/docs/zh/latest/plugins/traffic-split.md @@ -496,7 +496,7 @@ hello 1980 ### 匹配规则与上游对应 -如何让不同的匹配规则对应到不同的上游,我们可以通过配置多个 `rules.match` + `rules.weighted_upstreams` 来实现。 +通过配置多个 `rules`,我们可以实现不同的匹配规则与上游一一对应。 **示例:** @@ -565,21 +565,21 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 **测试插件:** -请求头 `x-api-id` 等于 1,命中 1981 端口的上游。 +请求头 `x-api-id` 等于 1,命中带 1981 端口的上游。 ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1' 1981 ``` -请求头 `x-api-id` 等于 2,命中 1982 端口的上游。 +请求头 `x-api-id` 等于 2,命中带 1982 端口的上游。 ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2' 1982 ``` -请求头 `x-api-id` 等于 3,规则不匹配,命中 1980 端口的上游。 +请求头 `x-api-id` 等于 3,规则不匹配,命中带 1980 端口的上游。 ```shell $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' diff --git a/t/plugin/traffic-split2.t b/t/plugin/traffic-split2.t index 78b8e433b079..9fe275299420 100644 --- a/t/plugin/traffic-split2.t +++ b/t/plugin/traffic-split2.t @@ -614,7 +614,7 @@ qr/1980, 1980, 1980, 1980, 1981, 1981, 1982, 1982/ -=== TEST 16: set upstream(upstream_id: 1, upstream_id: 2) +=== TEST 16: set upstream(upstream_id: 1, upstream_id: 2) and add route --- config location /t { content_by_lua_block { @@ -648,25 +648,10 @@ qr/1980, 1980, 1980, 1980, 1981, 1981, 1982, 1982/ if code >= 300 then ngx.status = code + ngx.say(body) end - ngx.say(body) - } - } ---- request -GET /t ---- response_body -passed ---- no_error_log -[error] - - -=== TEST 17: the plugin has multiple weighted_upstreams(upstream_id method) ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', + code, body = t('/apisix/admin/routes/1', ngx.HTTP_PATCH, [=[{ "uri": "/server_port", @@ -716,6 +701,8 @@ passed ngx.say(body) } } +--- request +GET /t --- response_body passed --- no_error_log @@ -723,7 +710,7 @@ passed -=== TEST 18: hit each upstream separately +=== TEST 17: hit each upstream separately --- config location /t { content_by_lua_block {