跳到主要内容
版本:3.19.0

内置变量

APISIX 中的_内置变量_是预定义的变量,可以直接在配置中引用。它们通常用于插件配置、路由匹配和日志自定义。

APISIX 支持三种类型的内置变量:

  • NGINX 变量
  • APISIX 变量
  • 自定义变量

这些变量按 给定的顺序 进行评估。

NGINX 变量​

NGINX 提供了一组变量,可用于访问各种特定于请求的信息。

常用变量包括:

  • upstream_addr
  • remote_addr
  • request_uri
  • server_name
  • uri
  • http_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_nameGraphQL 操作名称。
graphql_operationGraphQL 操作类型。
graphql_root_fieldsGraphQL 根字段。
rate_limiting_infolimit-count 插件生成的 JSON 限流结果。插件运行前为空。
route_id路由 ID。
route_name路由名称。
service_id服务 ID。
service_name服务名称。
upstream_unresolved_hostDNS 解析前所选上游节点的主机名或 IP 地址。对于配置为 httpbin.org 的节点,此变量保持为 httpbin.org,而 balancer_ip 包含解析后的地址。
resp_bodyHTTP 响应正文。
request_type可观测性插件使用的请求类别,例如 traditional_http、ai_chat 或 ai_stream。WebSocket 成功升级后会归类为 websocket。
mqtt_client_idMQTT 协议中的客户端 ID。
redis_cmd_lineRedis 命令行。
rpc_timeRPC 请求往返时间。

记录外部插件的上游耗时​

当 ext-plugin-post-resp 获取上游响应时,日志中也会填充 upstream_addr 和 upstream_response_time。响应时间包括连接和请求头耗时,以及读取上游响应正文所用的时间;不包括外部运行器的处理时间,也不包括向客户端发送响应的时间。

记录 Limit Count 结果​

limit-count 插件在评估请求后设置 rate_limiting_info。其他限流插件不会填充此变量。在 limit-count 运行之前,或路由未配置该插件时,该值为空字符串。

JSON 值包含以下字段:

字段描述
rate_limiting_key请求实际使用的计数器 Key。
rate_limiting_limit已配置的请求限制。
rate_limiting_remaining当前请求后的剩余配额。
rate_limiting_reset计数器重置前的秒数。

要在访问日志中包含该值,请将其添加到 config.yaml 的访问日志格式中:

config.yaml
nginx_config:
http:
access_log_format: >-
$remote_addr "$request" $status $rate_limiting_info

重新加载 APISIX,在路由上配置 limit-count,然后发送请求。访问日志应包含类似以下的值:

{\x22rate_limiting_key\x22:\x22/apisix/routes/limit-route:1729132800:example.com\x22,\x22rate_limiting_limit\x22:100,\x22rate_limiting_remaining\x22:99,\x22rate_limiting_reset\x22:60}

默认的 NGINX 日志转义会将引号表示为 \x22。下游日志处理器应先解码转义值,再将其解析为 JSON。

该 Key 是用于关联和故障排查的实现值。不要将其内部组成部分解析为稳定的公共格式。

自定义变量​

你还可以注册自己的变量并将它们用作内置变量。例如,你可以使用自定义变量来自定义日志插件中的日志格式,或将它们用作限流限速插件中的键。

示例​

以下示例演示了注册自定义变量的三种方法。前两种方法注册 a6_route_labels,并将路由标签写入远程日志;第三种方法使用 _meta.pre_function 注册路径参数并重写 URI。

创建服务​

以下请求创建一个配置了 http-logger 插件和上游的服务:

curl "http://127.0.0.1:9180/apisix/admin/services" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
--data-binary @- <<EOF
{
"id":"srv_custom_var",
"plugins": {
"http-logger": {
"uri": "${REMOTE_SERVER_ADDR}"
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
}
}
}
EOF

注册自定义变量​

你可以在启动钩子、无服务器函数插件或插件的 _meta.pre_function 中注册自定义变量。以下方法分别演示这些方式。

方法 1:使用自定义 Lua 模块在启动时注册​

以下示例演示如何在启动时注册自定义变量。将模块保存为 /opt/apisix-custom/custom_variables.lua。当匹配的路由存在 labels 值时,a6_route_labels 变量会返回该值:

require "apisix"
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)

return true

APISIX 在初始化时会先于主模块加载该钩子,因此钩子需要先加载 apisix,再加载 apisix.core。

配置 APISIX,使其能够在启动时找到并加载该模块:

config.yaml
apisix:
extra_lua_path: "/opt/apisix-custom/?.lua"
lua_module_hook: custom_variables

extra_lua_path 将目录加入 Lua 模块搜索路径,lua_module_hook 则在 APISIX 初始化时显式加载 custom_variables。更改模块或配置后需要重新加载 APISIX。Admin API 的插件重载操作不会重新加载这个启动钩子。

有关更多信息,请参阅 创建 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.pre_function 中​

你可以使用 _meta.pre_function,在插件的每个执行阶段开始前运行自定义代码。

以下示例演示如何在 proxy-rewrite 中配置 _meta.pre_function,将请求路径的 user_id 参数注册为变量,并在重写后的路径中使用该变量。

要在路由 URI 中使用参数,请先在配置文件中将路由器设置为 radixtree_uri_with_parameter:

config.yaml
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 值重写后的 URL:

{
"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 按给定顺序评估变量:

  1. 自定义变量
  2. APISIX 变量
  3. 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。