You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/10/24 07:16:52 UTC

[apisix] branch master updated: perf: allow skip body filter (#8149)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new fa654ed5a perf: allow skip body filter (#8149)
fa654ed5a is described below

commit fa654ed5a0b69de46b461b1e4ce7def0a07d0da7
Author: tzssangglass <tz...@gmail.com>
AuthorDate: Mon Oct 24 15:16:45 2022 +0800

    perf: allow skip body filter (#8149)
---
 apisix/plugins/ai.lua  |  41 ++++--
 t/debug/dynamic-hook.t |  10 ++
 t/plugin/ai2.t         | 377 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 420 insertions(+), 8 deletions(-)

diff --git a/apisix/plugins/ai.lua b/apisix/plugins/ai.lua
index eeb78ca80..699b140b4 100644
--- a/apisix/plugins/ai.lua
+++ b/apisix/plugins/ai.lua
@@ -19,10 +19,11 @@ local apisix          = require("apisix")
 local core            = require("apisix.core")
 local router          = require("apisix.router")
 local event           = require("apisix.core.event")
-local load_balancer   = require("apisix.balancer")
 local balancer        = require("ngx.balancer")
+local ngx             = ngx
 local is_http         = ngx.config.subsystem == "http"
 local enable_keepalive = balancer.enable_keepalive and is_http
+local is_apisix_or, response = pcall(require, "resty.apisix.response")
 local ipairs          = ipairs
 local pcall           = pcall
 local loadstring      = loadstring
@@ -65,7 +66,7 @@ local _M = {
 
 local orig_router_match
 local orig_handle_upstream = apisix.handle_upstream
-local orig_balancer_run = load_balancer.run
+local orig_http_balancer_phase = apisix.http_balancer_phase
 
 local default_keepalive_pool = {}
 
@@ -116,7 +117,21 @@ end
 
 
 local pool_opt
-local function ai_balancer_run(route)
+local function ai_http_balancer_phase()
+    local api_ctx = ngx.ctx.api_ctx
+    if not api_ctx then
+        core.log.error("invalid api_ctx")
+        return core.response.exit(500)
+    end
+
+    if is_apisix_or then
+        local ok, err = response.skip_body_filter_by_lua()
+        if not ok then
+            core.log.error("failed to skip body filter by lua: ", err)
+        end
+    end
+
+    local route = api_ctx.matched_route
     local server = route.value.upstream.nodes[1]
     if enable_keepalive then
         local ok, err = balancer.set_current_peer(server.host, server.port or 80, pool_opt)
@@ -132,6 +147,7 @@ local function ai_balancer_run(route)
     end
 end
 
+
 local function routes_analyze(routes)
     local route_flags = core.table.new(0, 16)
     local route_up_flags = core.table.new(0, 12)
@@ -161,6 +177,8 @@ local function routes_analyze(routes)
                     route_flags["service_id"] = true
                 elseif key == "plugin_config_id" then
                     route_flags["plugin_config_id"] = true
+                elseif key == "script" then
+                    route_flags["script"] = true
                 end
 
                 -- collect upstream flags
@@ -198,10 +216,14 @@ local function routes_analyze(routes)
         end
     end
 
+    local global_rules_flag = router.global_rules and router.global_rules.values
+                              and #router.global_rules.values ~= 0
+
     if route_flags["vars"] or route_flags["filter_fun"]
          or route_flags["remote_addr"]
          or route_flags["service_id"]
-         or route_flags["plugin_config_id"] then
+         or route_flags["plugin_config_id"]
+         or global_rules_flag then
         router.router_http.match = orig_router_match
     else
         core.log.info("use ai plane to match route")
@@ -215,10 +237,12 @@ local function routes_analyze(routes)
     end
 
     if route_flags["service"]
+         or route_flags["script"]
          or route_flags["service_id"]
          or route_flags["upstream_id"]
          or route_flags["enable_websocket"]
          or route_flags["plugins"]
+         or route_flags["plugin_config_id"]
          or route_up_flags["has_domain"]
          or route_up_flags["pass_host"]
          or route_up_flags["scheme"]
@@ -228,13 +252,14 @@ local function routes_analyze(routes)
          or route_up_flags["tls"]
          or route_up_flags["keepalive"]
          or route_up_flags["service_name"]
-         or route_up_flags["more_nodes"] then
+         or route_up_flags["more_nodes"]
+         or global_rules_flag then
         apisix.handle_upstream = orig_handle_upstream
-        load_balancer.run = orig_balancer_run
+        apisix.http_balancer_phase = orig_http_balancer_phase
     else
-        -- replace the upstream module
+        -- replace the upstream and balancer module
         apisix.handle_upstream = ai_upstream
-        load_balancer.run = ai_balancer_run
+        apisix.http_balancer_phase = ai_http_balancer_phase
     end
 end
 
diff --git a/t/debug/dynamic-hook.t b/t/debug/dynamic-hook.t
index 87d4450d5..9832d5dc7 100644
--- a/t/debug/dynamic-hook.t
+++ b/t/debug/dynamic-hook.t
@@ -30,6 +30,11 @@ run_tests();
 __DATA__
 
 === TEST 1: dynamic enable
+# ai module would conflict with the debug module
+--- extra_yaml_config
+plugins:
+    #- ai
+    - example-plugin
 --- debug_config eval: $::debug_config
 --- config
     location /t {
@@ -86,6 +91,11 @@ call require("apisix").http_access_phase() args:{}
 
 
 === TEST 2: dynamic enable by per request and disable after handle request
+# ai module would conflict with the debug module
+--- extra_yaml_config
+plugins:
+    #- ai
+    - example-plugin
 --- debug_config eval: $::debug_config
 --- config
     location /t {
diff --git a/t/plugin/ai2.t b/t/plugin/ai2.t
new file mode 100644
index 000000000..2a54ae97f
--- /dev/null
+++ b/t/plugin/ai2.t
@@ -0,0 +1,377 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX;
+
+my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $version = eval { `$nginx_binary -V 2>&1` };
+
+if ($version !~ m/\/apisix-nginx-module/) {
+    plan(skip_all => "apisix-nginx-module not installed");
+} else {
+    plan('no_plan');
+}
+
+repeat_each(1);
+log_level('info');
+worker_connections(256);
+no_root_location();
+no_shuffle();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    if (!defined $block->extra_init_by_lua) {
+        my $extra_init_by_lua = <<_EOC_;
+            local apisix = require("apisix")
+            apisix.http_header_filter_phase = function ()
+                ngx.header.content_length = 14
+            end
+
+            apisix.http_body_filter_phase = function ()
+                ngx.arg[1] = "do body filter"
+            end
+_EOC_
+
+        $block->set_value("extra_init_by_lua", $extra_init_by_lua);
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: enable skip body filter
+--- extra_init_by_lua
+    local apisix = require("apisix")
+    apisix.http_header_filter_phase = function ()
+        ngx.header.content_length = nil
+    end
+
+    apisix.http_body_filter_phase = function ()
+        ngx.arg[1] = "do body filter"
+    end
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "methods": ["GET"],
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri)
+            assert(res.status == 200)
+            if not res then
+                ngx.log(ngx.ERR, err)
+                return
+            end
+            ngx.print(res.body)
+        }
+    }
+--- response_body
+hello world
+
+
+
+=== TEST 2: route with plugin_config_id, disable skip body filter
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_configs/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "serverless-pre-function": {
+                            "phase": "before_proxy",
+                            "functions" : ["return function(conf, ctx) ngx.log(ngx.WARN, \"run before_proxy phase balancer_ip : \", ctx.balancer_ip) end"]
+                        }
+                    }
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugin_config_id": "1",
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri)
+            assert(res.status == 200)
+            if not res then
+                ngx.log(ngx.ERR, err)
+                return
+            end
+            ngx.say(res.body)
+        }
+    }
+--- response_body
+do body filter
+--- error_log
+run before_proxy phase balancer_ip : 127.0.0.1
+--- no_error_log
+enable sample upstream
+
+
+
+=== TEST 3: route with plugins, disable skip body filter
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/consumers',
+                ngx.HTTP_PUT,
+                [[{
+                    "username": "new_consumer",
+                    "plugins": {
+                        "key-auth": {
+                            "key": "auth-jack"
+                        },
+                        "serverless-pre-function": {
+                            "phase": "before_proxy",
+                            "functions" : ["return function(conf, ctx) ngx.log(ngx.WARN, \"run before_proxy phase balancer_ip : \", ctx.balancer_ip) end"]
+                        }
+                    }
+                }]]
+            )
+            ngx.sleep(0.5)
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "key-auth": {}
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
+            local httpc = http.new()
+            local headers = {
+                ["apikey"] = "auth-jack"
+            }
+            local res, err = httpc:request_uri(uri, {headers = headers})
+            assert(res.status == 200)
+            if not res then
+                ngx.log(ngx.ERR, err)
+                return
+            end
+            ngx.say(res.body)
+        }
+    }
+--- response_body
+do body filter
+--- error_log
+run before_proxy phase balancer_ip : 127.0.0.1
+--- no_error_log
+enable sample upstream
+
+
+
+=== TEST 4: one of route has plugins, disable skip body filter
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "serverless-pre-function": {
+                            "phase": "before_proxy",
+                            "functions" : ["return function(conf, ctx) ngx.log(ngx.WARN, \"run before_proxy phase balancer_ip : \", ctx.balancer_ip) end"]
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local code, body = t('/apisix/admin/routes/2',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello1"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello1"
+            local httpc = http.new()
+            local headers = {
+                ["apikey"] = "auth-jack"
+            }
+            local res, err = httpc:request_uri(uri, {headers = headers})
+            assert(res.status == 200)
+            if not res then
+                ngx.log(ngx.ERR, err)
+                return
+            end
+            ngx.say(res.body)
+        }
+    }
+--- response_body
+do body filter
+--- no_error_log
+enable sample upstream
+
+
+
+=== TEST 5: exist global_rules, disable skip body filter
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/global_rules/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "serverless-pre-function": {
+                            "phase": "before_proxy",
+                            "functions" : ["return function(conf, ctx) ngx.log(ngx.WARN, \"run before_proxy phase balancer_ip : \", ctx.balancer_ip) end"]
+                        }
+                    }
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "methods": ["GET"],
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/hello"
+                }]]
+            )
+            if code >= 300 then
+                ngx.status = code
+                ngx.say(body)
+                return
+            end
+            ngx.sleep(0.5)
+            local http = require "resty.http"
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello"
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri)
+            assert(res.status == 200)
+            if not res then
+                ngx.log(ngx.ERR, err)
+                return
+            end
+            ngx.say(res.body)
+        }
+    }
+--- response_body
+do body filter
+--- error_log
+run before_proxy phase balancer_ip : 127.0.0.1
+--- no_error_log
+enable sample upstream