内置变量
APISIX 中的_内置变量_是预定义的变量,可以直接在配置中引用。它们通常用于插件配置、路由匹配和日志自定义。
APISIX 支持三种类型的内置变量:
- NGINX 变量
- APISIX 变量
- 自定义变量
这些变量按 给定的顺序 进行评估。
NGINX 变量
NGINX 提供了一组变量,可用于访问各种特定于请求的信息。
一些常用的变量包括:
upstream_addrremote_addrrequest_uriserver_nameurihttp_user_agent
有关更多信息,请参阅 NGINX 变量的完整列表。
APISIX 变量
除了 NGINX 变量 之外,APISIX 还提供了各种内置变量:
| 变量名 | 描述 |
|---|---|
post_arg_* | 当内容类型为 application/x-www-form-urlencoded 时的 HTTP POST 表单数据。星号将替换为 POST 表单数据的实际名称。 |
post_arg.* | 当内容类型为 application/json、application/x-www-form-urlencoded 或 multipart/form-data 时的 HTTP POST 正文参数。星号将替换为 POST 参数的实际名称。支持类似 JSON 路径的选择,例如 post_arg.model.version 和 post_arg.messages[*].content[*].type。 |
arg_* | URL 查询字符串。星号将替换为实际的查询参数名称。 |
uri_param_* | 当 APISIX 使用 radixtree_uri_with_parameter 路由器 时的 URL 参数。星号将替换为实际的查询参数名称。 |
http_* | HTTP 请求头。星号将替换为头的实际名称。 |
cookie_* | 请求 Cookie。星号将替换为 Cookie 的实际名称。 |
balancer_ip | 上游服务器 IP。 |
balancer_port | 上游服务器端口。 |
consumer_name | 消费者用户名。 |
consumer_group_id | 消费者组 ID。 |
graphql_name | GraphQL 操作名称。 |
graphql_operation | GraphQL 操作类型。 |
graphql_root_fields | GraphQL 根字段。 |
rate_limiting_info | 限流插件生成的限流结果信息。 |
route_id | 路由 ID。 |
route_name | 路由名称。 |
service_id | 服务 ID。 |
service_name | 服务名称。 |
resp_body | HTTP 响应正文。 |
mqtt_client_id | MQTT 协议中的客户端 ID。 |
redis_cmd_line | Redis 命令行。 |
rpc_time | RPC 请求往返时间。 |
自定义变量
你还可以注册自己的变量并将它们用作内置变量。例如,你可以使用自定义变量来自定义日志插件中的日志格式,或将它们用作限流限速插件中的键。
示例
以下示例演示了注册自定义变量的两种方法,以及如何利用该变量从路由获取信息,随后将该信息记录到远程服务器。
创建服务
创建一个服务以配置 http-logger 插件和上游:
curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \
-H 'X-API-KEY: ${ADMIN_API_KEY}' \
-d '{
"id":"srv_custom_var",
"plugins": {
"http-logger": {
"uri": "'"${REMOTE_SERVER_ADDR}"'"
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
}
}
}'
注册自定义变量
你可以选择在源代码中注册自定义变量,或使用 serverless 插件。
方法 1:在源代码中
将以下代码段添加到你的自定义 Lua 文件并引用它,以注册一个名为 a6_route_labels 的自定义变量。该变 量表示路由请求中 labels 的值(如果可用):
local core = require "apisix.core"
core.ctx.register_var("a6_route_labels", function(ctx)
local route = ctx.matched_route and ctx.matched_route.value
if route and route.labels then
return route.labels
end
return nil
end)
相应地 启动或重新加载 APISIX。如果 APISIX 已经在运行,请向 /apisix/admin/plugins/reload 发送 PUT 请求以 热加载插件 使更改生效。
虽然从技术上讲,可以将代码段添加到代码引用的任何位置,但在修改 APISIX 核心代码库时要小心,以免对标准功能产生任何负面影响。
建议将你的自定义 Lua 代码保存在单独的目录中,并通过在 config.yaml 配置文件 中配置 extra_lua_path 和 extra_lua_cpath 来引用它。
有关更多信息,请参阅 创建 Lua 插件指南。
方法 2:在 serverless 插件中
你还可以使用 serverless-pre-function 或 serverless-post-function 无服务器函数插件 注册自定义变量。这些插件在指定的 执行阶段 之前或之后运行无服务器函数, 你可以在这些函数中注册自定义变量。
将 serverless-pre-function 插件添加到之前创建的服务中,其中函数注册自定义变量 a6_route_labels:
curl "http://127.0.0.1:9180/apisix/admin/services/srv_custom_var" -X PATCH \
-H 'X-API-KEY: ${ADMIN_API_KEY}' \
-d '{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function()
local core = require \"apisix.core\"
core.ctx.register_var(\"a6_route_labels\", function(ctx)
local route = ctx.matched_route and ctx.matched_route.value
if route and route.labels then
return route.labels
end
return nil
end);
end"
]
}
}
}'
方法 3:在插件的 _meta.prefunction 中
如果你更喜欢在插件中注册自定义变量而不使用 serverless 插件,你可以使用 _meta.pre_function 来配置在插件执行的每个 阶段 之前执行的自定义代码。
以下示例将向你展示如何在 proxy-rewrite 插件中声明 _meta.pre_function,以提取请求路径中的 user_id,将其注册为变量,并使用它来组成新的请求路径。
为了在路由的 URI 中使用参数,你应该首先将 配置文件 中的路由器更新为 radixtree_uri_with_parameter,因为它不是默认设置:
apisix:
router:
http: radixtree_uri_with_parameter
然后 重新加载 APISIX 以使更改生效。
创建一个到 httpbin 服务的路由以检查重写后的路径:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "pre-func-route",
"uri": "/anything/:user_id/hello",
"plugins": {
"proxy-rewrite": {
"_meta": {
"pre_function": "
return function(conf, ctx)
local core = require \"apisix.core\"
core.ctx.register_var(\"user_id\", function(ctx) return ctx.curr_req_matched.user_id end)
end"
},
"uri": "/anything/$user_id/world"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
❶ 匹配请求 /anything/:user_id/hello,其中 user_id 是一个参数。
❷ 自定义 _meta.pre_function 以从请求路径中提取 user_id 值并将其保存到同名变量中。
❸ 将请求路径重写为 /anything/$user_id/world,其中 $user_id 将被替换为变量值。
向路由发送请求:
curl -i "http://127.0.0.1:9080/anything/johndoe/hello"
你应该观察到以下响应,显示请求路径已部分重写为 user_id:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "text/html..."
...
},
"json": null,
"method": "GET",
"origin": "127.0.0.1, 59.71.xxx.xxx",
"url": "http://127.0.0.1/anything/johndoe/world"
}
配置日志格式
创建一个 插件元数据 对象,为所有 http-logger 实例配置日志格式:
curl "http://127.0.0.1:9180/apisix/admin/plugin_metadata/http-logger" -X PUT \
-H 'X-API-KEY: ${ADMIN_API_KEY}' \
-d '{
"log_format": {
"host": "$host",
"client_ip": "$remote_addr",
"labels": "$a6_route_labels"
}
}'
❶ host 和 remote_addr:NGINX 变量。
❷ a6_route_labels:自定义变量。
在服务中创建路由
在服务中创建 路由:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H 'X-API-KEY: ${ADMIN_API_KEY}' \
-d '{
"id":"route_custom_var",
"uri":"/get",
"service_id": "srv_custom_var",
"labels": {
"key": "test_a6_route_labels"
}
}'
❶ service_id:对应之前创建的服务。
❷ labels:要与自定义变量一起记录的路由信息。
验证自定义变量注册
发送请求以验证自定义变量是否记录了路由中的 labels 信息:
curl "http://127.0.0.1:9080/get"
你应该在远程服务器中看到一个由 POST 请求创建的日志条目,其正文类似于以下内容:
[
{
"labels": {
"key": "test_a6_route_labels"
},
"service_id": "srv_custom_var",
"client_ip": "172.21.0.1",
"route_id": "route_custom_var",
"host": "127.0.0.1"
}
]
这验证了自定义变量已注册,并且它成功记录了路由中的 labels 信息。
评估顺序
APISIX 按给定顺序评估变量:
- 自定义变量
- APISIX 变量
- NGINX 变量
如果变量在自定义变量中成功获取,APISIX 将不会继续在 APISIX 变量或 NGINX 变量中查找。
换句话说,自定义变量将覆盖 APISIX 变量或 NGINX 变量中定义的同名变量,以更好地满足你的特定用例需求。
可变语法
一个有效的可变名称可以包括字母,数字,下划线(_),以及周期(.).
带反斜角的逃逸变量(\)不作为变量处理,例如\$variable_name。
简单而粗糙的表格
一个变量可以两种形式引用:
$variable_name${variable_name}
这两种语法都得到了支持;然而,在某些情况下需要加固形式以确保正确的解析.
$variable的未覆盖形式在以下字符 不能 成为变量名称的一部分时有效. 例如,下列形式是等同的:
$http_host和${http_host}$arg_username-$arg_userid和${arg_username}-${arg_userid}
当一个变量后面有一个可能属于变量名称的字符时,解析器会错误地将其解释为更长的变量名称. 例如,使用https://$http_baseurl.com,解析器会将整个字符串http_baseurl.com作为变量,这是不正确的. 在此情况下,您应当使用https://${http_baseurl}.com的括号来明确定义变量。
如果使用??操作器, 您还需要加挂的表格 。
带有??的默认值
您可以使用??运算符指定变量的默认值。 如果不定义变量,则使用操作员之后的值。
| 示例 | 表现 |
|---|---|
${http_username ?? anonymous} | 如果定义了HTTP请求头username,则使用其值;否则使用字符串anonymous. |
${http_count ?? 10} | 如果定义了HTTP请求头count,则使用其值;否则使用字符串10. |