traffic-split

    注:由于加权循环算法(特别是在重置 wrr 状态时)的缺点,因此每个上游之间的比率可能不太准确。

    目前在 weighted_upstreams.upstream 的配置中,不支持的字段有: service_name、discovery_type、checks、retries、retry_timeout、desc、scheme、labels、create_time 和 update_time。但是你可以通过 weighted_upstreams.upstream_id 绑定 upstream 对象来实现他们。

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

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

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

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

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "weighted_upstreams": [
    9. {
    10. "upstream": {
    11. "name": "upstream_A",
    12. "type": "roundrobin",
    13. "nodes": {
    14. "127.0.0.1:1981":10
    15. },
    16. "timeout": {
    17. "connect": 15,
    18. "send": 15,
    19. "read": 15
    20. }
    21. },
    22. "weight": 1
    23. },
    24. {
    25. "weight": 1
    26. }
    27. ]
    28. }
    29. ]
    30. }
    31. },
    32. "upstream": {
    33. "type": "roundrobin",
    34. "nodes": {
    35. "127.0.0.1:1980": 1
    36. }
    37. }
    38. }'

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

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "weighted_upstreams": [
    9. {
    10. "upstream_id": 1,
    11. "weight": 1
    12. },
    13. {
    14. "weight": 1
    15. }
    16. ]
    17. }
    18. ]
    19. }
    20. },
    21. "upstream": {
    22. "type": "roundrobin",
    23. "nodes": {
    24. "127.0.0.1:1980": 1
    25. }
    26. }
    27. }'

    缺少 match 规则部分,根据插件中 weighted_upstreams 配置的 weight 值做流量分流。将 插件的 upstreamroute 的 upstream 按 3:2 的流量比例进行划分,其中 60% 的流量到达插件中的 1981 端口的 upstream, 40% 的流量到达 route 上默认 1980 端口的 upstream。

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "weighted_upstreams": [
    9. {
    10. "upstream": {
    11. "name": "upstream_A",
    12. "type": "roundrobin",
    13. "nodes": {
    14. "127.0.0.1:1981":10
    15. },
    16. "timeout": {
    17. "connect": 15,
    18. "send": 15,
    19. "read": 15
    20. }
    21. },
    22. "weight": 3
    23. },
    24. {
    25. "weight": 2
    26. }
    27. ]
    28. }
    29. ]
    30. }
    31. },
    32. "upstream": {
    33. "type": "roundrobin",
    34. "nodes": {
    35. "127.0.0.1:1980": 1
    36. }
    37. }
    38. }'

    插件测试:

    请求 5 次,3 次请求命中插件 1981 端口的 upstream, 2 次请求命中 route 的 1980 端口 upstream。

    1. $ curl http://127.0.0.1:9080/index.html -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. hello 1980
    5. $ curl http://127.0.0.1:9080/index.html -i
    6. HTTP/1.1 200 OK
    7. Content-Type: text/html; charset=utf-8
    8. world 1981
    9. ......

    通过请求头获取 match 规则参数 (也可以通过请求参数获取 NGINX 变量),在 match 规则匹配通过后,表示所有请求都命中到插件配置的 upstream ,否则所有请求只命中 route 上配置的 upstream 。

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "match": [
    9. {
    10. "vars": [
    11. ["http_release","==","new_release"]
    12. ]
    13. }
    14. ],
    15. "weighted_upstreams": [
    16. {
    17. "upstream": {
    18. "name": "upstream_A",
    19. "type": "roundrobin",
    20. "nodes": {
    21. "127.0.0.1:1981":10
    22. }
    23. }
    24. }
    25. ]
    26. }
    27. ]
    28. }
    29. },
    30. "upstream": {
    31. "type": "roundrobin",
    32. "nodes": {
    33. "127.0.0.1:1980": 1
    34. }
    35. }
    36. }'

    match 规则匹配通过,所有请求都命中插件配置的 1981 端口 upstream :

    1. $ curl http://127.0.0.1:9080/index.html -H 'release: new_release' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. world 1981

    match 规则匹配失败,所有请求都命中 route 上配置的 1980 端口 upstream :

    match 中可以设置多个 vars 规则,vars 中的多个表达式之间是 and 的关系, 多个 vars 规则之间是 or 的关系;只要其中一个 vars 规则通过,则整个 match 通过。

    示例 1:只配置了一个 vars 规则, vars 中的多个表达式是 and 的关系。在 weighted_upstreams 中根据 weight 值将流量按 3:2 划分,其中只有 weight 值的部分表示 route 上的 upstream 所占的比例。 当 match 匹配不通过时,所有的流量只会命中 route 上的 upstream 。

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "match": [
    9. {
    10. "vars": [
    11. ["arg_name","==","jack"],
    12. ["http_user-id",">","23"],
    13. ["http_apisix-key","~~","[a-z]+"]
    14. ]
    15. }
    16. ],
    17. "weighted_upstreams": [
    18. {
    19. "upstream": {
    20. "name": "upstream_A",
    21. "type": "roundrobin",
    22. "nodes": {
    23. "127.0.0.1:1981":10
    24. }
    25. },
    26. "weight": 3
    27. },
    28. {
    29. "weight": 2
    30. }
    31. ]
    32. }
    33. ]
    34. }
    35. },
    36. "upstream": {
    37. "type": "roundrobin",
    38. "nodes": {
    39. "127.0.0.1:1980": 1
    40. }
    41. }
    42. }'

    插件设置了请求的 match 规则及端口为 1981 的 upstream,route 上具有端口为 1980 的 upstream。

    插件测试:

    1、在 match 规则校验通过后,60% 的请求命中到插件的 1981 端口的 upstream, 40% 的请求命中到 route 的 1980 端口的 upstream。

    match 规则校验成功, 命中端口为 1981 的 upstream。

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'apisix-key: hello' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. world 1981

    match 规则校验失败,命中默认端口为 1980 的 upstream。

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'apisix-key: hello' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. hello 1980

    在请求 5 次后,3 次命中 1981 端口的服务,2 次命中 1980 端口的服务。

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. hello 1980

    示例 2:配置多个 vars 规则, vars 中的多个表达式是 and 的关系, 多个 vars 之间是 or 的关系。根据 weighted_upstreams 中的 weight 值将流量按 3:2 划分,其中只有 weight 值的部分表示 route 上的 upstream 所占的比例。 当 match 匹配不通过时,所有的流量只会命中 route 上的 upstream 。

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "match": [
    9. {
    10. ["arg_name","==","jack"],
    11. ["http_user-id",">","23"],
    12. ["http_apisix-key","~~","[a-z]+"]
    13. ]
    14. },
    15. {
    16. "vars": [
    17. ["arg_name2","==","rose"],
    18. ["http_user-id2","!",">","33"],
    19. ["http_apisix-key2","~~","[a-z]+"]
    20. }
    21. ],
    22. "weighted_upstreams": [
    23. {
    24. "upstream": {
    25. "name": "upstream_A",
    26. "type": "roundrobin",
    27. "nodes": {
    28. "127.0.0.1:1981":10
    29. }
    30. },
    31. "weight": 3
    32. },
    33. {
    34. "weight": 2
    35. }
    36. ]
    37. }
    38. ]
    39. }
    40. },
    41. "upstream": {
    42. "type": "roundrobin",
    43. "nodes": {
    44. "127.0.0.1:1980": 1
    45. }
    46. }
    47. }'

    测试插件:

    1、两个 vars 的表达式匹配成功, match 规则校验通过后,60% 的请求命中到插件的 1981 端口 upstream, 40% 的请求命中到 route 的 1980 端口 upstream。

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack&name2=rose' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. world 1981

    在请求 5 次后,3 次命中 1981 端口的服务,2 次命中 1980 端口的服务。

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. world 1981
    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -H 'user-id:30' -H 'user-id2:22' -H 'apisix-key: hello' -H 'apisix-key2: world' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. hello 1980

    在请求 5 次后,3 次命中 1981 端口的服务,2 次命中 1980 端口的服务。

    3、两个 vars 的表达式校验失败(缺少 namename2 请求参数),match 规则校验失败,响应都为默认 route 的 upstream 数据 hello 1980

    1. $ curl 'http://127.0.0.1:9080/index.html?name=jack' -i
    2. HTTP/1.1 200 OK
    3. Content-Type: text/html; charset=utf-8
    4. ......
    5. hello 1980

    通过配置多个 rules,我们可以实现不同的匹配规则与上游一一对应。

    示例:

    当请求头 x-api-id 等于 1 时,命中 1981 端口的上游;当 x-api-id 等于 2 时,命中 1982 端口的上游;否则,命中 1980 端口的上游(上游响应数据为对应的端口号)。

    1. curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/hello",
    4. "plugins": {
    5. "traffic-split": {
    6. "rules": [
    7. {
    8. "match": [
    9. {
    10. "vars": [
    11. ["http_x-api-id","==","1"]
    12. ]
    13. }
    14. ],
    15. "weighted_upstreams": [
    16. {
    17. "upstream": {
    18. "name": "upstream-A",
    19. "type": "roundrobin",
    20. "nodes": {
    21. "127.0.0.1:1981":1
    22. }
    23. },
    24. "weight": 3
    25. }
    26. ]
    27. },
    28. {
    29. "match": [
    30. {
    31. "vars": [
    32. ["http_x-api-id","==","2"]
    33. ]
    34. }
    35. ],
    36. "weighted_upstreams": [
    37. {
    38. "upstream": {
    39. "name": "upstream-B",
    40. "type": "roundrobin",
    41. "nodes": {
    42. "127.0.0.1:1982":1
    43. }
    44. },
    45. "weight": 3
    46. }
    47. ]
    48. }
    49. ]
    50. }
    51. },
    52. "upstream": {
    53. "type": "roundrobin",
    54. "nodes": {
    55. "127.0.0.1:1980": 1
    56. }
    57. }
    58. }'

    测试插件:

    请求头 x-api-id 等于 1,命中带 1981 端口的上游。

    1. $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 1'
    2. 1981

    请求头 x-api-id 等于 2,命中带 1982 端口的上游。

    1. $ curl http://127.0.0.1:9080/hello -H 'x-api-id: 2'
    2. 1982

    当你想去掉 traffic-split 插件的时候,很简单,在插件的配置中把对应的 json 配置删除即可,无须重启服务,即刻生效:

    1. $ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
    2. {
    3. "uri": "/index.html",
    4. "plugins": {},
    5. "upstream": {
    6. "type": "roundrobin",
    7. "nodes": {
    8. "127.0.0.1:1980": 1
    9. }
    10. }'