执行顺序介绍
apisix中插件的执行顺序由两个因素决定,首先是执行阶段
(phase
),如下所示,主要包括:rewrite → access → before_proxy → header_filter → body_filter → log几个阶段。
其次,影响执行顺序的还有一个因素priority
,在apisix的配置文件config.yaml中的plugins节点有记载,大致如下,不同版本可能不太一样,仅做参考。
plugins: # plugin list (sorted by priority)
- real-ip # priority: 23000
- ai # priority: 22900
- client-control # priority: 22000
- proxy-control # priority: 21990
- request-id # priority: 12015
- zipkin # priority: 12011
#- skywalking # priority: 12010
#- opentelemetry # priority: 12009
- ext-plugin-pre-req # priority: 12000
- fault-injection # priority: 11000
- mocking # priority: 10900
- serverless-pre-function # priority: 10000
#- batch-requests # priority: 4010
- cors # priority: 4000
- ip-restriction # priority: 3000
- ua-restriction # priority: 2999
- referer-restriction # priority: 2990
- csrf # priority: 2980
- uri-blocker # priority: 2900
- request-validation # priority: 2800
- chaitin-waf # priority: 2700
- multi-auth # priority: 2600
- openid-connect # priority: 2599
- cas-auth # priority: 2597
- authz-casbin # priority: 2560
- authz-casdoor # priority: 2559
- wolf-rbac # priority: 2555
- ldap-auth # priority: 2540
- hmac-auth # priority: 2530
- basic-auth # priority: 2520
- jwt-auth # priority: 2510
- jwe-decrypt # priority: 2509
- key-auth # priority: 2500
- consumer-restriction # priority: 2400
- attach-consumer-label # priority: 2399
- forward-auth # priority: 2002
- opa # priority: 2001
- authz-keycloak # priority: 2000
#- error-log-logger # priority: 1091
- proxy-cache # priority: 1085
- body-transformer # priority: 1080
- ai-prompt-template # priority: 1071
- ai-prompt-decorator # priority: 1070
- proxy-mirror # priority: 1010
- proxy-rewrite # priority: 1008
- workflow # priority: 1006
- api-breaker # priority: 1005
- limit-conn # priority: 1003
- limit-count # priority: 1002
- limit-req # priority: 1001
#- node-status # priority: 1000
- ai-proxy # priority: 999
#- brotli # priority: 996
- gzip # priority: 995
- server-info # priority: 990
- traffic-split # priority: 966
- redirect # priority: 900
- response-rewrite # priority: 899
- degraphql # priority: 509
- kafka-proxy # priority: 508
#- dubbo-proxy # priority: 507
- grpc-transcode # priority: 506
- grpc-web # priority: 505
- http-dubbo # priority: 504
- public-api # priority: 501
- prometheus # priority: 500
- datadog # priority: 495
- loki-logger # priority: 414
- elasticsearch-logger # priority: 413
- echo # priority: 412
- loggly # priority: 411
- http-logger # priority: 410
- splunk-hec-logging # priority: 409
- skywalking-logger # priority: 408
- google-cloud-logging # priority: 407
- sls-logger # priority: 406
- tcp-logger # priority: 405
- kafka-logger # priority: 403
- rocketmq-logger # priority: 402
- syslog # priority: 401
- udp-logger # priority: 400
- file-logger # priority: 399
- clickhouse-logger # priority: 398
- tencent-cloud-cls # priority: 397
- inspect # priority: 200
#- log-rotate # priority: 100
# <- recommend to use priority (0, 100) for your custom plugins
- example-plugin # priority: 0
#- gm # priority: -43
#- ocsp-stapling # priority: -44
- file-upload-filter # priority: -99
- aws-lambda # priority: -1899
- azure-functions # priority: -1900
- openwhisk # priority: -1901
- openfunction # priority: -1902
- serverless-post-function # priority: -2000
- ext-plugin-post-req # priority: -3000
- ext-plugin-post-resp # priority: -4000
总结: 两个(phase
和priority
)结合起来的影响,先根据执行阶段(phase
)排序,相同的执行阶段内,再根据priority
的大小排序,priority
值越大,越先执行。如果没有中途停止(比如高危请求直接中断返回),插件和阶段都会按顺序执行,也就是说只有满足所有插件规则的请求,最终才会到服务器。
案例说明
拿serverless-post-function与serverless-pre-function两个插件来举例
场景一:不同插件在相同phase
时
由配置文件可知,serverless-post-function
的priority
值为 -2000;serverless-pre-function
的priority
值为 10000。当两个插件在相同阶段时,两个插件的执行顺序仅根据priority
值来区分,值越大优先级越高,所以此处先执行的是serverless-pre-function
插件,后执行的是serverless-post-function
插件
场景二:不同插件在不同phase
时
将serverless-pre-function插件放在access阶段,将serverless-post-function放在rewrite阶段
由阶段执行顺序(rewrite → access → before_proxy → header_filter → body_filter → log)可知,rewrite阶段在access阶段前,故优先执行serverless-post-function
插件,后执行serverless-pre-function
插件
配置 serverless-post-function 在 rewrite 阶段
{
"uri": "/example/*",
"plugins": {
"serverless-post-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx) ngx.log(ngx.INFO, 'This is serverless-post-function in rewrite phase') end"
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}
配置 serverless-pre-function 在 access 阶段
{
"uri": "/example/*",
"plugins": {
"serverless-pre-function": {
"phase": "access",
"functions": [
"return function(conf, ctx) ngx.log(ngx.INFO, 'This is serverless-pre-function in access phase') end"
]
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"127.0.0.1:1980": 1
}
}
}
各个阶段上下文参数
插件在不同阶段(如 rewrite、access、header_filter、body_filter 等)执行时,上下文参数 conf 和 ctx 的基本结构是一致的,但它们的 可用数据和行为 会因阶段的不同而有所差异。以下是各阶段的上下文参数详解:
1. 上下文参数概览
conf:插件的配置参数,通常由用户在路由或全局插件配置中定义。
ctx:请求上下文对象,包含请求和响应的元数据(如 URI、请求头、响应状态码等)。
2. 各阶段的上下文参数差异
阶段 1:rewrite
执行时机: 请求到达 APISIX 后,修改请求 URI 或参数。
conf: 插件配置(如 uri_rewrite 规则)。
ctx:
- 包含请求信息(如 ctx.var.uri、ctx.var.args)。
- 不支持响应信息(如 ctx.var.status)。
示例:
function _M.rewrite(conf, ctx)
ngx.log(ngx.INFO, "URI: ", ctx.var.uri)
ctx.var.uri = "/new-path" -- 修改请求 URI
end
阶段 2:access
执行时机: 请求转发到上游服务之前,用于鉴权、限流等。
conf: 插件配置(如 limit-count 的限流规则)。
ctx:
- 包含完整的请求信息(如 ctx.var.request_uri、ctx.var.http_*)。
- 不支持响应信息。
示例:
function _M.access(conf, ctx)
if ctx.var.http_token ~= "secret" then
return 403, { message = "Forbidden" }
end
end
阶段 3:before_proxy
执行时机: 请求即将转发到上游服务之前,用于修改请求头或参数。
conf: 插件配置(如 proxy-rewrite 的请求头修改规则)。
ctx:
- 包含请求信息(如 ctx.var.http_*)。
- 不支持响应信息。
示例:
function _M.before_proxy(conf, ctx)
ngx.req.set_header("X-Custom-Header", "value")
end
阶段 4:header_filter
执行时机: 接收到上游响应头后,用于修改响应头。
conf: 插件配置(如 response-rewrite 的响应头规则)。
ctx:
- 包含响应头信息(如 ctx.var.status、ctx.var.header_*)。
- 不支持请求信息(如 ctx.var.uri)。
示例:
function _M.header_filter(conf, ctx)
ngx.header["X-APISIX-Version"] = "1.0"
end
阶段 5:body_filter
执行时机: 接收到上游响应体后,用于修改响应体。
conf: 插件配置(如 response-rewrite 的响应体规则)。
ctx:
- 包含响应信息(如 ctx.var.status、ctx.var.body)。
- 不支持请求信息。
示例:
function _M.body_filter(conf, ctx)
local body = ngx.arg[1]
if body then
ngx.arg[1] = body:gsub("foo", "bar") -- 修改响应体
end
end
阶段 6:log
执行时机: 请求处理完成后,用于记录日志。
conf: 插件配置(如 kafka-logger 的日志格式)。
ctx:
- 包含完整的请求和响应信息(如 ctx.var.request_uri、ctx.var.status)。
示例:
function _M.log(conf, ctx)
ngx.log(ngx.INFO, "Request completed: ", ctx.var.request_uri, " Status: ", ctx.var.status)
end
上下文参数对比表
阶段 | conf 可用性 | ctx 可用请求信息 | ctx 可用响应信息 |
---|---|---|---|
rewrite | ✔️ | ✔️ | ❌ |
access | ✔️ | ✔️ | ❌ |
before_proxy | ✔️ | ✔️ | ❌ |
header_filter | ✔️ | ❌ | ✔️ |
body_filter | ✔️ | ❌ | ✔️ |
log | ✔️ | ✔️ | ✔️ |
4. 注意事项
阶段限制: 某些变量(如 ctx.var.body)仅在特定阶段可用,需根据阶段选择合适操作。
性能影响: 在 body_filter 阶段修改响应体可能增加延迟,建议优化逻辑。
调试建议: 使用 ngx.log 记录调试信息,结合 APISIX 日志定位问题。
总结
conf: 在所有阶段均可用,表示插件配置。
ctx: 在不同阶段包含不同的请求和响应信息,需根据阶段合理使用。
阶段选择: 根据需求选择合适的阶段(如 rewrite 修改请求,header_filter 修改响应头)。