路由 RadixTree

什么是 libradixtree?

libradixtree, 是在 Lua 中为 OpenResty 实现的自适应 基数树

Apache APISIX 使用 libradixtree 作为路由调度库。

如何在 Apache APISIX 中使用 libradixtree?

libradixtree 是基于 raxlua-resty-* 实现。

我们通过下面的示例可以有一个直观的理解。

1. 完全匹配

  1. /blog/foo

此时只能匹配 /blog/foo

2. 前缀匹配

  1. /blog/bar*

它将匹配带有前缀 /blog/bar 的路径, 例如: /blog/bar/a/blog/bar/b/blog/bar/c/d/e/blog/bar 等。

3. 匹配优先级

完全匹配 -> 深度前缀匹配

以下是规则:

  1. /blog/foo/*
  2. /blog/foo/a/*
  3. /blog/foo/c/*
  4. /blog/foo/bar
路径匹配结果
/blog/foo/bar/blog/foo/bar
/blog/foo/a/b/c/blog/foo/a/
/blog/foo/c/d/blog/foo/c/
/blog/foo/gloo/blog/foo/*
/blog/barnot match

4. 不同的路由具有相同 uri

当不同的路由有相同的 uri 时,可以通过设置路由的 priority 字段来决定先匹配哪条路由,或者添加其他匹配规则来区分不同的路由。

注意:在匹配规则中, priority 字段优先于除 uri 之外的其他规则。

1、不同的路由有相同的 uri 并设置 priority 字段

创建两条 priority 值不同的路由(值越大,优先级越高)。

路由 RadixTree - 图1note

您可以这样从 config.yaml 中获取 admin_key 并存入环境变量:

  1. admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
路由 RadixTree - 图2note

您可以这样从 config.yaml 中获取 admin_key 并存入环境变量:

  1. admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d '
  2. {
  3. "upstream": {
  4. "nodes": {
  5. "127.0.0.1:1980": 1
  6. },
  7. "type": "roundrobin"
  8. },
  9. "priority": 3,
  10. "uri": "/hello"
  11. }'
  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/2 -H "X-API-KEY: $admin_key" -X PUT -d '
  2. {
  3. "upstream": {
  4. "nodes": {
  5. "127.0.0.1:1981": 1
  6. },
  7. "type": "roundrobin"
  8. },
  9. "priority": 2,
  10. "uri": "/hello"
  11. }'

测试:

  1. curl http://127.0.0.1:1980/hello
  2. 1980

所有请求只到达端口 1980 的路由。

2、不同的路由有相同的 uri 并设置不同的匹配条件

以下是设置主机匹配规则的示例:

  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d '
  2. {
  3. "upstream": {
  4. "nodes": {
  5. "127.0.0.1:1980": 1
  6. },
  7. "type": "roundrobin"
  8. },
  9. "hosts": ["localhost.com"],
  10. "uri": "/hello"
  11. }'
  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/2 -H "X-API-KEY: $admin_key" -X PUT -d '
  2. {
  3. "upstream": {
  4. "nodes": {
  5. "127.0.0.1:1981": 1
  6. },
  7. "type": "roundrobin"
  8. },
  9. "hosts": ["test.com"],
  10. "uri": "/hello"
  11. }'

测试:

  1. $ curl http://127.0.0.1:9080/hello -H 'host: localhost.com'
  2. 1980
  1. $ curl http://127.0.0.1:9080/hello -H 'host: test.com'
  2. 1981
  1. $ curl http://127.0.0.1:9080/hello
  2. {"error_msg":"404 Route Not Found"}

host 规则匹配,请求命中对应的上游,host 不匹配,请求返回 404 消息。

5. 参数匹配

当使用 radixtree_uri_with_parameter 时,我们可以用参数匹配路由。

例如,使用配置:

  1. apisix:
  2. router:
  3. http: 'radixtree_uri_with_parameter'

示例:

  1. /blog/:name

此时将匹配 /blog/dog/blog/cat

更多使用方式请参考:lua-resty-radixtree#parameters-in-path

如何通过 Nginx 内置变量过滤路由

具体参数及使用方式请查看 radixtree#new 文档,下面是一个简单的示例:

  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d '
  2. {
  3. "uri": "/index.html",
  4. "vars": [
  5. ["http_host", "==", "iresty.com"],
  6. ["cookie_device_id", "==", "a66f0cdc4ba2df8c096f74c9110163a9"],
  7. ["arg_name", "==", "json"],
  8. ["arg_age", ">", "18"],
  9. ["arg_address", "~~", "China.*"]
  10. ],
  11. "upstream": {
  12. "type": "roundrobin",
  13. "nodes": {
  14. "127.0.0.1:1980": 1
  15. }
  16. }
  17. }'

这个路由需要请求头 host 等于 iresty.com, 请求 cookie _device_id 等于 a66f0cdc4ba2df8c096f74c9110163a9 等。

如何通过 POST 表单属性过滤路由

APISIX 支持通过 POST 表单属性过滤路由,其中需要您使用 Content-Type = application/x-www-form-urlencoded 的 POST 请求。

我们可以定义这样的路由:

  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d '
  2. {
  3. "methods": ["POST"],
  4. "uri": "/_post",
  5. "vars": [
  6. ["post_arg_name", "==", "json"]
  7. ],
  8. "upstream": {
  9. "type": "roundrobin",
  10. "nodes": {
  11. "127.0.0.1:1980": 1
  12. }
  13. }
  14. }'

当 POST 表单中包含 name=json 的属性时,将匹配到路由。

如何通过 GraphQL 属性过滤路由

目前,APISIX 可以处理 HTTP GET 和 POST 方法。请求体正文可以是 GraphQL 查询字符串,也可以是 JSON 格式的内容。

APISIX 支持通过 GraphQL 的一些属性过滤路由。目前我们支持:

  • graphql_operation
  • graphql_name
  • graphql_root_fields

例如,像这样的 GraphQL:

  1. query getRepo {
  2. owner {
  3. name
  4. }
  5. repo {
  6. created
  7. }
  8. }
  • graphql_operationquery
  • graphql_namegetRepo
  • graphql_root_fields["owner", "repo"]

我们可以用以下方法过滤掉这样的路由:

  1. $ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -i -d '
  2. {
  3. "methods": ["POST", "GET"],
  4. "uri": "/graphql",
  5. "vars": [
  6. ["graphql_operation", "==", "query"],
  7. ["graphql_name", "==", "getRepo"],
  8. ["graphql_root_fields", "has", "owner"]
  9. ],
  10. "upstream": {
  11. "type": "roundrobin",
  12. "nodes": {
  13. "127.0.0.1:1980": 1
  14. }
  15. }
  16. }'

我们可以通过以下三种方式分别去验证 GraphQL 匹配:

  1. 使用 GraphQL 查询字符串
  1. $ curl -H 'content-type: application/graphql' -X POST http://127.0.0.1:9080/graphql -d '
  2. query getRepo {
  3. owner {
  4. name
  5. }
  6. repo {
  7. created
  8. }
  9. }'
  1. 使用 JSON 格式
  1. $ curl -H 'content-type: application/json' -X POST \
  2. http://127.0.0.1:9080/graphql --data '{"query": "query getRepo { owner {name } repo {created}}"}'
  1. 尝试 GET 请求
  1. $ curl -H 'content-type: application/graphql' -X GET \
  2. "http://127.0.0.1:9080/graphql?query=query getRepo { owner {name } repo {created}}" -g

为了防止花费太多时间读取无效的 GraphQL 请求正文,我们只读取前 1 MiB 来自请求体的数据。此限制是通过以下方式配置的:

  1. graphql:
  2. max_size: 1048576

如果你需要传递一个大于限制的 GraphQL 查询语句,你可以增加 conf/config.yaml 中的值。