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

feat: the traffic-split plugin supports upstream_id #3512

Merged
merged 12 commits into from
Feb 25, 2021
54 changes: 27 additions & 27 deletions apisix/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,33 @@ function _M.http_access_phase()
api_ctx.route_id = route.value.id
api_ctx.route_name = route.value.name

if route.value.script then
tokers marked this conversation as resolved.
Show resolved Hide resolved
script.load(route, api_ctx)
script.run("access", api_ctx)
else
local plugins = plugin.filter(route)
api_ctx.plugins = plugins

plugin.run_plugin("rewrite", plugins, api_ctx)
if api_ctx.consumer then
local changed
route, changed = plugin.merge_consumer_route(
route,
api_ctx.consumer,
api_ctx
)

core.log.info("find consumer ", api_ctx.consumer.username,
", config changed: ", changed)

if changed then
core.table.clear(api_ctx.plugins)
api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
end
end
plugin.run_plugin("access", plugins, api_ctx)
end

local up_id = route.value.upstream_id
if up_id then
local upstreams = core.config.fetch_created_obj("/upstreams")
Expand Down Expand Up @@ -419,33 +446,6 @@ function _M.http_access_phase()
core.log.info("enabled websocket for route: ", route.value.id)
end

if route.value.script then
script.load(route, api_ctx)
script.run("access", api_ctx)
else
local plugins = plugin.filter(route)
api_ctx.plugins = plugins

plugin.run_plugin("rewrite", plugins, api_ctx)
if api_ctx.consumer then
local changed
route, changed = plugin.merge_consumer_route(
route,
api_ctx.consumer,
api_ctx
)

core.log.info("find consumer ", api_ctx.consumer.username,
", config changed: ", changed)

if changed then
core.table.clear(api_ctx.plugins)
api_ctx.plugins = plugin.filter(route, api_ctx.plugins)
end
end
plugin.run_plugin("access", plugins, api_ctx)
end

if route.value.service_protocol == "grpc" then
api_ctx.upstream_scheme = "grpc"
end
Expand Down
31 changes: 19 additions & 12 deletions apisix/plugins/traffic-split.lua
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ local upstreams_schema = {
items = {
type = "object",
properties = {
upstream_id = schema_def.id_schema, -- todo: support upstream_id method
upstream_id = schema_def.id_schema,
tokers marked this conversation as resolved.
Show resolved Hide resolved
upstream = schema_def.upstream,
weight = {
description = "used to split traffic between different" ..
Expand Down Expand Up @@ -258,18 +258,19 @@ end
local function new_rr_obj(weighted_upstreams)
local server_list = {}
for i, upstream_obj in ipairs(weighted_upstreams) do
if not upstream_obj.upstream then
-- If the `upstream` object has only the `weight` value, it means
-- that the `upstream` weight value on the default `route` has been reached.
-- Need to set an identifier to mark the empty upstream.
upstream_obj.upstream = "empty_upstream"
end

if type(upstream_obj.upstream) == "table" then
-- Add a virtual id field to uniquely identify the upstream `key`.
if upstream_obj.upstream_id then
Firstsawyou marked this conversation as resolved.
Show resolved Hide resolved
server_list[upstream_obj.upstream_id] = upstream_obj.weight
elseif upstream_obj.upstream then
-- Add a virtual id field to uniquely identify the upstream key.
upstream_obj.upstream.vid = i
server_list[upstream_obj.upstream] = upstream_obj.weight
else
-- If the upstream object has only the weight value, it means
-- that the upstream weight value on the default route has been reached.
-- Mark empty upstream services in the plugin.
upstream_obj.upstream = "plugin#upstream#is#empty"
server_list[upstream_obj.upstream] = upstream_obj.weight
end
server_list[upstream_obj.upstream] = upstream_obj.weight
end

return roundrobin:new(server_list)
Expand Down Expand Up @@ -315,11 +316,17 @@ function _M.access(conf, ctx)
end

local upstream = rr_up:find()
if upstream and upstream ~= "empty_upstream" then
if upstream and type(upstream) == "table" then
core.log.info("upstream: ", core.json.encode(upstream))
return set_upstream(upstream, ctx)
elseif upstream and upstream ~= "plugin#upstream#is#empty" then
ctx.matched_route.value.upstream_id = upstream
core.log.info("upstream_id: ", upstream)
return
end

core.log.info("route_up: ", upstream)
ctx.matched_route.value.upstream_id = nil
return
end

Expand Down
42 changes: 39 additions & 3 deletions doc/plugins/traffic-split.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Note: The ratio between each upstream may not so accurate since the drawback of
| rules.match | array[object] | optional | | | List of matching rules. |
| rules.match.vars | array[array] | optional | | | A list consisting of one or more {var, operator, val} elements, like this: {{var, operator, val}, {var, operator, val}, ...}}. For example: {"arg_name", "==", "json"}, which means that the current request parameter name is json. The var here is consistent with the naming of Nginx internal variables, so request_uri, host, etc. can also be used; for the operator part, the currently supported operators are ==, ~=, ~~, >, <, in, has and !. For specific usage of operators, please see the `operator-list` part of [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). |
| rules.weighted_upstreams | array[object] | optional | | | List of upstream configuration rules. |
| weighted_upstreams.upstream_id | string/integer| optional | | | The upstream id is bound to the corresponding upstream(not currently supported). |
| weighted_upstreams.upstream_id | string/integer| optional | | | The upstream id is bound to the corresponding upstream. |
| weighted_upstreams.upstream | object | optional | | | Upstream configuration information. |
| upstream.type | enum | optional | roundrobin | [roundrobin, chash] | roundrobin supports weighted load, chash consistent hashing, the two are alternatives. |
| upstream.nodes | object | optional | | | In the hash table, the key of the internal element is the list of upstream machine addresses, in the format of address + Port, where the address part can be an IP or a 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 special meaning, which usually means that the upstream node is invalid and never wants to be selected. |
Expand All @@ -55,7 +55,7 @@ Note: The ratio between each upstream may not so accurate since the drawback of

The traffic-split plugin is mainly composed of two parts: `match` and `weighted_upstreams`. `match` is a custom conditional rule, and `weighted_upstreams` is upstream configuration information. If you configure `match` and `weighted_upstreams` information, then after the `match` rule is verified, it will be based on the `weight` value in `weighted_upstreams`; the ratio of traffic between each upstream in the plugin will be guided, otherwise, all traffic will be directly Reach the `upstream` configured on `route` or `service`. Of course, you can also configure only the `weighted_upstreams` part, which will directly guide the traffic ratio between each upstream in the plugin based on the `weight` value in `weighted_upstreams`.

>Note: 1. In `match`, the expression in vars is the relationship of `and`, and the relationship between multiple `vars` is the relationship of `or`. 2. In the weighted_upstreams field of the plugin, if there is a structure with only `weight`, it means the upstream traffic weight value on `route` or `service`. Such as:
Note: 1. In `match`, the expression in vars is the relationship of `and`, and the relationship between multiple `vars` is the relationship of `or`. 2. In the weighted_upstreams field of the plugin, if there is a structure with only `weight`, it means the upstream traffic weight value on `route` or `service`. Such as:

```json
"weighted_upstreams": [
Expand All @@ -68,7 +68,9 @@ The traffic-split plugin is mainly composed of two parts: `match` and `weighted_

## How To Enable

Create a route and enable the `traffic-split` plugin:
Create a route and enable the `traffic-split` plugin. When configuring the upstream information of the plugin, there are two ways:

1. Configure upstream information through the `upstream` attribute in the plugin.

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
Expand Down Expand Up @@ -111,6 +113,40 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13
}'
```

2. Use the `upstream_id` attribute in the plugin to bind upstream.

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/index.html",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
```

>Note: **1.** Use the `upstream_id` to bind the defined upstream, it can reuse upstream health detection, retry and other functions. **2.** Support the two configuration methods of `upstream` and `upstream_id` to be used together.
tokers marked this conversation as resolved.
Show resolved Hide resolved

## Example

### Grayscale Release
Expand Down
42 changes: 39 additions & 3 deletions doc/zh-cn/plugins/traffic-split.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ traffic-split 插件使用户可以逐步引导各个上游之间的流量百分
| rules.match | array[object] | 可选 | | | 匹配规则列表 |
| rules.match.vars | array[array] | 可选 | | | 由一个或多个{var, operator, val}元素组成的列表,类似这样:{{var, operator, val}, {var, operator, val}, ...}}。例如:{"arg_name", "==", "json"},表示当前请求参数 name 是 json。这里的 var 与 Nginx 内部自身变量命名是保持一致,所以也可以使用 request_uri、host 等;对于 operator 部分,目前已支持的运算符有 ==、~=、~~、>、<、in、has 和 ! 。操作符的具体用法请看 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list) 的 `operator-list` 部分。 |
| rules.weighted_upstreams | array[object] | 可选 | | | 上游配置规则列表。 |
| weighted_upstreams.upstream_id | string / integer | 可选 | | | 通过上游 id 绑定对应上游(暂不支持)。 |
| weighted_upstreams.upstream_id | string / integer | 可选 | | | 通过上游 id 绑定对应上游。 |
| weighted_upstreams.upstream | object | 可选 | | | 上游配置信息。 |
| upstream.type | enum | 可选 | roundrobin | [roundrobin, chash] | roundrobin 支持权重的负载,chash 一致性哈希,两者是二选一的(目前只支持 `roundrobin`)。 |
| upstream.nodes | object | 可选 | | | 哈希表,内部元素的 key 是上游机器地址 列表,格式为地址 + Port,其中地址部 分可以是 IP 也可以是域名,⽐如 192.168.1.100:80、foo.com:80等。 value 则是节点的权重,特别的,当权重 值为 0 有特殊含义,通常代表该上游节点 失效,永远不希望被选中。 |
Expand All @@ -55,7 +55,7 @@ traffic-split 插件使用户可以逐步引导各个上游之间的流量百分

traffic-split 插件主要由 `match` 和 `weighted_upstreams` 两部分组成,`match` 是自定义的条件规则,`weighted_upstreams` 是 upstream 的配置信息。如果配置 `match` 和 `weighted_upstreams` 信息,那么在 `match` 规则校验通过后,会根据 `weighted_upstreams` 中的 `weight` 值;引导插件中各个 upstream 之间的流量比例,否则,所有流量直接到达 `route` 或 `service` 上配置的 `upstream`。当然你也可以只配置 `weighted_upstreams` 部分,这样会直接根据 `weighted_upstreams` 中的 `weight` 值,引导插件中各个 upstream 之间的流量比例。

>注:1、在 `match` 里,vars 中的表达式是 `and` 的关系,多个 `vars` 之间是 `or` 的关系。2、在插件的 weighted_upstreams 域中,如果存在只有 `weight` 的结构,表示 `route` 或 `service` 上的 upstream 流量权重值。例如:
注:1、在 `match` 里,vars 中的表达式是 `and` 的关系,多个 `vars` 之间是 `or` 的关系。2、在插件的 weighted_upstreams 域中,如果存在只有 `weight` 的结构,表示 `route` 或 `service` 上的 upstream 流量权重值。例如:

```json
"weighted_upstreams": [
Expand All @@ -68,7 +68,9 @@ traffic-split 插件主要由 `match` 和 `weighted_upstreams` 两部分组成

## 如何启用

创建一个路由并启用 `traffic-split` 插件:
创建一个路由并启用 `traffic-split` 插件,在配置插件上游信息时,有以下两种方式:

1、通过插件中的 `upstream` 属性配置上游信息。

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
Expand Down Expand Up @@ -111,6 +113,40 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13
}'
```

2、通过插件中的 `upstream_id` 属性绑定上游服务。

```shell
curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"uri": "/index.html",
"plugins": {
"traffic-split": {
"rules": [
{
"weighted_upstreams": [
{
"upstream_id": 1,
"weight": 1
},
{
"weight": 1
}
]
}
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}'
```

>注:1、通过 `upstream_id` 方式来绑定已定义的上游,它可以复用上游具有的健康检测、重试等功能。2、支持 `upstream` 和 `upstream_id` 的两种配置方式一起使用。
tokers marked this conversation as resolved.
Show resolved Hide resolved

## 示例

### 灰度发布
Expand Down
Loading