Skip to content

Commit cbc29a7

Browse files
authored
feat: allows users to specify plugin execution priority (#7273)
1 parent a4dd1ac commit cbc29a7

File tree

7 files changed

+777
-5
lines changed

7 files changed

+777
-5
lines changed

apisix/init.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,10 @@ function _M.http_access_phase()
445445
if changed then
446446
api_ctx.matched_route = route
447447
core.table.clear(api_ctx.plugins)
448-
api_ctx.plugins = plugin.filter(api_ctx, route, api_ctx.plugins)
448+
local phase = "rewrite_in_consumer"
449+
api_ctx.plugins = plugin.filter(api_ctx, route, api_ctx.plugins, nil, phase)
449450
-- rerun rewrite phase for newly added plugins in consumer
450-
plugin.run_plugin("rewrite_in_consumer", api_ctx.plugins, api_ctx)
451+
plugin.run_plugin(phase, api_ctx.plugins, api_ctx)
451452
end
452453
end
453454
plugin.run_plugin("access", plugins, api_ctx)

apisix/plugin.lua

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ local function sort_plugin(l, r)
6868
return l.priority > r.priority
6969
end
7070

71+
local function custom_sort_plugin(l, r)
72+
return l._meta.priority > r._meta.priority
73+
end
74+
7175

7276
local PLUGIN_TYPE_HTTP = 1
7377
local PLUGIN_TYPE_STREAM = 2
@@ -368,7 +372,7 @@ local function trace_plugins_info_for_debug(ctx, plugins)
368372
end
369373

370374

371-
function _M.filter(ctx, conf, plugins, route_conf)
375+
function _M.filter(ctx, conf, plugins, route_conf, phase)
372376
local user_plugin_conf = conf.value.plugins
373377
if user_plugin_conf == nil or
374378
core.table.nkeys(user_plugin_conf) == 0 then
@@ -378,6 +382,7 @@ function _M.filter(ctx, conf, plugins, route_conf)
378382
return plugins or core.tablepool.fetch("plugins", 0, 0)
379383
end
380384

385+
local custom_sort = false
381386
local route_plugin_conf = route_conf and route_conf.value.plugins
382387
plugins = plugins or core.tablepool.fetch("plugins", 32, 0)
383388
for _, plugin_obj in ipairs(local_plugins) do
@@ -392,6 +397,9 @@ function _M.filter(ctx, conf, plugins, route_conf)
392397
end
393398
end
394399

400+
if plugin_conf._meta and plugin_conf._meta.priority then
401+
custom_sort = true
402+
end
395403
core.table.insert(plugins, plugin_obj)
396404
core.table.insert(plugins, plugin_conf)
397405

@@ -401,6 +409,51 @@ function _M.filter(ctx, conf, plugins, route_conf)
401409

402410
trace_plugins_info_for_debug(ctx, plugins)
403411

412+
if custom_sort then
413+
local tmp_plugin_objs = core.tablepool.fetch("tmp_plugin_objs", 0, #plugins / 2)
414+
local tmp_plugin_confs = core.tablepool.fetch("tmp_plugin_confs", #plugins / 2, 0)
415+
416+
for i = 1, #plugins, 2 do
417+
local plugin_obj = plugins[i]
418+
local plugin_conf = plugins[i + 1]
419+
420+
-- in the rewrite phase, the plugin executes in the following order:
421+
-- 1. execute the rewrite phase of the plugins on route(including the auth plugins)
422+
-- 2. merge plugins from consumer and route
423+
-- 3. execute the rewrite phase of the plugins on consumer(phase: rewrite_in_consumer)
424+
-- in this case, we need to skip the plugins that was already executed(step 1)
425+
if phase == "rewrite_in_consumer" and not plugin_conf._from_consumer then
426+
plugin_conf._skip_rewrite_in_consumer = true
427+
end
428+
429+
tmp_plugin_objs[plugin_conf] = plugin_obj
430+
core.table.insert(tmp_plugin_confs, plugin_conf)
431+
432+
if not plugin_conf._meta then
433+
plugin_conf._meta = core.table.new(0, 1)
434+
plugin_conf._meta.priority = plugin_obj.priority
435+
else
436+
if not plugin_conf._meta.priority then
437+
plugin_conf._meta.priority = plugin_obj.priority
438+
end
439+
end
440+
end
441+
442+
sort_tab(tmp_plugin_confs, custom_sort_plugin)
443+
444+
local index
445+
for i = 1, #tmp_plugin_confs do
446+
index = i * 2 - 1
447+
local plugin_conf = tmp_plugin_confs[i]
448+
local plugin_obj = tmp_plugin_objs[plugin_conf]
449+
plugins[index] = plugin_obj
450+
plugins[index + 1] = plugin_conf
451+
end
452+
453+
core.tablepool.release("tmp_plugin_objs", tmp_plugin_objs)
454+
core.tablepool.release("tmp_plugin_confs", tmp_plugin_confs)
455+
end
456+
404457
return plugins
405458
end
406459

@@ -757,6 +810,11 @@ function _M.run_plugin(phase, plugins, api_ctx)
757810
phase = "rewrite"
758811
end
759812
local phase_func = plugins[i][phase]
813+
814+
if phase == "rewrite" and plugins[i + 1]._skip_rewrite_in_consumer then
815+
goto CONTINUE
816+
end
817+
760818
if phase_func then
761819
plugin_run = true
762820
local conf = plugins[i + 1]
@@ -784,6 +842,8 @@ function _M.run_plugin(phase, plugins, api_ctx)
784842
end
785843
end
786844
end
845+
846+
::CONTINUE::
787847
end
788848
return api_ctx, plugin_run
789849
end

apisix/schema_def.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,10 @@ _M.plugin_injected_schema = {
952952
{ type = "object" },
953953
}
954954
},
955+
priority = {
956+
description = "priority of plugins by customized order",
957+
type = "integer",
958+
},
955959
}
956960
}
957961
}

docs/en/latest/terminology/plugin.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,43 @@ the configuration above means customizing the error response from the jwt-auth p
9595
| Name | Type | Description |
9696
|--------------|------|-------------|
9797
| error_response | string/object | Custom error response |
98+
| priority | integer | Custom plugin priority |
99+
100+
### Custom Plugin Priority
101+
102+
All plugins have a default priority, but it is possible to customize the plugin priority to change the plugin's execution order.
103+
104+
```json
105+
{
106+
"serverless-post-function": {
107+
"_meta": {
108+
"priority": 10000
109+
},
110+
"phase": "rewrite",
111+
"functions" : ["return function(conf, ctx)
112+
ngx.say(\"serverless-post-function\");
113+
end"]
114+
},
115+
"serverless-pre-function": {
116+
"_meta": {
117+
"priority": -2000
118+
},
119+
"phase": "rewrite",
120+
"functions": ["return function(conf, ctx)
121+
ngx.say(\"serverless-pre-function\");
122+
end"]
123+
}
124+
}
125+
```
126+
127+
The default priority of serverless-pre-function is 10000, and the default priority of serverless-post-function is -2000. By default, the serverless-pre-function plugin will be executed first, and serverless-post-function plugin will be executed next.
128+
129+
The above configuration means setting the priority of the serverless-pre-function plugin to -2000 and the priority of the serverless-post-function plugin to 10000. The serverless-post-function plugin will be executed first, and serverless-pre-function plugin will be executed next.
130+
131+
Note:
132+
133+
- Custom plugin priority only affects the current object(route, service ...) of the plugin instance binding, not all instances of that plugin. For example, if the above plugin configuration belongs to Route A, the order of execution of the plugins serverless-post-function and serverless-post-function on Route B will not be affected and the default priority will be used.
134+
- Custom plugin priority does not apply to the rewrite phase of some plugins configured on the consumer. The rewrite phase of plugins configured on the route will be executed first, and then the rewrite phase of plugins (exclude auth plugins) from the consumer will be executed.
98135

99136
## Hot Reload
100137

docs/zh/latest/terminology/plugin.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,43 @@ local _M = {
8989
| 名称 | 类型 | 描述 |
9090
|--------------|------|----------------|
9191
| error_response | string/object | 自定义错误响应 |
92+
| priority | integer | 自定义插件优先级 |
93+
94+
### 自定义插件优先级
95+
96+
所有插件都有默认优先级,但是可以自定义插件优先级来改变插件执行顺序。
97+
98+
```json
99+
{
100+
"serverless-post-function": {
101+
"_meta": {
102+
"priority": 10000
103+
},
104+
"phase": "rewrite",
105+
"functions" : ["return function(conf, ctx)
106+
ngx.say(\"serverless-post-function\");
107+
end"]
108+
},
109+
"serverless-pre-function": {
110+
"_meta": {
111+
"priority": -2000
112+
},
113+
"phase": "rewrite",
114+
"functions": ["return function(conf, ctx)
115+
ngx.say(\"serverless-pre-function\");
116+
end"]
117+
}
118+
}
119+
```
120+
121+
serverless-pre-function 的默认优先级是 10000,serverless-post-function 的默认优先级是 -2000。默认情况下会先执行 serverless-pre-function 插件,再执行 serverless-post-function 插件。
122+
123+
上面的配置意味着将 serverless-pre-function 插件的优先级设置为 -2000,serverless-post-function 插件的优先级设置为 10000。serverless-post-function 插件会先执行,再执行 serverless-pre-function 插件。
124+
125+
注意:
126+
127+
- 自定义插件优先级只会影响插件实例绑定的主体,不会影响该插件的所有实例。比如上面的插件配置属于路由 A ,路由 B 上的插件 serverless-post-function 和 serverless-post-function 插件执行顺序不会受到影响,会使用默认优先级。
128+
- 自定义插件优先级不适用于 consumer 上配置的插件的 rewrite 阶段。路由上配置的插件的 rewrite 阶段将会优先运行,然后才会运行 consumer 上除 auth 插件之外的其他插件的 rewrite 阶段。
92129

93130
## 热加载
94131

t/admin/plugins.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ plugins:
265265
}
266266
}
267267
--- response_body eval
268-
qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"skey":\{"type":"string"\}\},"required":\["ikey","skey"\],"type":"object"\},"priority":0,"schema":\{"\$comment":"this is a mark for our injected plugin schema","properties":\{"_meta":\{"properties":\{"error_response":\{"oneOf":\[\{"type":"string"\},\{"type":"object"\}\]\}\},"type":"object"\},"disable":\{"type":"boolean"\},"i":\{"minimum":0,"type":"number"\},"ip":\{"type":"string"\},"port":\{"type":"integer"\},"s":\{"type":"string"\},"t":\{"minItems":1,"type":"array"\}\},"required":\["i"\],"type":"object"\},"version":0.1\}/
268+
qr/\{"metadata_schema":\{"properties":\{"ikey":\{"minimum":0,"type":"number"\},"skey":\{"type":"string"\}\},"required":\["ikey","skey"\],"type":"object"\},"priority":0,"schema":\{"\$comment":"this is a mark for our injected plugin schema","properties":\{"_meta":\{"properties":\{"error_response":\{"oneOf":\[\{"type":"string"\},\{"type":"object"\}\]\},"priority":\{"description":"priority of plugins by customized order","type":"integer"\}\},"type":"object"\},"disable":\{"type":"boolean"\},"i":\{"minimum":0,"type":"number"\},"ip":\{"type":"string"\},"port":\{"type":"integer"\},"s":\{"type":"string"\},"t":\{"minItems":1,"type":"array"\}\},"required":\["i"\],"type":"object"\},"version":0.1\}/
269269
270270
271271
@@ -366,7 +366,7 @@ qr/\{"properties":\{"password":\{"type":"string"\},"username":\{"type":"string"\
366366
}
367367
}
368368
--- response_body
369-
{"priority":1003,"schema":{"$comment":"this is a mark for our injected plugin schema","properties":{"_meta":{"properties":{"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]}},"type":"object"},"burst":{"minimum":0,"type":"integer"},"conn":{"exclusiveMinimum":0,"type":"integer"},"default_conn_delay":{"exclusiveMinimum":0,"type":"number"},"disable":{"type":"boolean"},"key":{"type":"string"},"key_type":{"default":"var","enum":["var","var_combination"],"type":"string"},"only_use_default_delay":{"default":false,"type":"boolean"}},"required":["conn","burst","default_conn_delay","key"],"type":"object"},"version":0.1}
369+
{"priority":1003,"schema":{"$comment":"this is a mark for our injected plugin schema","properties":{"_meta":{"properties":{"error_response":{"oneOf":[{"type":"string"},{"type":"object"}]},"priority":{"description":"priority of plugins by customized order","type":"integer"}},"type":"object"},"burst":{"minimum":0,"type":"integer"},"conn":{"exclusiveMinimum":0,"type":"integer"},"default_conn_delay":{"exclusiveMinimum":0,"type":"number"},"disable":{"type":"boolean"},"key":{"type":"string"},"key_type":{"default":"var","enum":["var","var_combination"],"type":"string"},"only_use_default_delay":{"default":false,"type":"boolean"}},"required":["conn","burst","default_conn_delay","key"],"type":"object"},"version":0.1}
370370
371371
372372

0 commit comments

Comments
 (0)