From cfddd8b6489f1390de71d6853390ab7f1d841297 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 19 Jun 2023 11:39:55 +0800 Subject: [PATCH 01/22] increment route update for radixtree host uri, radixtree uri and radixtree uri with parameter --- apisix/core/config_etcd.lua | 9 +- apisix/http/route.lua | 1 + apisix/http/router/radixtree_host_uri.lua | 267 +++++++++++++---- apisix/http/router/radixtree_uri.lua | 26 +- .../router/radixtree_uri_with_parameter.lua | 26 +- apisix/router.lua | 272 +++++++++++++++++- 6 files changed, 480 insertions(+), 121 deletions(-) diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua index ecb76270452d..611135061462 100644 --- a/apisix/core/config_etcd.lua +++ b/apisix/core/config_etcd.lua @@ -499,7 +499,7 @@ local function load_full_data(self, dir_res, headers) item.clean_handlers = {} if self.filter then - self.filter(item) + self.filter(item, 1, self) end end @@ -551,7 +551,7 @@ local function load_full_data(self, dir_res, headers) item.clean_handlers = {} if self.filter then - self.filter(item) + self.filter(item, #values, self) end end @@ -695,9 +695,10 @@ local function sync_data(self) return false end + local pre_val local pre_index = self.values_hash[key] if pre_index then - local pre_val = self.values[pre_index] + pre_val = self.values[pre_index] if pre_val then config_util.fire_all_clean_handlers(pre_val) end @@ -755,7 +756,7 @@ local function sync_data(self) -- /plugins' filter need to known self.values when it is called -- so the filter should be called after self.values set. if self.filter then - self.filter(res) + self.filter(res, pre_val, self) end self.conf_version = self.conf_version + 1 diff --git a/apisix/http/route.lua b/apisix/http/route.lua index d475646b56c6..2feab7888638 100644 --- a/apisix/http/route.lua +++ b/apisix/http/route.lua @@ -73,6 +73,7 @@ function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter) core.log.info("insert uri route: ", core.json.delay_encode(route.value, true)) core.table.insert(uri_routes, { + id = route.value.id, paths = route.value.uris or route.value.uri, methods = route.value.methods, priority = route.value.priority, diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 532576e53d4a..369a54dbac00 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -27,20 +27,28 @@ local loadstring = loadstring local pairs = pairs local cached_router_version local cached_service_version -local host_router -local only_uri_router +local base = require("resty.core.base") +local clear_tab = base.clear_tab local _M = {version = 0.1} -local function push_host_router(route, host_routes, only_uri_routes) +local function tab_cpy(t1, t2) + t1 = {} + for k, v in pairs(t2) do + t1[k] = v + end +end + + +function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) if type(route) ~= "table" then return end local filter_fun, err - if route.value.filter_func then + if route.value and route.value.filter_func then filter_fun, err = loadstring( "return " .. route.value.filter_func, "router#" .. route.value.id) @@ -53,67 +61,213 @@ local function push_host_router(route, host_routes, only_uri_routes) filter_fun = filter_fun() end - local hosts = route.value.hosts - if not hosts then - if route.value.host then - hosts = {route.value.host} - elseif route.value.service_id then - local service = service_fetch(route.value.service_id) - if not service then - core.log.error("failed to fetch service configuration by ", - "id: ", route.value.service_id) - -- we keep the behavior that missing service won't affect the route matching - else - hosts = service.value.hosts + local radixtree_route, pre_radixtree_route = {}, {} + local hosts + if route and route.value then + hosts = route.value.hosts + if not hosts then + if route.value.host then + hosts = {route.value.host} + elseif route.value.service_id then + local service = service_fetch(route.value.service_id) + if not service then + core.log.error("failed to fetch service configuration by ", + "id: ", route.value.service_id) + -- we keep the behavior that missing service won't affect the route matching + else + hosts = service.value.hosts + end + end + end + + radixtree_route = { + id = route.value.id, + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, + remote_addrs = route.value.remote_addrs + or route.value.remote_addr, + vars = route.value.vars, + filter_fun = filter_fun, + handler = function (api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = route + api_ctx.curr_req_matched = match_opts.matched + api_ctx.real_curr_req_matched_path = match_opts.matched._path + end + } + + if rdx_rt ~= nil then + for k, v in pairs(radixtree_route) do + rdx_rt[k] = v end end end - local radixtree_route = { - paths = route.value.uris or route.value.uri, - methods = route.value.methods, - priority = route.value.priority, - remote_addrs = route.value.remote_addrs - or route.value.remote_addr, - vars = route.value.vars, - filter_fun = filter_fun, - handler = function (api_ctx, match_opts) - api_ctx.matched_params = nil - api_ctx.matched_route = route - api_ctx.curr_req_matched = match_opts.matched - api_ctx.real_curr_req_matched_path = match_opts.matched._path - end - } - - if hosts == nil then + if hosts == nil and all_hosts == nil then core.table.insert(only_uri_routes, radixtree_route) return end - for i, host in ipairs(hosts) do - local host_rev = host:reverse() - if not host_routes[host_rev] then - host_routes[host_rev] = {radixtree_route} + local pre_hosts + if pre_route and pre_route.value then + pre_hosts = pre_route.value.hosts + if not pre_hosts then + if pre_route.value.host then + pre_hosts = {pre_route.value.host} + elseif pre_route.value.service_id then + local service = service_fetch(pre_route.value.service_id) + if not service then + core.log.error("failed to fetch service configuration by ", + "id: ", pre_route.value.service_id) + -- we keep the behavior that missing service won't affect the route matching + else + pre_hosts = service.value.hosts + end + end + end + + pre_radixtree_route = { + id = pre_route.value.id, + paths = pre_route.value.uris or pre_route.value.uri, + methods = pre_route.value.methods, + priority = pre_route.value.priority, + remote_addrs = pre_route.value.remote_addrs + or pre_route.value.remote_addr, + vars = pre_route.value.vars, + filter_fun = filter_fun, + handler = function (api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = pre_route + api_ctx.curr_req_matched = match_opts.matched + api_ctx.real_curr_req_matched_path = match_opts.matched._path + end + } + + if pre_rdx_rt ~= nil then + for k, v in pairs(pre_radixtree_route) do + pre_rdx_rt[k] = v + end + end + end + + if all_hosts ~= nil then + all_hosts["host"] = hosts + all_hosts["pre_host"] = pre_hosts + end + + local pre_t = {} + if pre_hosts then + for i, h in ipairs(pre_hosts) do + local rev_h = h:reverse() + pre_t[rev_h] = 1 + end + end + + local t = {} + if hosts then + for i, h in ipairs(hosts) do + local rev_h = h:reverse() + t[rev_h] = 1 + end + end + + local comm = {} + for k, v in pairs(pre_t) do + if t[k] ~= nil then + tab_insert(comm, k) + pre_t[k] = nil + t[k] = nil + end + end + + for _, j in ipairs(comm) do + local routes = host_routes[j] + if routes == nil then + core.log.error("no routes array for reverse host in the map.", j) + return + end + + local found = false + for i, r in ipairs(routes) do + if r.id == radixtree_route.id then + routes[i] = radixtree_route + found = true + if op then + table.insert(op["upd"], j) + end + break + end + end + + if not found then + core.log.error("cannot find the route in common host's table.", j, radixtree_route.id) + return + end + end + + for k, v in pairs(pre_t) do + local routes = host_routes[k] + if routes == nil then + core.log.error("no routes array for reverse host in the map.", k) + return + end + + local found = false + for i, r in ipairs(routes) do + if r.id == pre_radixtree_route.id then + table.remove(routes, i) + found = true + break + end + end + + if not found then + core.log.error("cannot find the route in previous host's table.", k, pre_radixtree_route.id) + return + end + + if #routes == 0 then + host_routes[k] = nil + if op then + table.insert(op["del"], k) + end + else + if op then + table.insert(op["upd"], k) + end + end + end + + for k, v in pairs(t) do + local routes = host_routes[k] + if routes == nil then + host_routes[k] = {radixtree_route} + if op then + table.insert(op["add"], k) + end else - tab_insert(host_routes[host_rev], radixtree_route) + table.insert(routes, radixtree_route) + if op then + table.insert(op["upd"], k) + end end end end -local function create_radixtree_router(routes) +function _M.create_radixtree_router(routes) local host_routes = {} local only_uri_routes = {} - host_router = nil routes = routes or {} for _, route in ipairs(routes) do local status = core.table.try_read_attr(route, "value", "status") -- check the status if not status or status == 1 then - push_host_router(route, host_routes, only_uri_routes) - end - end + _M.push_host_router(route, host_routes, only_uri_routes) + end + end -- create router: host_router local host_router_routes = {} @@ -121,45 +275,42 @@ local function create_radixtree_router(routes) local sub_router = router.new(routes) core.table.insert(host_router_routes, { + id = 1, paths = host_rev, filter_fun = function(vars, opts, ...) return sub_router:dispatch(vars.uri, opts, ...) end, handler = function (api_ctx, match_opts) api_ctx.real_curr_req_matched_host = match_opts.matched._path - end - }) - end + end + }) + end + + _M.host_routes = host_routes event.push(event.CONST.BUILD_ROUTER, routes) if #host_router_routes > 0 then - host_router = router.new(host_router_routes) + _M.host_router = router.new(host_router_routes) end -- create router: only_uri_router - only_uri_router = router.new(only_uri_routes) + _M.only_uri_router = router.new(only_uri_routes) + return true end local match_opts = {} function _M.match(api_ctx) - local user_routes = _M.user_routes - local _, service_version = get_services() - if not cached_router_version or cached_router_version ~= user_routes.conf_version - or not cached_service_version or cached_service_version ~= service_version - then - create_radixtree_router(user_routes.values) - cached_router_version = user_routes.conf_version - cached_service_version = service_version - end - return _M.matching(api_ctx) end function _M.matching(api_ctx) + local host_router = _M.host_router + local only_uri_router = _M.only_uri_router + core.log.info("route match mode: radixtree_host_uri") core.table.clear(match_opts) diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 6e546364ac14..4c3867c4dff6 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -17,43 +17,21 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") -local get_services = require("apisix.http.service").services -local cached_router_version -local cached_service_version +local router = require("apisix.router") local _M = {version = 0.2} - local uri_routes = {} - local uri_router local match_opts = {} function _M.match(api_ctx) - local user_routes = _M.user_routes - local _, service_version = get_services() - if not cached_router_version or cached_router_version ~= user_routes.conf_version - or not cached_service_version or cached_service_version ~= service_version - then - uri_router = base_router.create_radixtree_uri_router(user_routes.values, - uri_routes, false) - cached_router_version = user_routes.conf_version - cached_service_version = service_version - end - - if not uri_router then - core.log.error("failed to fetch valid `uri` router: ") - return true - end - return _M.matching(api_ctx) end function _M.matching(api_ctx) core.log.info("route match mode: radixtree_uri") - - return base_router.match_uri(uri_router, match_opts, api_ctx) + return base_router.match_uri(router.uri_router, match_opts, api_ctx) end - return _M diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index 4bf7f3ebee5f..e36008c8bbbd 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -17,43 +17,21 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") -local get_services = require("apisix.http.service").services -local cached_router_version -local cached_service_version +local router = require("apisix.router") local _M = {} - local uri_routes = {} - local uri_router local match_opts = {} function _M.match(api_ctx) - local user_routes = _M.user_routes - local _, service_version = get_services() - if not cached_router_version or cached_router_version ~= user_routes.conf_version - or not cached_service_version or cached_service_version ~= service_version - then - uri_router = base_router.create_radixtree_uri_router(user_routes.values, - uri_routes, true) - cached_router_version = user_routes.conf_version - cached_service_version = service_version - end - - if not uri_router then - core.log.error("failed to fetch valid `uri_with_parameter` router: ") - return true - end - return _M.matching(api_ctx) end function _M.matching(api_ctx) core.log.info("route match mode: radixtree_uri_with_parameter") - - return base_router.match_uri(uri_router, match_opts, api_ctx) + return base_router.match_uri(router.uri_router, match_opts, api_ctx) end - return _M diff --git a/apisix/router.lua b/apisix/router.lua index 2fd14917c299..ec262e74eeba 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -22,31 +22,281 @@ local plugin_checker = require("apisix.plugin").plugin_checker local str_lower = string.lower local error = error local ipairs = ipairs +local sub_str = string.sub +local table = require("apisix.core.table") +local json = require("apisix.core.json") +local router_util = require("apisix.utils.router") +local tab_insert = table.insert +local event = require("apisix.core.event") local _M = {version = 0.3} +local function empty_func() end +local routes_obj, first_route -local function filter(route) +local function filter(route, pre_route_or_size, obj) route.orig_modifiedIndex = route.modifiedIndex route.update_count = 0 route.has_domain = false - if not route.value then - return + if route.value then + if route.value.host then + route.value.host = str_lower(route.value.host) + elseif route.value.hosts then + for i, v in ipairs(route.value.hosts) do + route.value.hosts[i] = str_lower(v) + end + end + + apisix_upstream.filter_upstream(route.value.upstream, route) end - if route.value.host then - route.value.host = str_lower(route.value.host) - elseif route.value.hosts then - for i, v in ipairs(route.value.hosts) do - route.value.hosts[i] = str_lower(v) + core.log.info("filter route: ", core.json.delay_encode(route, true)) + + --load_full_data()'s filter() goes here. create radixtree while etcd compacts + local conf = core.config.local_conf() + if conf.apisix.router.http == "radixtree_uri" or conf.apisix.router.http == "radixtree_uri_with_parameter" then + local router_opts + local with_parameter = false + if conf.apisix.router.http == "radixtree_uri" then + router_opts = { + no_param_match = true + } + else + with_parameter = true + router_opts = { + no_param_match = false + } end - end + + if type(pre_route_or_size) == "number" then + if pre_route_or_size == #obj.values then + routes_obj = obj + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + local uri_routes = {} + core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) + local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) + if not uri_router then + core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) + return + end - apisix_upstream.filter_upstream(route.value.upstream, route) + _M.uri_router = uri_router + end - core.log.info("filter route: ", core.json.delay_encode(route, true)) + return + end + + if not first_route then + routes_obj = obj + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + local uri_routes = {} + core.log.notice("create radixtree uri for the first route income.") + local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) + if not uri_router then + core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) + return + end + + _M.uri_router = uri_router + first_route = true + return + end + + --only sync_data()'s filter() goes here + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + + local router_module = require("apisix.router") + local radixtree_obj = router_module.uri_router + + local cur_route + if route.value then + local status = table.try_read_attr(route, "value", "status") + if status and status == 0 then + return + end + + local filter_fun, err + if route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id + ) + if not filter_fun then + core.log.error("failed to load filter function: ", err, " route id", route.value.id) + return + end + + filter_fun = filter_fun() + end + + cur_route = { + id = route.value.id, + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, + hosts = route.value.hosts or route.value.host, + remote_addrs = route.value.remote_addrs or route.value.remote_addr, + vars = route.value.vars, + filter_fun = filter_fun, + handler = function(api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = route + api_ctx.curr_req_matched = match_opts.matched + end + } + end + + local err + if pre_route_or_size then + local last_route = { + id = pre_route_or_size.value.id, + paths = pre_route_or_size.value.uris or pre_route_or_size.value.uri, + methods = pre_route_or_size.value.methods, + priority = pre_route_or_size.value.priority, + hosts = pre_route_or_size.value.hosts or pre_route_or_size.value.host, + remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, + vars = pre_route_or_size.value.vars + } + + if route.value then + --update route + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + err = radixtree_obj:update_route(last_route, cur_route, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(route), err) + return + end + else + --delete route + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + err = radixtree_obj:delete_route(last_route, router_opts) + if err ~= nil then + core.log.error("delete a route into radixtree failed.", json.encode(route), err) + return + end + end + elseif route.value then + --create route + core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) + err = radixtree_obj:add_route(cur_route, router_opts) + if err ~= nil then + core.log.error("add routes into radixtree failed.", json.encode(route), err) + return + end + else + core.log.error("invalid operation type for a route.", route.key) + return + end + elseif conf.apisix.router.http == "radixtree_host_uri" then + local router_opts = { + no_param_match = true + } + + local host_uri = require("apisix.http.router.radixtree_host_uri") + if type(pre_route_or_size) == "number" then + if pre_route_or_size == #obj.values then + routes_obj = obj + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) + host_uri.create_radixtree_router(routes_obj.values) + end + + return + end + + if not first_route then + routes_obj = obj + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + core.log.notice("create radixtree uri for the first route income.") + host_uri.create_radixtree_router(routes_obj.values) + first_route = true + return + end + + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + + local only_uri_routes = {} + local route_opt, pre_route_opt = {}, {} + local all_hosts = {} + local hosts, pre_hosts = nil, nil + local rdx_r = {} + local pre_rdx_r = {} + local op = {add={}, upd={}, del={}} + + + local status = table.try_read_attr(route, "value", "status") + if status and status == 0 then + return + end + + -- to be confirm??? assign variable of other module. + host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) + + hosts = all_hosts["host"] + if hosts ~= nil then + for _, h in ipairs(hosts) do + local host_rev = h:reverse() + local routes = host_uri.host_routes[host_rev] + local sub_router = router_util.new(routes) + route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = empty_func, + } + end + end + + pre_hosts = all_hosts["pre_host"] + if pre_hosts ~= nil then + for _, h in ipairs(pre_hosts) do + local host_rev = h:reverse() + pre_route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = empty_func, + handler = empty_func, + } + end + end + + for k, v in pairs(op) do + if k == "add" then + for _, j in ipairs(v) do + core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + host_uri.host_router:add_route(r_opt, router_opts) + end + elseif k == "upd" then + for _, j in ipairs(v) do + core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + host_uri.host_router:update_route(r_opt, r_opt, router_opts) + end + elseif k == "del" then + for _, j in ipairs(v) do + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local pre_r_opt = pre_route_opt[j] + host_uri.host_router:delete_route(pre_r_opt, router_opts) + end + end + end + + if (route.value and not hosts) and (not pre_route_or_size or pre_hosts) then + core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) + host_uri.only_uri_router:add_route(rdx_r, router_opts) + elseif (route.value and not hosts) and (pre_route_or_size and not pre_hosts) then + core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) + host_uri.only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) + elseif (pre_route_or_size and not pre_hosts) and (not route.value or hosts) then + core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(pre_route_or_size)) + host_uri.only_uri_router:delete_route(pre_rdx_r, router_opts) + end + end end From 3a18a38faa18f346cdbebf965fee24476c89da06 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 26 Jun 2023 16:31:20 +0800 Subject: [PATCH 02/22] erase unused function and remove useless space --- apisix/http/router/radixtree_host_uri.lua | 20 ++++++-------------- apisix/router.lua | 6 +++--- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 369a54dbac00..c66b27d5f494 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -34,14 +34,6 @@ local clear_tab = base.clear_tab local _M = {version = 0.1} -local function tab_cpy(t1, t2) - t1 = {} - for k, v in pairs(t2) do - t1[k] = v - end -end - - function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) if type(route) ~= "table" then return @@ -266,8 +258,8 @@ function _M.create_radixtree_router(routes) -- check the status if not status or status == 1 then _M.push_host_router(route, host_routes, only_uri_routes) - end - end + end + end -- create router: host_router local host_router_routes = {} @@ -282,9 +274,9 @@ function _M.create_radixtree_router(routes) end, handler = function (api_ctx, match_opts) api_ctx.real_curr_req_matched_host = match_opts.matched._path - end - }) - end + end + }) + end _M.host_routes = host_routes @@ -296,7 +288,7 @@ function _M.create_radixtree_router(routes) -- create router: only_uri_router _M.only_uri_router = router.new(only_uri_routes) - + return true end diff --git a/apisix/router.lua b/apisix/router.lua index ec262e74eeba..78fea3f552c4 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -69,7 +69,7 @@ local function filter(route, pre_route_or_size, obj) no_param_match = false } end - + if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then routes_obj = obj @@ -159,7 +159,7 @@ local function filter(route, pre_route_or_size, obj) remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, vars = pre_route_or_size.value.vars } - + if route.value then --update route core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) @@ -202,7 +202,7 @@ local function filter(route, pre_route_or_size, obj) core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) host_uri.create_radixtree_router(routes_obj.values) end - + return end From 180e2a806c006a60adddd2098f4d6eeac03a52b4 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 26 Jun 2023 16:36:07 +0800 Subject: [PATCH 03/22] upgrade version of radixtree --- rockspec/apisix-master-0.rockspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index ab3bee75c877..a383cbb4504b 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -45,7 +45,7 @@ dependencies = { "lua-resty-cookie = 0.1.0", "lua-resty-session = 3.10", "opentracing-openresty = 0.1", - "lua-resty-radixtree = 2.8.2", + "lua-resty-radixtree = 2.8.3", "lua-protobuf = 0.4.1", "lua-resty-openidc = 1.7.5", "luafilesystem = 1.7.0-2", From 2ccd28d89b7a1ffec954a37a4cb77f8dcc654a43 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 26 Jun 2023 18:03:55 +0800 Subject: [PATCH 04/22] avoid incremental update a route while not base on config_etcd source. --- apisix/router.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apisix/router.lua b/apisix/router.lua index 78fea3f552c4..b8d6f2ea06c2 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -53,6 +53,10 @@ local function filter(route, pre_route_or_size, obj) end core.log.info("filter route: ", core.json.delay_encode(route, true)) + --filter route from other config source + if not obj then + return + end --load_full_data()'s filter() goes here. create radixtree while etcd compacts local conf = core.config.local_conf() @@ -83,6 +87,9 @@ local function filter(route, pre_route_or_size, obj) end _M.uri_router = uri_router + if not first_route then + first_route = true + end end return @@ -201,6 +208,9 @@ local function filter(route, pre_route_or_size, obj) event.push(event.CONST.BUILD_ROUTER, routes_obj.values) core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) host_uri.create_radixtree_router(routes_obj.values) + if not first_route then + first_route = true + end end return @@ -231,7 +241,6 @@ local function filter(route, pre_route_or_size, obj) return end - -- to be confirm??? assign variable of other module. host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) hosts = all_hosts["host"] From 02021f8704fc1b126cc5562d1bcf342b4adce52b Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 27 Jun 2023 14:32:43 +0800 Subject: [PATCH 05/22] be compitable to ai plugin and other config source --- apisix/router.lua | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/apisix/router.lua b/apisix/router.lua index b8d6f2ea06c2..cac337118c9f 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -59,6 +59,7 @@ local function filter(route, pre_route_or_size, obj) end --load_full_data()'s filter() goes here. create radixtree while etcd compacts + local router_module = require("apisix.router") local conf = core.config.local_conf() if conf.apisix.router.http == "radixtree_uri" or conf.apisix.router.http == "radixtree_uri_with_parameter" then local router_opts @@ -77,7 +78,10 @@ local function filter(route, pre_route_or_size, obj) if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then routes_obj = obj - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end + local uri_routes = {} core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) @@ -97,7 +101,10 @@ local function filter(route, pre_route_or_size, obj) if not first_route then routes_obj = obj - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end + local uri_routes = {} core.log.notice("create radixtree uri for the first route income.") local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) @@ -112,11 +119,11 @@ local function filter(route, pre_route_or_size, obj) end --only sync_data()'s filter() goes here - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end - local router_module = require("apisix.router") local radixtree_obj = router_module.uri_router - local cur_route if route.value then local status = table.try_read_attr(route, "value", "status") @@ -205,7 +212,10 @@ local function filter(route, pre_route_or_size, obj) if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then routes_obj = obj - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end + core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) host_uri.create_radixtree_router(routes_obj.values) if not first_route then @@ -218,14 +228,19 @@ local function filter(route, pre_route_or_size, obj) if not first_route then routes_obj = obj - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end + core.log.notice("create radixtree uri for the first route income.") host_uri.create_radixtree_router(routes_obj.values) first_route = true return end - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + end local only_uri_routes = {} local route_opt, pre_route_opt = {}, {} From 6db2b6da6b3e2ffe07e8b3c2615166128153a7c3 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 27 Jun 2023 15:22:48 +0800 Subject: [PATCH 06/22] inform ai plugin after init config_etcd object. --- apisix/router.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/apisix/router.lua b/apisix/router.lua index cac337118c9f..8dfc3dc0e806 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -359,6 +359,7 @@ function _M.http_init_worker() attach_http_router_common_methods(router_http) router_http.init_worker(filter) _M.router_http = router_http + event.push(event.CONST.BUILD_ROUTER, router_http.user_routes.values) local router_ssl = require("apisix.ssl.router." .. router_ssl_name) router_ssl.init_worker() From 345a190b81ad5e4725e91cdb967460f54b68507a Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 27 Jun 2023 15:43:35 +0800 Subject: [PATCH 07/22] remove redundant even.push() --- apisix/http/route.lua | 6 +++++- apisix/http/router/radixtree_host_uri.lua | 5 ++++- apisix/router.lua | 16 ---------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apisix/http/route.lua b/apisix/http/route.lua index 2feab7888638..a489b2d1a794 100644 --- a/apisix/http/route.lua +++ b/apisix/http/route.lua @@ -93,7 +93,11 @@ function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter) end end - event.push(event.CONST.BUILD_ROUTER, routes) + local router_module = require("apisix.router") + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes) + end + core.log.info("route items: ", core.json.delay_encode(uri_routes, true)) if with_parameter then diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index c66b27d5f494..b4d5ce66f925 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -280,7 +280,10 @@ function _M.create_radixtree_router(routes) _M.host_routes = host_routes - event.push(event.CONST.BUILD_ROUTER, routes) + local router_module = require("apisix.router") + if router_module.router_http then + event.push(event.CONST.BUILD_ROUTER, routes) + end if #host_router_routes > 0 then _M.host_router = router.new(host_router_routes) diff --git a/apisix/router.lua b/apisix/router.lua index 8dfc3dc0e806..a96586693c3c 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -78,10 +78,6 @@ local function filter(route, pre_route_or_size, obj) if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then routes_obj = obj - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - end - local uri_routes = {} core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) @@ -101,10 +97,6 @@ local function filter(route, pre_route_or_size, obj) if not first_route then routes_obj = obj - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - end - local uri_routes = {} core.log.notice("create radixtree uri for the first route income.") local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) @@ -212,10 +204,6 @@ local function filter(route, pre_route_or_size, obj) if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then routes_obj = obj - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - end - core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) host_uri.create_radixtree_router(routes_obj.values) if not first_route then @@ -228,10 +216,6 @@ local function filter(route, pre_route_or_size, obj) if not first_route then routes_obj = obj - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - end - core.log.notice("create radixtree uri for the first route income.") host_uri.create_radixtree_router(routes_obj.values) first_route = true From 478ecaf20c5a23376bbd3c0881fedc4b8da506d8 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 27 Jun 2023 16:06:03 +0800 Subject: [PATCH 08/22] fix update route from only uri to host uri while host_uri mode --- apisix/router.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apisix/router.lua b/apisix/router.lua index a96586693c3c..537297c63364 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -214,7 +214,7 @@ local function filter(route, pre_route_or_size, obj) return end - if not first_route then + if not first_route or (not host_uri.host_router or not host_uri.only_uri_router) then routes_obj = obj core.log.notice("create radixtree uri for the first route income.") host_uri.create_radixtree_router(routes_obj.values) From 36a7a649edcc29e52455ad37dca251c7ce8daad0 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 3 Jul 2023 15:57:46 +0800 Subject: [PATCH 09/22] move filter() content to radixtree_uri --- a | 129 +++ apisix/http/router/radixtree_host_uri.lua | 87 +- apisix/http/router/radixtree_uri.lua | 129 ++- .../router/radixtree_uri_with_parameter.lua | 129 ++- apisix/router.lua | 304 +------ increment_update_route.patch | 806 ++++++++++++++++++ 6 files changed, 1302 insertions(+), 282 deletions(-) create mode 100644 a create mode 100644 increment_update_route.patch diff --git a/a b/a new file mode 100644 index 000000000000..fec84e79a4a8 --- /dev/null +++ b/a @@ -0,0 +1,129 @@ +diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua +index 369a54db..c66b27d5 100644 +--- a/apisix/http/router/radixtree_host_uri.lua ++++ b/apisix/http/router/radixtree_host_uri.lua +@@ -34,14 +34,6 @@ local clear_tab = base.clear_tab + local _M = {version = 0.1} + + +-local function tab_cpy(t1, t2) +- t1 = {} +- for k, v in pairs(t2) do +- t1[k] = v +- end +-end +- +- + function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) + if type(route) ~= "table" then + return +@@ -266,8 +258,8 @@ function _M.create_radixtree_router(routes) + -- check the status + if not status or status == 1 then + _M.push_host_router(route, host_routes, only_uri_routes) +- end +- end ++ end ++ end + + -- create router: host_router + local host_router_routes = {} +@@ -282,9 +274,9 @@ function _M.create_radixtree_router(routes) + end, + handler = function (api_ctx, match_opts) + api_ctx.real_curr_req_matched_host = match_opts.matched._path +- end +- }) +- end ++ end ++ }) ++ end + + _M.host_routes = host_routes + +@@ -296,7 +288,7 @@ function _M.create_radixtree_router(routes) + + -- create router: only_uri_router + _M.only_uri_router = router.new(only_uri_routes) +- ++ + return true + end + +diff --git a/apisix/router.lua b/apisix/router.lua +index ec262e74..b8d6f2ea 100644 +--- a/apisix/router.lua ++++ b/apisix/router.lua +@@ -53,6 +53,10 @@ local function filter(route, pre_route_or_size, obj) + end + + core.log.info("filter route: ", core.json.delay_encode(route, true)) ++ --filter route from other config source ++ if not obj then ++ return ++ end + + --load_full_data()'s filter() goes here. create radixtree while etcd compacts + local conf = core.config.local_conf() +@@ -69,7 +73,7 @@ local function filter(route, pre_route_or_size, obj) + no_param_match = false + } + end +- ++ + if type(pre_route_or_size) == "number" then + if pre_route_or_size == #obj.values then + routes_obj = obj +@@ -83,6 +87,9 @@ local function filter(route, pre_route_or_size, obj) + end + + _M.uri_router = uri_router ++ if not first_route then ++ first_route = true ++ end + end + + return +@@ -159,7 +166,7 @@ local function filter(route, pre_route_or_size, obj) + remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, + vars = pre_route_or_size.value.vars + } +- ++ + if route.value then + --update route + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) +@@ -201,8 +208,11 @@ local function filter(route, pre_route_or_size, obj) + event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) + host_uri.create_radixtree_router(routes_obj.values) ++ if not first_route then ++ first_route = true ++ end + end +- ++ + return + end + +@@ -231,7 +241,6 @@ local function filter(route, pre_route_or_size, obj) + return + end + +- -- to be confirm??? assign variable of other module. + host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) + + hosts = all_hosts["host"] +diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec +index ab3bee75..a383cbb4 100644 +--- a/rockspec/apisix-master-0.rockspec ++++ b/rockspec/apisix-master-0.rockspec +@@ -45,7 +45,7 @@ dependencies = { + "lua-resty-cookie = 0.1.0", + "lua-resty-session = 3.10", + "opentracing-openresty = 0.1", +- "lua-resty-radixtree = 2.8.2", ++ "lua-resty-radixtree = 2.8.3", + "lua-protobuf = 0.4.1", + "lua-resty-openidc = 1.7.5", + "luafilesystem = 1.7.0-2", diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index b4d5ce66f925..0f0c90af71c6 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -20,6 +20,7 @@ local core = require("apisix.core") local event = require("apisix.core.event") local get_services = require("apisix.http.service").services local service_fetch = require("apisix.http.service").get +local apisix_router = require("apisix/router") local ipairs = ipairs local type = type local tab_insert = table.insert @@ -27,14 +28,14 @@ local loadstring = loadstring local pairs = pairs local cached_router_version local cached_service_version -local base = require("resty.core.base") -local clear_tab = base.clear_tab +local host_router +local only_uri_router local _M = {version = 0.1} -function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) +local function push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) if type(route) ~= "table" then return end @@ -248,16 +249,17 @@ function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, end -function _M.create_radixtree_router(routes) +local function create_radixtree_router(routes) local host_routes = {} local only_uri_routes = {} + host_router = nil routes = routes or {} for _, route in ipairs(routes) do local status = core.table.try_read_attr(route, "value", "status") -- check the status if not status or status == 1 then - _M.push_host_router(route, host_routes, only_uri_routes) + push_host_router(route, host_routes, only_uri_routes) end end @@ -267,7 +269,6 @@ function _M.create_radixtree_router(routes) local sub_router = router.new(routes) core.table.insert(host_router_routes, { - id = 1, paths = host_rev, filter_fun = function(vars, opts, ...) return sub_router:dispatch(vars.uri, opts, ...) @@ -278,34 +279,84 @@ function _M.create_radixtree_router(routes) }) end - _M.host_routes = host_routes - - local router_module = require("apisix.router") - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes) - end + event.push(event.CONST.BUILD_ROUTER, routes) if #host_router_routes > 0 then - _M.host_router = router.new(host_router_routes) + host_router = router.new(host_router_routes) end -- create router: only_uri_router - _M.only_uri_router = router.new(only_uri_routes) - + only_uri_router = router.new(only_uri_routes) return true end +local function incremental_operate_radixtree(routes) + if apisix_router.need_create_radixtree then + core.log.notice("create radixtree uri after load_full_data.", #routes) + create_radixtree_router(routes) + apisix_router.need_create_radixtree = false + return + end + + local sync_tb = apisix_router.sync_tb + local op, cur_rt, lst_rt, err + local router_opts = { + no_param_match = true + } + for k, v in pairs(sync_tb) do + op = sync_tb[k]["op"] + cur_rt = sync_tb[k]["cur_route"] + lst_rt = sync_tb[k]["last_route"] + + if op == "update" then + core.log.notice("update routes watched from etcd into radixtree.", json.encode(cur_rt)) + err = uri_router:update_route(lst_rt, cur_rt, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(cur_rt), err) + return + end + elseif op == "create" then + core.log.notice("create routes watched from etcd into radixtree.", json.encode(cur_rt)) + err = uri_router:add_route(cur_rt, router_opts) + if err ~= nil then + core.log.error("add routes into radixtree failed.", json.encode(cur_rt), err) + return + end + elseif op == "delete" then + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(cur_rt)) + err = uri_router:delete_route(lst_rt, router_opts) + if err ~= nil then + core.log.error("delete a route into radixtree failed.", json.encode(cur_rt), err) + return + end + end + + sync_tb[k] = nil + end + + apisix_router.sync_tb = sync_tb +end + + local match_opts = {} function _M.match(api_ctx) + local user_routes = _M.user_routes + local _, service_version = get_services() + if not cached_router_version or cached_router_version ~= user_routes.conf_version + or not cached_service_version or cached_service_version ~= service_version + then + --create_radixtree_router(user_routes.values) + incremental_operate_radixtree(user_routes.values) + cached_router_version = user_routes.conf_version + cached_service_version = service_version + end + return _M.matching(api_ctx) end function _M.matching(api_ctx) - local host_router = _M.host_router - local only_uri_router = _M.only_uri_router - core.log.info("route match mode: radixtree_host_uri") core.table.clear(match_opts) diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 4c3867c4dff6..11ae64ad3930 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -17,21 +17,142 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") -local router = require("apisix.router") - +local get_services = require("apisix.http.service").services +local apisix_router = require("apisix/router") +local json = require("apisix.core.json") +local cached_router_version +local cached_service_version +local uri_routes = {} +local uri_router +local match_opts = {} local _M = {version = 0.2} - local match_opts = {} +local function incremental_operate_radixtree(routes) + if apisix_router.need_create_radixtree then + uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, false) + apisix_router.need_create_radixtree = false + return + end + + local sync_tb = apisix_router.sync_tb + local op, route, last_route, err + local cur_tmp, last_tmp = {}, {} + local router_opts = { + no_param_match = false + } + for k, v in pairs(sync_tb) do + op = sync_tb[k]["op"] + route = sync_tb[k]["cur_route"] + last_route = sync_tb[k]["last_route"] + cur_tmp = {} + last_tmp = {} + + if route then + local status = table.try_read_attr(route, "value", "status") + if status and status == 0 then + return + end + + local filter_fun, err + if route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id + ) + if not filter_fun then + core.log.error("failed to load filter function: ", err, " route id", route.value.id) + return + end + + filter_fun = filter_fun() + end + + cur_tmp = { + id = route.value.id, + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, + hosts = route.value.hosts or route.value.host, + remote_addrs = route.value.remote_addrs or route.value.remote_addr, + vars = route.value.vars, + filter_fun = filter_fun, + handler = function(api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = route + api_ctx.curr_req_matched = match_opts.matched + end + } + end + + if last_route then + last_tmp = { + id = last_route.value.id, + paths = last_route.value.uris or last_route.value.uri, + methods = last_route.value.methods, + priority = last_route.value.priority, + hosts = last_route.value.hosts or last_route.value.host, + remote_addrs = last_route.value.remote_addrs or last_route.value.remote_addr, + vars = last_route.value.vars + } + end + + if op == "update" then + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:update_route(last_tmp, cur_tmp, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(route), err) + return + end + elseif op == "create" then + core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:add_route(cur_tmp, router_opts) + if err ~= nil then + core.log.error("add routes into radixtree failed.", json.encode(route), err) + return + end + elseif op == "delete" then + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:delete_route(last_tmp, router_opts) + if err ~= nil then + core.log.error("delete a route into radixtree failed.", json.encode(route), err) + return + end + end + + sync_tb[k] = nil + end + + apisix_router.sync_tb = sync_tb +end + + function _M.match(api_ctx) + local user_routes = _M.user_routes + local _, service_version = get_services() + if not cached_router_version or cached_router_version ~= user_routes.conf_version + or not cached_service_version or cached_service_version ~= service_version + then + incremental_operate_radixtree(user_routes.values) + cached_router_version = user_routes.conf_version + cached_service_version = service_version + end + + if not uri_router then + core.log.error("failed to fetch valid `uri` router: ") + return true + end + return _M.matching(api_ctx) end function _M.matching(api_ctx) core.log.info("route match mode: radixtree_uri") - return base_router.match_uri(router.uri_router, match_opts, api_ctx) + + return base_router.match_uri(uri_router, match_opts, api_ctx) end + return _M diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index e36008c8bbbd..e2e4af8f7fc3 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -17,21 +17,144 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") -local router = require("apisix.router") +local get_services = require("apisix.http.service").services +local apisix_router = require("apisix/router") +local json = require("apisix.core.json") +local table = require("apisix.core.table") +local cached_router_version +local cached_service_version +local uri_routes = {} +local uri_router +local match_opts = {} local _M = {} - local match_opts = {} +local function incremental_operate_radixtree(routes) + if apisix_router.need_create_radixtree then + uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, true) + apisix_router.need_create_radixtree = false + return + end + + local sync_tb = apisix_router.sync_tb + local op, route, last_route, err + local cur_tmp, last_tmp = {}, {} + local router_opts = { + no_param_match = false + } + for k, v in pairs(sync_tb) do + op = sync_tb[k]["op"] + route = sync_tb[k]["cur_route"] + last_route = sync_tb[k]["last_route"] + cur_tmp = {} + last_tmp = {} + + if route then + local status = table.try_read_attr(route, "value", "status") + if status and status == 0 then + return + end + + local filter_fun, err + if route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id + ) + if not filter_fun then + core.log.error("failed to load filter function: ", err, " route id", route.value.id) + return + end + + filter_fun = filter_fun() + end + + cur_tmp = { + id = route.value.id, + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, + hosts = route.value.hosts or route.value.host, + remote_addrs = route.value.remote_addrs or route.value.remote_addr, + vars = route.value.vars, + filter_fun = filter_fun, + handler = function(api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = route + api_ctx.curr_req_matched = match_opts.matched + end + } + end + + if last_route then + last_tmp = { + id = last_route.value.id, + paths = last_route.value.uris or last_route.value.uri, + methods = last_route.value.methods, + priority = last_route.value.priority, + hosts = last_route.value.hosts or last_route.value.host, + remote_addrs = last_route.value.remote_addrs or last_route.value.remote_addr, + vars = last_route.value.vars + } + end + + if op == "update" then + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:update_route(last_tmp, cur_tmp, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(route), err) + return + end + elseif op == "create" then + core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:add_route(cur_tmp, router_opts) + if err ~= nil then + core.log.error("add routes into radixtree failed.", json.encode(route), err) + return + end + elseif op == "delete" then + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + err = uri_router:delete_route(last_tmp, router_opts) + if err ~= nil then + core.log.error("delete a route into radixtree failed.", json.encode(route), err) + return + end + end + + sync_tb[k] = nil + end + + apisix_router.sync_tb = sync_tb +end + + function _M.match(api_ctx) + local user_routes = _M.user_routes + local _, service_version = get_services() + if not cached_router_version or cached_router_version ~= user_routes.conf_version + or not cached_service_version or cached_service_version ~= service_version + then + incremental_operate_radixtree(user_routes.values) + cached_router_version = user_routes.conf_version + cached_service_version = service_version + end + + if not uri_router then + core.log.error("failed to fetch valid `uri_with_parameter` router: ") + return true + end + return _M.matching(api_ctx) end function _M.matching(api_ctx) core.log.info("route match mode: radixtree_uri_with_parameter") - return base_router.match_uri(router.uri_router, match_opts, api_ctx) + + return base_router.match_uri(uri_router, match_opts, api_ctx) end + return _M diff --git a/apisix/router.lua b/apisix/router.lua index 537297c63364..feee6066559f 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -22,289 +22,79 @@ local plugin_checker = require("apisix.plugin").plugin_checker local str_lower = string.lower local error = error local ipairs = ipairs -local sub_str = string.sub local table = require("apisix.core.table") local json = require("apisix.core.json") -local router_util = require("apisix.utils.router") -local tab_insert = table.insert -local event = require("apisix.core.event") local _M = {version = 0.3} -local function empty_func() end -local routes_obj, first_route local function filter(route, pre_route_or_size, obj) route.orig_modifiedIndex = route.modifiedIndex route.update_count = 0 route.has_domain = false - if route.value then - if route.value.host then - route.value.host = str_lower(route.value.host) - elseif route.value.hosts then - for i, v in ipairs(route.value.hosts) do - route.value.hosts[i] = str_lower(v) - end - end - - apisix_upstream.filter_upstream(route.value.upstream, route) - end - - core.log.info("filter route: ", core.json.delay_encode(route, true)) - --filter route from other config source - if not obj then + if not route.value then return end - --load_full_data()'s filter() goes here. create radixtree while etcd compacts - local router_module = require("apisix.router") - local conf = core.config.local_conf() - if conf.apisix.router.http == "radixtree_uri" or conf.apisix.router.http == "radixtree_uri_with_parameter" then - local router_opts - local with_parameter = false - if conf.apisix.router.http == "radixtree_uri" then - router_opts = { - no_param_match = true - } - else - with_parameter = true - router_opts = { - no_param_match = false - } - end - - if type(pre_route_or_size) == "number" then - if pre_route_or_size == #obj.values then - routes_obj = obj - local uri_routes = {} - core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) - local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) - if not uri_router then - core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) - return - end - - _M.uri_router = uri_router - if not first_route then - first_route = true - end - end - - return + if route.value.host then + route.value.host = str_lower(route.value.host) + elseif route.value.hosts then + for i, v in ipairs(route.value.hosts) do + route.value.hosts[i] = str_lower(v) end + end - if not first_route then - routes_obj = obj - local uri_routes = {} - core.log.notice("create radixtree uri for the first route income.") - local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) - if not uri_router then - core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) - return - end - - _M.uri_router = uri_router - first_route = true - return - end + apisix_upstream.filter_upstream(route.value.upstream, route) - --only sync_data()'s filter() goes here - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) + --save sync route and operation type into a map + if type(pre_route_or_size) == "number" then + if pre_route_or_size == #obj.values then + _M.need_create_radixtree = true end + return + end - local radixtree_obj = router_module.uri_router - local cur_route + local sync_tb = _M.sync_tb + local val + if pre_route_or_size then if route.value then - local status = table.try_read_attr(route, "value", "status") - if status and status == 0 then - return - end - - local filter_fun, err - if route.value.filter_func then - filter_fun, err = loadstring( - "return " .. route.value.filter_func, - "router#" .. route.value.id - ) - if not filter_fun then - core.log.error("failed to load filter function: ", err, " route id", route.value.id) - return - end - - filter_fun = filter_fun() - end - - cur_route = { - id = route.value.id, - paths = route.value.uris or route.value.uri, - methods = route.value.methods, - priority = route.value.priority, - hosts = route.value.hosts or route.value.host, - remote_addrs = route.value.remote_addrs or route.value.remote_addr, - vars = route.value.vars, - filter_fun = filter_fun, - handler = function(api_ctx, match_opts) - api_ctx.matched_params = nil - api_ctx.matched_route = route - api_ctx.curr_req_matched = match_opts.matched - end - } - end - - local err - if pre_route_or_size then - local last_route = { - id = pre_route_or_size.value.id, - paths = pre_route_or_size.value.uris or pre_route_or_size.value.uri, - methods = pre_route_or_size.value.methods, - priority = pre_route_or_size.value.priority, - hosts = pre_route_or_size.value.hosts or pre_route_or_size.value.host, - remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, - vars = pre_route_or_size.value.vars - } - - if route.value then - --update route - core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) - err = radixtree_obj:update_route(last_route, cur_route, router_opts) - if err ~= nil then - core.log.error("update a route into radixtree failed.", json.encode(route), err) - return - end - else - --delete route - core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) - err = radixtree_obj:delete_route(last_route, router_opts) - if err ~= nil then - core.log.error("delete a route into radixtree failed.", json.encode(route), err) - return - end - end - elseif route.value then - --create route - core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) - err = radixtree_obj:add_route(cur_route, router_opts) - if err ~= nil then - core.log.error("add routes into radixtree failed.", json.encode(route), err) - return + --update route + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + if not sync_tb[route.value.id] then + sync_tb[route.value.id] = {op = "update", last_route = pre_route_or_size, cur_route = route} + elseif sync_tb[route.value.id]["op"] == "update" then + sync_tb[route.value.id] = {op = "update", last_route = sync_tb[route.value.id]["last_route"], cur_route = route} + elseif sync_tb[route.value.id]["op"] == "create" then + sync_tb[route.value.id] = {op = "create", cur_route = route} end else - core.log.error("invalid operation type for a route.", route.key) - return - end - elseif conf.apisix.router.http == "radixtree_host_uri" then - local router_opts = { - no_param_match = true - } - - local host_uri = require("apisix.http.router.radixtree_host_uri") - if type(pre_route_or_size) == "number" then - if pre_route_or_size == #obj.values then - routes_obj = obj - core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) - host_uri.create_radixtree_router(routes_obj.values) - if not first_route then - first_route = true - end - end - - return - end - - if not first_route or (not host_uri.host_router or not host_uri.only_uri_router) then - routes_obj = obj - core.log.notice("create radixtree uri for the first route income.") - host_uri.create_radixtree_router(routes_obj.values) - first_route = true - return - end - - if router_module.router_http then - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - end - - local only_uri_routes = {} - local route_opt, pre_route_opt = {}, {} - local all_hosts = {} - local hosts, pre_hosts = nil, nil - local rdx_r = {} - local pre_rdx_r = {} - local op = {add={}, upd={}, del={}} - - - local status = table.try_read_attr(route, "value", "status") - if status and status == 0 then - return - end - - host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) - - hosts = all_hosts["host"] - if hosts ~= nil then - for _, h in ipairs(hosts) do - local host_rev = h:reverse() - local routes = host_uri.host_routes[host_rev] - local sub_router = router_util.new(routes) - route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = empty_func, - } - end - end - - pre_hosts = all_hosts["pre_host"] - if pre_hosts ~= nil then - for _, h in ipairs(pre_hosts) do - local host_rev = h:reverse() - pre_route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = empty_func, - handler = empty_func, - } - end - end - - for k, v in pairs(op) do - if k == "add" then - for _, j in ipairs(v) do - core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local r_opt = route_opt[j] - host_uri.host_router:add_route(r_opt, router_opts) - end - elseif k == "upd" then - for _, j in ipairs(v) do - core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local r_opt = route_opt[j] - host_uri.host_router:update_route(r_opt, r_opt, router_opts) - end - elseif k == "del" then - for _, j in ipairs(v) do - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local pre_r_opt = pre_route_opt[j] - host_uri.host_router:delete_route(pre_r_opt, router_opts) - end + --delete route + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + if not sync_tb[route.value.id] then + sync_tb[route.value.id] = {op = "delete", last_route = pre_route_or_size} + elseif sync_tb[route.value.id]["op"] == "create" then + sync_tb[route.value.id] = nil + elseif sync_tb[route.value.id]["op"] == "update" then + sync_tb[route.value.id] = {op = "delete", last_route = sync_tb[route.value.id]["last_route"]} end end - - if (route.value and not hosts) and (not pre_route_or_size or pre_hosts) then - core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) - host_uri.only_uri_router:add_route(rdx_r, router_opts) - elseif (route.value and not hosts) and (pre_route_or_size and not pre_hosts) then - core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) - host_uri.only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) - elseif (pre_route_or_size and not pre_hosts) and (not route.value or hosts) then - core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(pre_route_or_size)) - host_uri.only_uri_router:delete_route(pre_rdx_r, router_opts) + elseif route.value then + --create route + core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) + if not sync_tb[route.value.id] then + sync_tb[route.value.id] = {op = "create", cur_route = route} + elseif sync_tb[route.value.id]["op"] == "delete" then + sync_tb[route.value.id] = {op = "update", cur_route = route, last_route = sync_tb[route.value.id]["last_route"]} end + else + core.log.error("invalid operation type for a route.", route.key) + return end + + _M.sync_tb = sync_tb + core.log.info("filter route: ", core.json.delay_encode(route, true)) end @@ -330,6 +120,7 @@ end function _M.http_init_worker() + _M.sync_tb = {} local conf = core.config.local_conf() local router_http_name = "radixtree_uri" local router_ssl_name = "radixtree_sni" @@ -343,7 +134,6 @@ function _M.http_init_worker() attach_http_router_common_methods(router_http) router_http.init_worker(filter) _M.router_http = router_http - event.push(event.CONST.BUILD_ROUTER, router_http.user_routes.values) local router_ssl = require("apisix.ssl.router." .. router_ssl_name) router_ssl.init_worker() diff --git a/increment_update_route.patch b/increment_update_route.patch new file mode 100644 index 000000000000..c03a2b8eb623 --- /dev/null +++ b/increment_update_route.patch @@ -0,0 +1,806 @@ +commit cfddd8b6489f1390de71d6853390ab7f1d841297 +Author: ranxuxin001 +Date: Mon Jun 19 11:39:55 2023 +0800 + + increment route update for radixtree host uri, radixtree uri and radixtree uri with parameter + +diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua +index ecb76270..61113506 100644 +--- a/apisix/core/config_etcd.lua ++++ b/apisix/core/config_etcd.lua +@@ -499,7 +499,7 @@ local function load_full_data(self, dir_res, headers) + item.clean_handlers = {} + + if self.filter then +- self.filter(item) ++ self.filter(item, 1, self) + end + end + +@@ -551,7 +551,7 @@ local function load_full_data(self, dir_res, headers) + item.clean_handlers = {} + + if self.filter then +- self.filter(item) ++ self.filter(item, #values, self) + end + end + +@@ -695,9 +695,10 @@ local function sync_data(self) + return false + end + ++ local pre_val + local pre_index = self.values_hash[key] + if pre_index then +- local pre_val = self.values[pre_index] ++ pre_val = self.values[pre_index] + if pre_val then + config_util.fire_all_clean_handlers(pre_val) + end +@@ -755,7 +756,7 @@ local function sync_data(self) + -- /plugins' filter need to known self.values when it is called + -- so the filter should be called after self.values set. + if self.filter then +- self.filter(res) ++ self.filter(res, pre_val, self) + end + + self.conf_version = self.conf_version + 1 +diff --git a/apisix/http/route.lua b/apisix/http/route.lua +index d475646b..2feab788 100644 +--- a/apisix/http/route.lua ++++ b/apisix/http/route.lua +@@ -73,6 +73,7 @@ function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter) + core.log.info("insert uri route: ", + core.json.delay_encode(route.value, true)) + core.table.insert(uri_routes, { ++ id = route.value.id, + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, +diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua +index 532576e5..369a54db 100644 +--- a/apisix/http/router/radixtree_host_uri.lua ++++ b/apisix/http/router/radixtree_host_uri.lua +@@ -27,20 +27,28 @@ local loadstring = loadstring + local pairs = pairs + local cached_router_version + local cached_service_version +-local host_router +-local only_uri_router ++local base = require("resty.core.base") ++local clear_tab = base.clear_tab + + + local _M = {version = 0.1} + + +-local function push_host_router(route, host_routes, only_uri_routes) ++local function tab_cpy(t1, t2) ++ t1 = {} ++ for k, v in pairs(t2) do ++ t1[k] = v ++ end ++end ++ ++ ++function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) + if type(route) ~= "table" then + return + end + + local filter_fun, err +- if route.value.filter_func then ++ if route.value and route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id) +@@ -53,67 +61,213 @@ local function push_host_router(route, host_routes, only_uri_routes) + filter_fun = filter_fun() + end + +- local hosts = route.value.hosts +- if not hosts then +- if route.value.host then +- hosts = {route.value.host} +- elseif route.value.service_id then +- local service = service_fetch(route.value.service_id) +- if not service then +- core.log.error("failed to fetch service configuration by ", +- "id: ", route.value.service_id) +- -- we keep the behavior that missing service won't affect the route matching +- else +- hosts = service.value.hosts ++ local radixtree_route, pre_radixtree_route = {}, {} ++ local hosts ++ if route and route.value then ++ hosts = route.value.hosts ++ if not hosts then ++ if route.value.host then ++ hosts = {route.value.host} ++ elseif route.value.service_id then ++ local service = service_fetch(route.value.service_id) ++ if not service then ++ core.log.error("failed to fetch service configuration by ", ++ "id: ", route.value.service_id) ++ -- we keep the behavior that missing service won't affect the route matching ++ else ++ hosts = service.value.hosts ++ end ++ end ++ end ++ ++ radixtree_route = { ++ id = route.value.id, ++ paths = route.value.uris or route.value.uri, ++ methods = route.value.methods, ++ priority = route.value.priority, ++ remote_addrs = route.value.remote_addrs ++ or route.value.remote_addr, ++ vars = route.value.vars, ++ filter_fun = filter_fun, ++ handler = function (api_ctx, match_opts) ++ api_ctx.matched_params = nil ++ api_ctx.matched_route = route ++ api_ctx.curr_req_matched = match_opts.matched ++ api_ctx.real_curr_req_matched_path = match_opts.matched._path ++ end ++ } ++ ++ if rdx_rt ~= nil then ++ for k, v in pairs(radixtree_route) do ++ rdx_rt[k] = v + end + end + end + +- local radixtree_route = { +- paths = route.value.uris or route.value.uri, +- methods = route.value.methods, +- priority = route.value.priority, +- remote_addrs = route.value.remote_addrs +- or route.value.remote_addr, +- vars = route.value.vars, +- filter_fun = filter_fun, +- handler = function (api_ctx, match_opts) +- api_ctx.matched_params = nil +- api_ctx.matched_route = route +- api_ctx.curr_req_matched = match_opts.matched +- api_ctx.real_curr_req_matched_path = match_opts.matched._path +- end +- } +- +- if hosts == nil then ++ if hosts == nil and all_hosts == nil then + core.table.insert(only_uri_routes, radixtree_route) + return + end + +- for i, host in ipairs(hosts) do +- local host_rev = host:reverse() +- if not host_routes[host_rev] then +- host_routes[host_rev] = {radixtree_route} ++ local pre_hosts ++ if pre_route and pre_route.value then ++ pre_hosts = pre_route.value.hosts ++ if not pre_hosts then ++ if pre_route.value.host then ++ pre_hosts = {pre_route.value.host} ++ elseif pre_route.value.service_id then ++ local service = service_fetch(pre_route.value.service_id) ++ if not service then ++ core.log.error("failed to fetch service configuration by ", ++ "id: ", pre_route.value.service_id) ++ -- we keep the behavior that missing service won't affect the route matching ++ else ++ pre_hosts = service.value.hosts ++ end ++ end ++ end ++ ++ pre_radixtree_route = { ++ id = pre_route.value.id, ++ paths = pre_route.value.uris or pre_route.value.uri, ++ methods = pre_route.value.methods, ++ priority = pre_route.value.priority, ++ remote_addrs = pre_route.value.remote_addrs ++ or pre_route.value.remote_addr, ++ vars = pre_route.value.vars, ++ filter_fun = filter_fun, ++ handler = function (api_ctx, match_opts) ++ api_ctx.matched_params = nil ++ api_ctx.matched_route = pre_route ++ api_ctx.curr_req_matched = match_opts.matched ++ api_ctx.real_curr_req_matched_path = match_opts.matched._path ++ end ++ } ++ ++ if pre_rdx_rt ~= nil then ++ for k, v in pairs(pre_radixtree_route) do ++ pre_rdx_rt[k] = v ++ end ++ end ++ end ++ ++ if all_hosts ~= nil then ++ all_hosts["host"] = hosts ++ all_hosts["pre_host"] = pre_hosts ++ end ++ ++ local pre_t = {} ++ if pre_hosts then ++ for i, h in ipairs(pre_hosts) do ++ local rev_h = h:reverse() ++ pre_t[rev_h] = 1 ++ end ++ end ++ ++ local t = {} ++ if hosts then ++ for i, h in ipairs(hosts) do ++ local rev_h = h:reverse() ++ t[rev_h] = 1 ++ end ++ end ++ ++ local comm = {} ++ for k, v in pairs(pre_t) do ++ if t[k] ~= nil then ++ tab_insert(comm, k) ++ pre_t[k] = nil ++ t[k] = nil ++ end ++ end ++ ++ for _, j in ipairs(comm) do ++ local routes = host_routes[j] ++ if routes == nil then ++ core.log.error("no routes array for reverse host in the map.", j) ++ return ++ end ++ ++ local found = false ++ for i, r in ipairs(routes) do ++ if r.id == radixtree_route.id then ++ routes[i] = radixtree_route ++ found = true ++ if op then ++ table.insert(op["upd"], j) ++ end ++ break ++ end ++ end ++ ++ if not found then ++ core.log.error("cannot find the route in common host's table.", j, radixtree_route.id) ++ return ++ end ++ end ++ ++ for k, v in pairs(pre_t) do ++ local routes = host_routes[k] ++ if routes == nil then ++ core.log.error("no routes array for reverse host in the map.", k) ++ return ++ end ++ ++ local found = false ++ for i, r in ipairs(routes) do ++ if r.id == pre_radixtree_route.id then ++ table.remove(routes, i) ++ found = true ++ break ++ end ++ end ++ ++ if not found then ++ core.log.error("cannot find the route in previous host's table.", k, pre_radixtree_route.id) ++ return ++ end ++ ++ if #routes == 0 then ++ host_routes[k] = nil ++ if op then ++ table.insert(op["del"], k) ++ end ++ else ++ if op then ++ table.insert(op["upd"], k) ++ end ++ end ++ end ++ ++ for k, v in pairs(t) do ++ local routes = host_routes[k] ++ if routes == nil then ++ host_routes[k] = {radixtree_route} ++ if op then ++ table.insert(op["add"], k) ++ end + else +- tab_insert(host_routes[host_rev], radixtree_route) ++ table.insert(routes, radixtree_route) ++ if op then ++ table.insert(op["upd"], k) ++ end + end + end + end + + +-local function create_radixtree_router(routes) ++function _M.create_radixtree_router(routes) + local host_routes = {} + local only_uri_routes = {} +- host_router = nil + routes = routes or {} + + for _, route in ipairs(routes) do + local status = core.table.try_read_attr(route, "value", "status") + -- check the status + if not status or status == 1 then +- push_host_router(route, host_routes, only_uri_routes) +- end +- end ++ _M.push_host_router(route, host_routes, only_uri_routes) ++ end ++ end + + -- create router: host_router + local host_router_routes = {} +@@ -121,45 +275,42 @@ local function create_radixtree_router(routes) + local sub_router = router.new(routes) + + core.table.insert(host_router_routes, { ++ id = 1, + paths = host_rev, + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = function (api_ctx, match_opts) + api_ctx.real_curr_req_matched_host = match_opts.matched._path +- end +- }) +- end ++ end ++ }) ++ end ++ ++ _M.host_routes = host_routes + + event.push(event.CONST.BUILD_ROUTER, routes) + + if #host_router_routes > 0 then +- host_router = router.new(host_router_routes) ++ _M.host_router = router.new(host_router_routes) + end + + -- create router: only_uri_router +- only_uri_router = router.new(only_uri_routes) ++ _M.only_uri_router = router.new(only_uri_routes) ++ + return true + end + + + local match_opts = {} + function _M.match(api_ctx) +- local user_routes = _M.user_routes +- local _, service_version = get_services() +- if not cached_router_version or cached_router_version ~= user_routes.conf_version +- or not cached_service_version or cached_service_version ~= service_version +- then +- create_radixtree_router(user_routes.values) +- cached_router_version = user_routes.conf_version +- cached_service_version = service_version +- end +- + return _M.matching(api_ctx) + end + + + function _M.matching(api_ctx) ++ local host_router = _M.host_router ++ local only_uri_router = _M.only_uri_router ++ + core.log.info("route match mode: radixtree_host_uri") + + core.table.clear(match_opts) +diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua +index 6e546364..4c3867c4 100644 +--- a/apisix/http/router/radixtree_uri.lua ++++ b/apisix/http/router/radixtree_uri.lua +@@ -17,43 +17,21 @@ + local require = require + local core = require("apisix.core") + local base_router = require("apisix.http.route") +-local get_services = require("apisix.http.service").services +-local cached_router_version +-local cached_service_version ++local router = require("apisix.router") + + + local _M = {version = 0.2} + + +- local uri_routes = {} +- local uri_router + local match_opts = {} + function _M.match(api_ctx) +- local user_routes = _M.user_routes +- local _, service_version = get_services() +- if not cached_router_version or cached_router_version ~= user_routes.conf_version +- or not cached_service_version or cached_service_version ~= service_version +- then +- uri_router = base_router.create_radixtree_uri_router(user_routes.values, +- uri_routes, false) +- cached_router_version = user_routes.conf_version +- cached_service_version = service_version +- end +- +- if not uri_router then +- core.log.error("failed to fetch valid `uri` router: ") +- return true +- end +- + return _M.matching(api_ctx) + end + + + function _M.matching(api_ctx) + core.log.info("route match mode: radixtree_uri") +- +- return base_router.match_uri(uri_router, match_opts, api_ctx) ++ return base_router.match_uri(router.uri_router, match_opts, api_ctx) + end + +- + return _M +diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua +index 4bf7f3eb..e36008c8 100644 +--- a/apisix/http/router/radixtree_uri_with_parameter.lua ++++ b/apisix/http/router/radixtree_uri_with_parameter.lua +@@ -17,43 +17,21 @@ + local require = require + local core = require("apisix.core") + local base_router = require("apisix.http.route") +-local get_services = require("apisix.http.service").services +-local cached_router_version +-local cached_service_version ++local router = require("apisix.router") + + + local _M = {} + + +- local uri_routes = {} +- local uri_router + local match_opts = {} + function _M.match(api_ctx) +- local user_routes = _M.user_routes +- local _, service_version = get_services() +- if not cached_router_version or cached_router_version ~= user_routes.conf_version +- or not cached_service_version or cached_service_version ~= service_version +- then +- uri_router = base_router.create_radixtree_uri_router(user_routes.values, +- uri_routes, true) +- cached_router_version = user_routes.conf_version +- cached_service_version = service_version +- end +- +- if not uri_router then +- core.log.error("failed to fetch valid `uri_with_parameter` router: ") +- return true +- end +- + return _M.matching(api_ctx) + end + + + function _M.matching(api_ctx) + core.log.info("route match mode: radixtree_uri_with_parameter") +- +- return base_router.match_uri(uri_router, match_opts, api_ctx) ++ return base_router.match_uri(router.uri_router, match_opts, api_ctx) + end + +- + return _M +diff --git a/apisix/router.lua b/apisix/router.lua +index 2fd14917..ec262e74 100644 +--- a/apisix/router.lua ++++ b/apisix/router.lua +@@ -22,31 +22,281 @@ local plugin_checker = require("apisix.plugin").plugin_checker + local str_lower = string.lower + local error = error + local ipairs = ipairs ++local sub_str = string.sub ++local table = require("apisix.core.table") ++local json = require("apisix.core.json") ++local router_util = require("apisix.utils.router") ++local tab_insert = table.insert ++local event = require("apisix.core.event") + + + local _M = {version = 0.3} + ++local function empty_func() end ++local routes_obj, first_route + +-local function filter(route) ++local function filter(route, pre_route_or_size, obj) + route.orig_modifiedIndex = route.modifiedIndex + route.update_count = 0 + + route.has_domain = false +- if not route.value then +- return ++ if route.value then ++ if route.value.host then ++ route.value.host = str_lower(route.value.host) ++ elseif route.value.hosts then ++ for i, v in ipairs(route.value.hosts) do ++ route.value.hosts[i] = str_lower(v) ++ end ++ end ++ ++ apisix_upstream.filter_upstream(route.value.upstream, route) + end + +- if route.value.host then +- route.value.host = str_lower(route.value.host) +- elseif route.value.hosts then +- for i, v in ipairs(route.value.hosts) do +- route.value.hosts[i] = str_lower(v) ++ core.log.info("filter route: ", core.json.delay_encode(route, true)) ++ ++ --load_full_data()'s filter() goes here. create radixtree while etcd compacts ++ local conf = core.config.local_conf() ++ if conf.apisix.router.http == "radixtree_uri" or conf.apisix.router.http == "radixtree_uri_with_parameter" then ++ local router_opts ++ local with_parameter = false ++ if conf.apisix.router.http == "radixtree_uri" then ++ router_opts = { ++ no_param_match = true ++ } ++ else ++ with_parameter = true ++ router_opts = { ++ no_param_match = false ++ } + end +- end ++ ++ if type(pre_route_or_size) == "number" then ++ if pre_route_or_size == #obj.values then ++ routes_obj = obj ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ local uri_routes = {} ++ core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) ++ local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) ++ if not uri_router then ++ core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) ++ return ++ end + +- apisix_upstream.filter_upstream(route.value.upstream, route) ++ _M.uri_router = uri_router ++ end + +- core.log.info("filter route: ", core.json.delay_encode(route, true)) ++ return ++ end ++ ++ if not first_route then ++ routes_obj = obj ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ local uri_routes = {} ++ core.log.notice("create radixtree uri for the first route income.") ++ local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) ++ if not uri_router then ++ core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) ++ return ++ end ++ ++ _M.uri_router = uri_router ++ first_route = true ++ return ++ end ++ ++ --only sync_data()'s filter() goes here ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ ++ local router_module = require("apisix.router") ++ local radixtree_obj = router_module.uri_router ++ ++ local cur_route ++ if route.value then ++ local status = table.try_read_attr(route, "value", "status") ++ if status and status == 0 then ++ return ++ end ++ ++ local filter_fun, err ++ if route.value.filter_func then ++ filter_fun, err = loadstring( ++ "return " .. route.value.filter_func, ++ "router#" .. route.value.id ++ ) ++ if not filter_fun then ++ core.log.error("failed to load filter function: ", err, " route id", route.value.id) ++ return ++ end ++ ++ filter_fun = filter_fun() ++ end ++ ++ cur_route = { ++ id = route.value.id, ++ paths = route.value.uris or route.value.uri, ++ methods = route.value.methods, ++ priority = route.value.priority, ++ hosts = route.value.hosts or route.value.host, ++ remote_addrs = route.value.remote_addrs or route.value.remote_addr, ++ vars = route.value.vars, ++ filter_fun = filter_fun, ++ handler = function(api_ctx, match_opts) ++ api_ctx.matched_params = nil ++ api_ctx.matched_route = route ++ api_ctx.curr_req_matched = match_opts.matched ++ end ++ } ++ end ++ ++ local err ++ if pre_route_or_size then ++ local last_route = { ++ id = pre_route_or_size.value.id, ++ paths = pre_route_or_size.value.uris or pre_route_or_size.value.uri, ++ methods = pre_route_or_size.value.methods, ++ priority = pre_route_or_size.value.priority, ++ hosts = pre_route_or_size.value.hosts or pre_route_or_size.value.host, ++ remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, ++ vars = pre_route_or_size.value.vars ++ } ++ ++ if route.value then ++ --update route ++ core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) ++ err = radixtree_obj:update_route(last_route, cur_route, router_opts) ++ if err ~= nil then ++ core.log.error("update a route into radixtree failed.", json.encode(route), err) ++ return ++ end ++ else ++ --delete route ++ core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) ++ err = radixtree_obj:delete_route(last_route, router_opts) ++ if err ~= nil then ++ core.log.error("delete a route into radixtree failed.", json.encode(route), err) ++ return ++ end ++ end ++ elseif route.value then ++ --create route ++ core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) ++ err = radixtree_obj:add_route(cur_route, router_opts) ++ if err ~= nil then ++ core.log.error("add routes into radixtree failed.", json.encode(route), err) ++ return ++ end ++ else ++ core.log.error("invalid operation type for a route.", route.key) ++ return ++ end ++ elseif conf.apisix.router.http == "radixtree_host_uri" then ++ local router_opts = { ++ no_param_match = true ++ } ++ ++ local host_uri = require("apisix.http.router.radixtree_host_uri") ++ if type(pre_route_or_size) == "number" then ++ if pre_route_or_size == #obj.values then ++ routes_obj = obj ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) ++ host_uri.create_radixtree_router(routes_obj.values) ++ end ++ ++ return ++ end ++ ++ if not first_route then ++ routes_obj = obj ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ core.log.notice("create radixtree uri for the first route income.") ++ host_uri.create_radixtree_router(routes_obj.values) ++ first_route = true ++ return ++ end ++ ++ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) ++ ++ local only_uri_routes = {} ++ local route_opt, pre_route_opt = {}, {} ++ local all_hosts = {} ++ local hosts, pre_hosts = nil, nil ++ local rdx_r = {} ++ local pre_rdx_r = {} ++ local op = {add={}, upd={}, del={}} ++ ++ ++ local status = table.try_read_attr(route, "value", "status") ++ if status and status == 0 then ++ return ++ end ++ ++ -- to be confirm??? assign variable of other module. ++ host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) ++ ++ hosts = all_hosts["host"] ++ if hosts ~= nil then ++ for _, h in ipairs(hosts) do ++ local host_rev = h:reverse() ++ local routes = host_uri.host_routes[host_rev] ++ local sub_router = router_util.new(routes) ++ route_opt[host_rev] = { ++ id = 1, ++ paths = host_rev, ++ filter_fun = function(vars, opts, ...) ++ return sub_router:dispatch(vars.uri, opts, ...) ++ end, ++ handler = empty_func, ++ } ++ end ++ end ++ ++ pre_hosts = all_hosts["pre_host"] ++ if pre_hosts ~= nil then ++ for _, h in ipairs(pre_hosts) do ++ local host_rev = h:reverse() ++ pre_route_opt[host_rev] = { ++ id = 1, ++ paths = host_rev, ++ filter_fun = empty_func, ++ handler = empty_func, ++ } ++ end ++ end ++ ++ for k, v in pairs(op) do ++ if k == "add" then ++ for _, j in ipairs(v) do ++ core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) ++ local r_opt = route_opt[j] ++ host_uri.host_router:add_route(r_opt, router_opts) ++ end ++ elseif k == "upd" then ++ for _, j in ipairs(v) do ++ core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) ++ local r_opt = route_opt[j] ++ host_uri.host_router:update_route(r_opt, r_opt, router_opts) ++ end ++ elseif k == "del" then ++ for _, j in ipairs(v) do ++ core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) ++ local pre_r_opt = pre_route_opt[j] ++ host_uri.host_router:delete_route(pre_r_opt, router_opts) ++ end ++ end ++ end ++ ++ if (route.value and not hosts) and (not pre_route_or_size or pre_hosts) then ++ core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) ++ host_uri.only_uri_router:add_route(rdx_r, router_opts) ++ elseif (route.value and not hosts) and (pre_route_or_size and not pre_hosts) then ++ core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) ++ host_uri.only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) ++ elseif (pre_route_or_size and not pre_hosts) and (not route.value or hosts) then ++ core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(pre_route_or_size)) ++ host_uri.only_uri_router:delete_route(pre_rdx_r, router_opts) ++ end ++ end + end + + From bd7292130436a3dbe336cdc0cc61357758b59db5 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Mon, 3 Jul 2023 18:51:47 +0800 Subject: [PATCH 10/22] increment update a route for radixtree host uri --- apisix/http/router/radixtree_host_uri.lua | 111 +++++++++++++++++----- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 0f0c90af71c6..37ae43df0c9f 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -20,7 +20,9 @@ local core = require("apisix.core") local event = require("apisix.core.event") local get_services = require("apisix.http.service").services local service_fetch = require("apisix.http.service").get -local apisix_router = require("apisix/router") +local apisix_router = require("apisix.router") +local table = require("apisix.core.table") +local json = require("apisix.core.json") local ipairs = ipairs local type = type local tab_insert = table.insert @@ -30,11 +32,14 @@ local cached_router_version local cached_service_version local host_router local only_uri_router +local host_routes = {} +local only_uri_routes = {} local _M = {version = 0.1} +local function empty_func() end local function push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) if type(route) ~= "table" then return @@ -250,8 +255,6 @@ end local function create_radixtree_router(routes) - local host_routes = {} - local only_uri_routes = {} host_router = nil routes = routes or {} @@ -300,35 +303,93 @@ local function incremental_operate_radixtree(routes) end local sync_tb = apisix_router.sync_tb - local op, cur_rt, lst_rt, err + local op, route, last_route, err local router_opts = { no_param_match = true } - for k, v in pairs(sync_tb) do + + event.push(event.CONST.BUILD_ROUTER, routes) + for k, _ in pairs(sync_tb) do op = sync_tb[k]["op"] - cur_rt = sync_tb[k]["cur_route"] - lst_rt = sync_tb[k]["last_route"] - - if op == "update" then - core.log.notice("update routes watched from etcd into radixtree.", json.encode(cur_rt)) - err = uri_router:update_route(lst_rt, cur_rt, router_opts) - if err ~= nil then - core.log.error("update a route into radixtree failed.", json.encode(cur_rt), err) + route = sync_tb[k]["cur_route"] + last_route = sync_tb[k]["last_route"] + + if route then + local route_opt, pre_route_opt = {}, {} + local all_hosts = {} + local hosts, pre_hosts = nil, nil + local rdx_r = {} + local pre_rdx_r = {} + local op = {add={}, upd={}, del={}} + + local status = table.try_read_attr(route, "value", "status") + if status and status == 0 then return end - elseif op == "create" then - core.log.notice("create routes watched from etcd into radixtree.", json.encode(cur_rt)) - err = uri_router:add_route(cur_rt, router_opts) - if err ~= nil then - core.log.error("add routes into radixtree failed.", json.encode(cur_rt), err) - return + + push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_r, last_route, pre_rdx_r) + + hosts = all_hosts["host"] + if hosts ~= nil then + for _, h in ipairs(hosts) do + local host_rev = h:reverse() + local routes = host_routes[host_rev] + local sub_router = router.new(routes) + route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = empty_func, + } + end end - elseif op == "delete" then - core.log.notice("delete routes watched from etcd into radixtree.", json.encode(cur_rt)) - err = uri_router:delete_route(lst_rt, router_opts) - if err ~= nil then - core.log.error("delete a route into radixtree failed.", json.encode(cur_rt), err) - return + + pre_hosts = all_hosts["pre_host"] + if pre_hosts ~= nil then + for _, h in ipairs(pre_hosts) do + local host_rev = h:reverse() + pre_route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = empty_func, + handler = empty_func, + } + end + end + + for k, v in pairs(op) do + if k == "add" then + for _, j in ipairs(v) do + core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + host_router:add_route(r_opt, router_opts) + end + elseif k == "upd" then + for _, j in ipairs(v) do + core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + host_router:update_route(r_opt, r_opt, router_opts) + end + elseif k == "del" then + for _, j in ipairs(v) do + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local pre_r_opt = pre_route_opt[j] + host_router:delete_route(pre_r_opt, router_opts) + end + end + end + + if (route.value and not hosts) and (not last_route or pre_hosts) then + core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) + only_uri_router:add_route(rdx_r, router_opts) + elseif (route.value and not hosts) and (last_route and not pre_hosts) then + core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) + only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) + elseif (last_route and not pre_hosts) and (not route.value or hosts) then + core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(last_route)) + only_uri_router:delete_route(pre_rdx_r, router_opts) end end From aab6bb16a73648e0427e37d4fff1544ddd0db3c6 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 4 Jul 2023 15:21:54 +0800 Subject: [PATCH 11/22] remove useless file --- a | 129 ------ increment_update_route.patch | 806 ----------------------------------- 2 files changed, 935 deletions(-) delete mode 100644 a delete mode 100644 increment_update_route.patch diff --git a/a b/a deleted file mode 100644 index fec84e79a4a8..000000000000 --- a/a +++ /dev/null @@ -1,129 +0,0 @@ -diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua -index 369a54db..c66b27d5 100644 ---- a/apisix/http/router/radixtree_host_uri.lua -+++ b/apisix/http/router/radixtree_host_uri.lua -@@ -34,14 +34,6 @@ local clear_tab = base.clear_tab - local _M = {version = 0.1} - - --local function tab_cpy(t1, t2) -- t1 = {} -- for k, v in pairs(t2) do -- t1[k] = v -- end --end -- -- - function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) - if type(route) ~= "table" then - return -@@ -266,8 +258,8 @@ function _M.create_radixtree_router(routes) - -- check the status - if not status or status == 1 then - _M.push_host_router(route, host_routes, only_uri_routes) -- end -- end -+ end -+ end - - -- create router: host_router - local host_router_routes = {} -@@ -282,9 +274,9 @@ function _M.create_radixtree_router(routes) - end, - handler = function (api_ctx, match_opts) - api_ctx.real_curr_req_matched_host = match_opts.matched._path -- end -- }) -- end -+ end -+ }) -+ end - - _M.host_routes = host_routes - -@@ -296,7 +288,7 @@ function _M.create_radixtree_router(routes) - - -- create router: only_uri_router - _M.only_uri_router = router.new(only_uri_routes) -- -+ - return true - end - -diff --git a/apisix/router.lua b/apisix/router.lua -index ec262e74..b8d6f2ea 100644 ---- a/apisix/router.lua -+++ b/apisix/router.lua -@@ -53,6 +53,10 @@ local function filter(route, pre_route_or_size, obj) - end - - core.log.info("filter route: ", core.json.delay_encode(route, true)) -+ --filter route from other config source -+ if not obj then -+ return -+ end - - --load_full_data()'s filter() goes here. create radixtree while etcd compacts - local conf = core.config.local_conf() -@@ -69,7 +73,7 @@ local function filter(route, pre_route_or_size, obj) - no_param_match = false - } - end -- -+ - if type(pre_route_or_size) == "number" then - if pre_route_or_size == #obj.values then - routes_obj = obj -@@ -83,6 +87,9 @@ local function filter(route, pre_route_or_size, obj) - end - - _M.uri_router = uri_router -+ if not first_route then -+ first_route = true -+ end - end - - return -@@ -159,7 +166,7 @@ local function filter(route, pre_route_or_size, obj) - remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, - vars = pre_route_or_size.value.vars - } -- -+ - if route.value then - --update route - core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) -@@ -201,8 +208,11 @@ local function filter(route, pre_route_or_size, obj) - event.push(event.CONST.BUILD_ROUTER, routes_obj.values) - core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) - host_uri.create_radixtree_router(routes_obj.values) -+ if not first_route then -+ first_route = true -+ end - end -- -+ - return - end - -@@ -231,7 +241,6 @@ local function filter(route, pre_route_or_size, obj) - return - end - -- -- to be confirm??? assign variable of other module. - host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) - - hosts = all_hosts["host"] -diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec -index ab3bee75..a383cbb4 100644 ---- a/rockspec/apisix-master-0.rockspec -+++ b/rockspec/apisix-master-0.rockspec -@@ -45,7 +45,7 @@ dependencies = { - "lua-resty-cookie = 0.1.0", - "lua-resty-session = 3.10", - "opentracing-openresty = 0.1", -- "lua-resty-radixtree = 2.8.2", -+ "lua-resty-radixtree = 2.8.3", - "lua-protobuf = 0.4.1", - "lua-resty-openidc = 1.7.5", - "luafilesystem = 1.7.0-2", diff --git a/increment_update_route.patch b/increment_update_route.patch deleted file mode 100644 index c03a2b8eb623..000000000000 --- a/increment_update_route.patch +++ /dev/null @@ -1,806 +0,0 @@ -commit cfddd8b6489f1390de71d6853390ab7f1d841297 -Author: ranxuxin001 -Date: Mon Jun 19 11:39:55 2023 +0800 - - increment route update for radixtree host uri, radixtree uri and radixtree uri with parameter - -diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua -index ecb76270..61113506 100644 ---- a/apisix/core/config_etcd.lua -+++ b/apisix/core/config_etcd.lua -@@ -499,7 +499,7 @@ local function load_full_data(self, dir_res, headers) - item.clean_handlers = {} - - if self.filter then -- self.filter(item) -+ self.filter(item, 1, self) - end - end - -@@ -551,7 +551,7 @@ local function load_full_data(self, dir_res, headers) - item.clean_handlers = {} - - if self.filter then -- self.filter(item) -+ self.filter(item, #values, self) - end - end - -@@ -695,9 +695,10 @@ local function sync_data(self) - return false - end - -+ local pre_val - local pre_index = self.values_hash[key] - if pre_index then -- local pre_val = self.values[pre_index] -+ pre_val = self.values[pre_index] - if pre_val then - config_util.fire_all_clean_handlers(pre_val) - end -@@ -755,7 +756,7 @@ local function sync_data(self) - -- /plugins' filter need to known self.values when it is called - -- so the filter should be called after self.values set. - if self.filter then -- self.filter(res) -+ self.filter(res, pre_val, self) - end - - self.conf_version = self.conf_version + 1 -diff --git a/apisix/http/route.lua b/apisix/http/route.lua -index d475646b..2feab788 100644 ---- a/apisix/http/route.lua -+++ b/apisix/http/route.lua -@@ -73,6 +73,7 @@ function _M.create_radixtree_uri_router(routes, uri_routes, with_parameter) - core.log.info("insert uri route: ", - core.json.delay_encode(route.value, true)) - core.table.insert(uri_routes, { -+ id = route.value.id, - paths = route.value.uris or route.value.uri, - methods = route.value.methods, - priority = route.value.priority, -diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua -index 532576e5..369a54db 100644 ---- a/apisix/http/router/radixtree_host_uri.lua -+++ b/apisix/http/router/radixtree_host_uri.lua -@@ -27,20 +27,28 @@ local loadstring = loadstring - local pairs = pairs - local cached_router_version - local cached_service_version --local host_router --local only_uri_router -+local base = require("resty.core.base") -+local clear_tab = base.clear_tab - - - local _M = {version = 0.1} - - --local function push_host_router(route, host_routes, only_uri_routes) -+local function tab_cpy(t1, t2) -+ t1 = {} -+ for k, v in pairs(t2) do -+ t1[k] = v -+ end -+end -+ -+ -+function _M.push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) - if type(route) ~= "table" then - return - end - - local filter_fun, err -- if route.value.filter_func then -+ if route.value and route.value.filter_func then - filter_fun, err = loadstring( - "return " .. route.value.filter_func, - "router#" .. route.value.id) -@@ -53,67 +61,213 @@ local function push_host_router(route, host_routes, only_uri_routes) - filter_fun = filter_fun() - end - -- local hosts = route.value.hosts -- if not hosts then -- if route.value.host then -- hosts = {route.value.host} -- elseif route.value.service_id then -- local service = service_fetch(route.value.service_id) -- if not service then -- core.log.error("failed to fetch service configuration by ", -- "id: ", route.value.service_id) -- -- we keep the behavior that missing service won't affect the route matching -- else -- hosts = service.value.hosts -+ local radixtree_route, pre_radixtree_route = {}, {} -+ local hosts -+ if route and route.value then -+ hosts = route.value.hosts -+ if not hosts then -+ if route.value.host then -+ hosts = {route.value.host} -+ elseif route.value.service_id then -+ local service = service_fetch(route.value.service_id) -+ if not service then -+ core.log.error("failed to fetch service configuration by ", -+ "id: ", route.value.service_id) -+ -- we keep the behavior that missing service won't affect the route matching -+ else -+ hosts = service.value.hosts -+ end -+ end -+ end -+ -+ radixtree_route = { -+ id = route.value.id, -+ paths = route.value.uris or route.value.uri, -+ methods = route.value.methods, -+ priority = route.value.priority, -+ remote_addrs = route.value.remote_addrs -+ or route.value.remote_addr, -+ vars = route.value.vars, -+ filter_fun = filter_fun, -+ handler = function (api_ctx, match_opts) -+ api_ctx.matched_params = nil -+ api_ctx.matched_route = route -+ api_ctx.curr_req_matched = match_opts.matched -+ api_ctx.real_curr_req_matched_path = match_opts.matched._path -+ end -+ } -+ -+ if rdx_rt ~= nil then -+ for k, v in pairs(radixtree_route) do -+ rdx_rt[k] = v - end - end - end - -- local radixtree_route = { -- paths = route.value.uris or route.value.uri, -- methods = route.value.methods, -- priority = route.value.priority, -- remote_addrs = route.value.remote_addrs -- or route.value.remote_addr, -- vars = route.value.vars, -- filter_fun = filter_fun, -- handler = function (api_ctx, match_opts) -- api_ctx.matched_params = nil -- api_ctx.matched_route = route -- api_ctx.curr_req_matched = match_opts.matched -- api_ctx.real_curr_req_matched_path = match_opts.matched._path -- end -- } -- -- if hosts == nil then -+ if hosts == nil and all_hosts == nil then - core.table.insert(only_uri_routes, radixtree_route) - return - end - -- for i, host in ipairs(hosts) do -- local host_rev = host:reverse() -- if not host_routes[host_rev] then -- host_routes[host_rev] = {radixtree_route} -+ local pre_hosts -+ if pre_route and pre_route.value then -+ pre_hosts = pre_route.value.hosts -+ if not pre_hosts then -+ if pre_route.value.host then -+ pre_hosts = {pre_route.value.host} -+ elseif pre_route.value.service_id then -+ local service = service_fetch(pre_route.value.service_id) -+ if not service then -+ core.log.error("failed to fetch service configuration by ", -+ "id: ", pre_route.value.service_id) -+ -- we keep the behavior that missing service won't affect the route matching -+ else -+ pre_hosts = service.value.hosts -+ end -+ end -+ end -+ -+ pre_radixtree_route = { -+ id = pre_route.value.id, -+ paths = pre_route.value.uris or pre_route.value.uri, -+ methods = pre_route.value.methods, -+ priority = pre_route.value.priority, -+ remote_addrs = pre_route.value.remote_addrs -+ or pre_route.value.remote_addr, -+ vars = pre_route.value.vars, -+ filter_fun = filter_fun, -+ handler = function (api_ctx, match_opts) -+ api_ctx.matched_params = nil -+ api_ctx.matched_route = pre_route -+ api_ctx.curr_req_matched = match_opts.matched -+ api_ctx.real_curr_req_matched_path = match_opts.matched._path -+ end -+ } -+ -+ if pre_rdx_rt ~= nil then -+ for k, v in pairs(pre_radixtree_route) do -+ pre_rdx_rt[k] = v -+ end -+ end -+ end -+ -+ if all_hosts ~= nil then -+ all_hosts["host"] = hosts -+ all_hosts["pre_host"] = pre_hosts -+ end -+ -+ local pre_t = {} -+ if pre_hosts then -+ for i, h in ipairs(pre_hosts) do -+ local rev_h = h:reverse() -+ pre_t[rev_h] = 1 -+ end -+ end -+ -+ local t = {} -+ if hosts then -+ for i, h in ipairs(hosts) do -+ local rev_h = h:reverse() -+ t[rev_h] = 1 -+ end -+ end -+ -+ local comm = {} -+ for k, v in pairs(pre_t) do -+ if t[k] ~= nil then -+ tab_insert(comm, k) -+ pre_t[k] = nil -+ t[k] = nil -+ end -+ end -+ -+ for _, j in ipairs(comm) do -+ local routes = host_routes[j] -+ if routes == nil then -+ core.log.error("no routes array for reverse host in the map.", j) -+ return -+ end -+ -+ local found = false -+ for i, r in ipairs(routes) do -+ if r.id == radixtree_route.id then -+ routes[i] = radixtree_route -+ found = true -+ if op then -+ table.insert(op["upd"], j) -+ end -+ break -+ end -+ end -+ -+ if not found then -+ core.log.error("cannot find the route in common host's table.", j, radixtree_route.id) -+ return -+ end -+ end -+ -+ for k, v in pairs(pre_t) do -+ local routes = host_routes[k] -+ if routes == nil then -+ core.log.error("no routes array for reverse host in the map.", k) -+ return -+ end -+ -+ local found = false -+ for i, r in ipairs(routes) do -+ if r.id == pre_radixtree_route.id then -+ table.remove(routes, i) -+ found = true -+ break -+ end -+ end -+ -+ if not found then -+ core.log.error("cannot find the route in previous host's table.", k, pre_radixtree_route.id) -+ return -+ end -+ -+ if #routes == 0 then -+ host_routes[k] = nil -+ if op then -+ table.insert(op["del"], k) -+ end -+ else -+ if op then -+ table.insert(op["upd"], k) -+ end -+ end -+ end -+ -+ for k, v in pairs(t) do -+ local routes = host_routes[k] -+ if routes == nil then -+ host_routes[k] = {radixtree_route} -+ if op then -+ table.insert(op["add"], k) -+ end - else -- tab_insert(host_routes[host_rev], radixtree_route) -+ table.insert(routes, radixtree_route) -+ if op then -+ table.insert(op["upd"], k) -+ end - end - end - end - - --local function create_radixtree_router(routes) -+function _M.create_radixtree_router(routes) - local host_routes = {} - local only_uri_routes = {} -- host_router = nil - routes = routes or {} - - for _, route in ipairs(routes) do - local status = core.table.try_read_attr(route, "value", "status") - -- check the status - if not status or status == 1 then -- push_host_router(route, host_routes, only_uri_routes) -- end -- end -+ _M.push_host_router(route, host_routes, only_uri_routes) -+ end -+ end - - -- create router: host_router - local host_router_routes = {} -@@ -121,45 +275,42 @@ local function create_radixtree_router(routes) - local sub_router = router.new(routes) - - core.table.insert(host_router_routes, { -+ id = 1, - paths = host_rev, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = function (api_ctx, match_opts) - api_ctx.real_curr_req_matched_host = match_opts.matched._path -- end -- }) -- end -+ end -+ }) -+ end -+ -+ _M.host_routes = host_routes - - event.push(event.CONST.BUILD_ROUTER, routes) - - if #host_router_routes > 0 then -- host_router = router.new(host_router_routes) -+ _M.host_router = router.new(host_router_routes) - end - - -- create router: only_uri_router -- only_uri_router = router.new(only_uri_routes) -+ _M.only_uri_router = router.new(only_uri_routes) -+ - return true - end - - - local match_opts = {} - function _M.match(api_ctx) -- local user_routes = _M.user_routes -- local _, service_version = get_services() -- if not cached_router_version or cached_router_version ~= user_routes.conf_version -- or not cached_service_version or cached_service_version ~= service_version -- then -- create_radixtree_router(user_routes.values) -- cached_router_version = user_routes.conf_version -- cached_service_version = service_version -- end -- - return _M.matching(api_ctx) - end - - - function _M.matching(api_ctx) -+ local host_router = _M.host_router -+ local only_uri_router = _M.only_uri_router -+ - core.log.info("route match mode: radixtree_host_uri") - - core.table.clear(match_opts) -diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua -index 6e546364..4c3867c4 100644 ---- a/apisix/http/router/radixtree_uri.lua -+++ b/apisix/http/router/radixtree_uri.lua -@@ -17,43 +17,21 @@ - local require = require - local core = require("apisix.core") - local base_router = require("apisix.http.route") --local get_services = require("apisix.http.service").services --local cached_router_version --local cached_service_version -+local router = require("apisix.router") - - - local _M = {version = 0.2} - - -- local uri_routes = {} -- local uri_router - local match_opts = {} - function _M.match(api_ctx) -- local user_routes = _M.user_routes -- local _, service_version = get_services() -- if not cached_router_version or cached_router_version ~= user_routes.conf_version -- or not cached_service_version or cached_service_version ~= service_version -- then -- uri_router = base_router.create_radixtree_uri_router(user_routes.values, -- uri_routes, false) -- cached_router_version = user_routes.conf_version -- cached_service_version = service_version -- end -- -- if not uri_router then -- core.log.error("failed to fetch valid `uri` router: ") -- return true -- end -- - return _M.matching(api_ctx) - end - - - function _M.matching(api_ctx) - core.log.info("route match mode: radixtree_uri") -- -- return base_router.match_uri(uri_router, match_opts, api_ctx) -+ return base_router.match_uri(router.uri_router, match_opts, api_ctx) - end - -- - return _M -diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua -index 4bf7f3eb..e36008c8 100644 ---- a/apisix/http/router/radixtree_uri_with_parameter.lua -+++ b/apisix/http/router/radixtree_uri_with_parameter.lua -@@ -17,43 +17,21 @@ - local require = require - local core = require("apisix.core") - local base_router = require("apisix.http.route") --local get_services = require("apisix.http.service").services --local cached_router_version --local cached_service_version -+local router = require("apisix.router") - - - local _M = {} - - -- local uri_routes = {} -- local uri_router - local match_opts = {} - function _M.match(api_ctx) -- local user_routes = _M.user_routes -- local _, service_version = get_services() -- if not cached_router_version or cached_router_version ~= user_routes.conf_version -- or not cached_service_version or cached_service_version ~= service_version -- then -- uri_router = base_router.create_radixtree_uri_router(user_routes.values, -- uri_routes, true) -- cached_router_version = user_routes.conf_version -- cached_service_version = service_version -- end -- -- if not uri_router then -- core.log.error("failed to fetch valid `uri_with_parameter` router: ") -- return true -- end -- - return _M.matching(api_ctx) - end - - - function _M.matching(api_ctx) - core.log.info("route match mode: radixtree_uri_with_parameter") -- -- return base_router.match_uri(uri_router, match_opts, api_ctx) -+ return base_router.match_uri(router.uri_router, match_opts, api_ctx) - end - -- - return _M -diff --git a/apisix/router.lua b/apisix/router.lua -index 2fd14917..ec262e74 100644 ---- a/apisix/router.lua -+++ b/apisix/router.lua -@@ -22,31 +22,281 @@ local plugin_checker = require("apisix.plugin").plugin_checker - local str_lower = string.lower - local error = error - local ipairs = ipairs -+local sub_str = string.sub -+local table = require("apisix.core.table") -+local json = require("apisix.core.json") -+local router_util = require("apisix.utils.router") -+local tab_insert = table.insert -+local event = require("apisix.core.event") - - - local _M = {version = 0.3} - -+local function empty_func() end -+local routes_obj, first_route - --local function filter(route) -+local function filter(route, pre_route_or_size, obj) - route.orig_modifiedIndex = route.modifiedIndex - route.update_count = 0 - - route.has_domain = false -- if not route.value then -- return -+ if route.value then -+ if route.value.host then -+ route.value.host = str_lower(route.value.host) -+ elseif route.value.hosts then -+ for i, v in ipairs(route.value.hosts) do -+ route.value.hosts[i] = str_lower(v) -+ end -+ end -+ -+ apisix_upstream.filter_upstream(route.value.upstream, route) - end - -- if route.value.host then -- route.value.host = str_lower(route.value.host) -- elseif route.value.hosts then -- for i, v in ipairs(route.value.hosts) do -- route.value.hosts[i] = str_lower(v) -+ core.log.info("filter route: ", core.json.delay_encode(route, true)) -+ -+ --load_full_data()'s filter() goes here. create radixtree while etcd compacts -+ local conf = core.config.local_conf() -+ if conf.apisix.router.http == "radixtree_uri" or conf.apisix.router.http == "radixtree_uri_with_parameter" then -+ local router_opts -+ local with_parameter = false -+ if conf.apisix.router.http == "radixtree_uri" then -+ router_opts = { -+ no_param_match = true -+ } -+ else -+ with_parameter = true -+ router_opts = { -+ no_param_match = false -+ } - end -- end -+ -+ if type(pre_route_or_size) == "number" then -+ if pre_route_or_size == #obj.values then -+ routes_obj = obj -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ local uri_routes = {} -+ core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) -+ local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) -+ if not uri_router then -+ core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) -+ return -+ end - -- apisix_upstream.filter_upstream(route.value.upstream, route) -+ _M.uri_router = uri_router -+ end - -- core.log.info("filter route: ", core.json.delay_encode(route, true)) -+ return -+ end -+ -+ if not first_route then -+ routes_obj = obj -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ local uri_routes = {} -+ core.log.notice("create radixtree uri for the first route income.") -+ local uri_router = http_route.create_radixtree_uri_router(routes_obj.values, uri_routes, with_parameter) -+ if not uri_router then -+ core.log.error("create radixtree in init worker phase failed.", #routes_obj.values) -+ return -+ end -+ -+ _M.uri_router = uri_router -+ first_route = true -+ return -+ end -+ -+ --only sync_data()'s filter() goes here -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ -+ local router_module = require("apisix.router") -+ local radixtree_obj = router_module.uri_router -+ -+ local cur_route -+ if route.value then -+ local status = table.try_read_attr(route, "value", "status") -+ if status and status == 0 then -+ return -+ end -+ -+ local filter_fun, err -+ if route.value.filter_func then -+ filter_fun, err = loadstring( -+ "return " .. route.value.filter_func, -+ "router#" .. route.value.id -+ ) -+ if not filter_fun then -+ core.log.error("failed to load filter function: ", err, " route id", route.value.id) -+ return -+ end -+ -+ filter_fun = filter_fun() -+ end -+ -+ cur_route = { -+ id = route.value.id, -+ paths = route.value.uris or route.value.uri, -+ methods = route.value.methods, -+ priority = route.value.priority, -+ hosts = route.value.hosts or route.value.host, -+ remote_addrs = route.value.remote_addrs or route.value.remote_addr, -+ vars = route.value.vars, -+ filter_fun = filter_fun, -+ handler = function(api_ctx, match_opts) -+ api_ctx.matched_params = nil -+ api_ctx.matched_route = route -+ api_ctx.curr_req_matched = match_opts.matched -+ end -+ } -+ end -+ -+ local err -+ if pre_route_or_size then -+ local last_route = { -+ id = pre_route_or_size.value.id, -+ paths = pre_route_or_size.value.uris or pre_route_or_size.value.uri, -+ methods = pre_route_or_size.value.methods, -+ priority = pre_route_or_size.value.priority, -+ hosts = pre_route_or_size.value.hosts or pre_route_or_size.value.host, -+ remote_addrs = pre_route_or_size.value.remote_addrs or pre_route_or_size.value.remote_addr, -+ vars = pre_route_or_size.value.vars -+ } -+ -+ if route.value then -+ --update route -+ core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) -+ err = radixtree_obj:update_route(last_route, cur_route, router_opts) -+ if err ~= nil then -+ core.log.error("update a route into radixtree failed.", json.encode(route), err) -+ return -+ end -+ else -+ --delete route -+ core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) -+ err = radixtree_obj:delete_route(last_route, router_opts) -+ if err ~= nil then -+ core.log.error("delete a route into radixtree failed.", json.encode(route), err) -+ return -+ end -+ end -+ elseif route.value then -+ --create route -+ core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) -+ err = radixtree_obj:add_route(cur_route, router_opts) -+ if err ~= nil then -+ core.log.error("add routes into radixtree failed.", json.encode(route), err) -+ return -+ end -+ else -+ core.log.error("invalid operation type for a route.", route.key) -+ return -+ end -+ elseif conf.apisix.router.http == "radixtree_host_uri" then -+ local router_opts = { -+ no_param_match = true -+ } -+ -+ local host_uri = require("apisix.http.router.radixtree_host_uri") -+ if type(pre_route_or_size) == "number" then -+ if pre_route_or_size == #obj.values then -+ routes_obj = obj -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ core.log.notice("create radixtree uri after load_full_data.", #routes_obj.values) -+ host_uri.create_radixtree_router(routes_obj.values) -+ end -+ -+ return -+ end -+ -+ if not first_route then -+ routes_obj = obj -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ core.log.notice("create radixtree uri for the first route income.") -+ host_uri.create_radixtree_router(routes_obj.values) -+ first_route = true -+ return -+ end -+ -+ event.push(event.CONST.BUILD_ROUTER, routes_obj.values) -+ -+ local only_uri_routes = {} -+ local route_opt, pre_route_opt = {}, {} -+ local all_hosts = {} -+ local hosts, pre_hosts = nil, nil -+ local rdx_r = {} -+ local pre_rdx_r = {} -+ local op = {add={}, upd={}, del={}} -+ -+ -+ local status = table.try_read_attr(route, "value", "status") -+ if status and status == 0 then -+ return -+ end -+ -+ -- to be confirm??? assign variable of other module. -+ host_uri.push_host_router(route, host_uri.host_routes, only_uri_routes, all_hosts, op, rdx_r, pre_route_or_size, pre_rdx_r) -+ -+ hosts = all_hosts["host"] -+ if hosts ~= nil then -+ for _, h in ipairs(hosts) do -+ local host_rev = h:reverse() -+ local routes = host_uri.host_routes[host_rev] -+ local sub_router = router_util.new(routes) -+ route_opt[host_rev] = { -+ id = 1, -+ paths = host_rev, -+ filter_fun = function(vars, opts, ...) -+ return sub_router:dispatch(vars.uri, opts, ...) -+ end, -+ handler = empty_func, -+ } -+ end -+ end -+ -+ pre_hosts = all_hosts["pre_host"] -+ if pre_hosts ~= nil then -+ for _, h in ipairs(pre_hosts) do -+ local host_rev = h:reverse() -+ pre_route_opt[host_rev] = { -+ id = 1, -+ paths = host_rev, -+ filter_fun = empty_func, -+ handler = empty_func, -+ } -+ end -+ end -+ -+ for k, v in pairs(op) do -+ if k == "add" then -+ for _, j in ipairs(v) do -+ core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) -+ local r_opt = route_opt[j] -+ host_uri.host_router:add_route(r_opt, router_opts) -+ end -+ elseif k == "upd" then -+ for _, j in ipairs(v) do -+ core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) -+ local r_opt = route_opt[j] -+ host_uri.host_router:update_route(r_opt, r_opt, router_opts) -+ end -+ elseif k == "del" then -+ for _, j in ipairs(v) do -+ core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) -+ local pre_r_opt = pre_route_opt[j] -+ host_uri.host_router:delete_route(pre_r_opt, router_opts) -+ end -+ end -+ end -+ -+ if (route.value and not hosts) and (not pre_route_or_size or pre_hosts) then -+ core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) -+ host_uri.only_uri_router:add_route(rdx_r, router_opts) -+ elseif (route.value and not hosts) and (pre_route_or_size and not pre_hosts) then -+ core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) -+ host_uri.only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) -+ elseif (pre_route_or_size and not pre_hosts) and (not route.value or hosts) then -+ core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(pre_route_or_size)) -+ host_uri.only_uri_router:delete_route(pre_rdx_r, router_opts) -+ end -+ end - end - - From 46069c7a3df8c90cba462efa80d5af30ff83f3fa Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 4 Jul 2023 16:47:32 +0800 Subject: [PATCH 12/22] tested host uri done --- apisix/http/router/radixtree_host_uri.lua | 196 ++++++++++-------- apisix/http/router/radixtree_uri.lua | 2 +- .../router/radixtree_uri_with_parameter.lua | 2 +- apisix/router.lua | 51 +++-- 4 files changed, 149 insertions(+), 102 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 37ae43df0c9f..58b351518470 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -40,23 +40,11 @@ local _M = {version = 0.1} local function empty_func() end -local function push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) - if type(route) ~= "table" then - return - end - local filter_fun, err - if route.value and route.value.filter_func then - filter_fun, err = loadstring( - "return " .. route.value.filter_func, - "router#" .. route.value.id) - if not filter_fun then - core.log.error("failed to load filter function: ", err, - " route id: ", route.value.id) - return - end - filter_fun = filter_fun() +local function push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) + if route and type(route) ~= "table" then + return end local radixtree_route, pre_radixtree_route = {}, {} @@ -78,6 +66,20 @@ local function push_host_router(route, host_routes, only_uri_routes, all_hosts, end end + local filter_fun, err + if route.value and route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id) + if not filter_fun then + core.log.error("failed to load filter function: ", err, + " route id: ", route.value.id) + return + end + + filter_fun = filter_fun() + end + radixtree_route = { id = route.value.id, paths = route.value.uris or route.value.uri, @@ -125,6 +127,20 @@ local function push_host_router(route, host_routes, only_uri_routes, all_hosts, end end + local filter_fun, err + if pre_route.value and pre_route.value.filter_func then + filter_fun, err = loadstring( + "return " .. pre_route.value.filter_func, + "router#" .. pre_route.value.id) + if not filter_fun then + core.log.error("failed to load filter function: ", err, + " route id: ", pre_route.value.id) + return + end + + filter_fun = filter_fun() + end + pre_radixtree_route = { id = pre_route.value.id, paths = pre_route.value.uris or pre_route.value.uri, @@ -295,103 +311,115 @@ end local function incremental_operate_radixtree(routes) + local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then - core.log.notice("create radixtree uri after load_full_data.", #routes) + core.log.error("######11111#############", #routes) + core.log.notice("create object of radixtree host uri after load_full_data or init.", #routes) create_radixtree_router(routes) apisix_router.need_create_radixtree = false + for k, _ in pairs(sync_tb) do + sync_tb[k] = nil + end return end - local sync_tb = apisix_router.sync_tb - local op, route, last_route, err + core.log.error("######22222222#############") + + local operate, route, last_route, err local router_opts = { no_param_match = true } event.push(event.CONST.BUILD_ROUTER, routes) for k, _ in pairs(sync_tb) do - op = sync_tb[k]["op"] + operate = sync_tb[k]["op"] route = sync_tb[k]["cur_route"] last_route = sync_tb[k]["last_route"] if route then - local route_opt, pre_route_opt = {}, {} - local all_hosts = {} - local hosts, pre_hosts = nil, nil - local rdx_r = {} - local pre_rdx_r = {} - local op = {add={}, upd={}, del={}} - local status = table.try_read_attr(route, "value", "status") if status and status == 0 then return end + end - push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_r, last_route, pre_rdx_r) - - hosts = all_hosts["host"] - if hosts ~= nil then - for _, h in ipairs(hosts) do - local host_rev = h:reverse() - local routes = host_routes[host_rev] - local sub_router = router.new(routes) - route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = empty_func, - } - end + local route_opt, pre_route_opt = {}, {} + local all_hosts = {} + local hosts, pre_hosts = nil, nil + local rdx_r = {} + local pre_rdx_r = {} + local op = {add={}, upd={}, del={}} + + + + push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_r, last_route, pre_rdx_r) + + hosts = all_hosts["host"] + if hosts ~= nil then + for _, h in ipairs(hosts) do + local host_rev = h:reverse() + local routes = host_routes[host_rev] + local sub_router = router.new(routes) + route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = empty_func, + } end + end - pre_hosts = all_hosts["pre_host"] - if pre_hosts ~= nil then - for _, h in ipairs(pre_hosts) do - local host_rev = h:reverse() - pre_route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = empty_func, - handler = empty_func, - } - end + pre_hosts = all_hosts["pre_host"] + if pre_hosts ~= nil then + for _, h in ipairs(pre_hosts) do + local host_rev = h:reverse() + pre_route_opt[host_rev] = { + id = 1, + paths = host_rev, + filter_fun = empty_func, + handler = empty_func, + } end + end - for k, v in pairs(op) do - if k == "add" then - for _, j in ipairs(v) do - core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local r_opt = route_opt[j] - host_router:add_route(r_opt, router_opts) - end - elseif k == "upd" then - for _, j in ipairs(v) do - core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local r_opt = route_opt[j] - host_router:update_route(r_opt, r_opt, router_opts) - end - elseif k == "del" then - for _, j in ipairs(v) do - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) - local pre_r_opt = pre_route_opt[j] - host_router:delete_route(pre_r_opt, router_opts) - end + for k, v in pairs(op) do + if k == "add" then + for _, j in ipairs(v) do + core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221add!!!", k, r_opt.paths) + host_router:add_route(r_opt, router_opts) + end + elseif k == "upd" then + for _, j in ipairs(v) do + core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local r_opt = route_opt[j] + core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221upd!!!", k, r_opt.paths) + host_router:update_route(r_opt, r_opt, router_opts) + end + elseif k == "del" then + for _, j in ipairs(v) do + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + local pre_r_opt = pre_route_opt[j] + core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221del!!!", k, pre_r_opt.paths) + host_router:delete_route(pre_r_opt, router_opts) end end + end - if (route.value and not hosts) and (not last_route or pre_hosts) then - core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) - only_uri_router:add_route(rdx_r, router_opts) - elseif (route.value and not hosts) and (last_route and not pre_hosts) then - core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) - only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) - elseif (last_route and not pre_hosts) and (not route.value or hosts) then - core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(last_route)) - only_uri_router:delete_route(pre_rdx_r, router_opts) - end + if (route and (route.value and not hosts)) and (not last_route or pre_hosts) then + core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) + only_uri_router:add_route(rdx_r, router_opts) + elseif (route and (route.value and not hosts)) and (last_route and not pre_hosts) then + core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) + only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) + elseif (last_route and not pre_hosts) and (not (route and route.value) or hosts) then + core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(last_route)) + only_uri_router:delete_route(pre_rdx_r, router_opts) end + sync_tb[k] = nil end @@ -429,6 +457,7 @@ function _M.matching(api_ctx) if host_router then local host_uri = api_ctx.var.host + core.log.error("@@@@@@@@@@@@@@@@", host_uri:reverse()) local ok = host_router:dispatch(host_uri:reverse(), match_opts, api_ctx, match_opts) if ok then if api_ctx.real_curr_req_matched_path then @@ -443,6 +472,7 @@ function _M.matching(api_ctx) end end + core.log.error("@@@@@@@@@@@@@@@@",api_ctx.var.uri) local ok = only_uri_router:dispatch(api_ctx.var.uri, match_opts, api_ctx, match_opts) return ok end diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 11ae64ad3930..912f271d5299 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -18,7 +18,7 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") local get_services = require("apisix.http.service").services -local apisix_router = require("apisix/router") +local apisix_router = require("apisix.router") local json = require("apisix.core.json") local cached_router_version local cached_service_version diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index e2e4af8f7fc3..018763a71764 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -18,7 +18,7 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") local get_services = require("apisix.http.service").services -local apisix_router = require("apisix/router") +local apisix_router = require("apisix.router") local json = require("apisix.core.json") local table = require("apisix.core.table") local cached_router_version diff --git a/apisix/router.lua b/apisix/router.lua index feee6066559f..3e241957716c 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -24,30 +24,41 @@ local error = error local ipairs = ipairs local table = require("apisix.core.table") local json = require("apisix.core.json") +local sub_str = string.sub local _M = {version = 0.3} +_M.need_create_radixtree = true + + +local function short_key(self, str) + return sub_str(str, #self.key + 2) +end + local function filter(route, pre_route_or_size, obj) route.orig_modifiedIndex = route.modifiedIndex route.update_count = 0 route.has_domain = false - if not route.value then - return - end - - if route.value.host then - route.value.host = str_lower(route.value.host) - elseif route.value.hosts then - for i, v in ipairs(route.value.hosts) do - route.value.hosts[i] = str_lower(v) + if route.value then + if route.value.host then + route.value.host = str_lower(route.value.host) + elseif route.value.hosts then + for i, v in ipairs(route.value.hosts) do + route.value.hosts[i] = str_lower(v) + end end + + apisix_upstream.filter_upstream(route.value.upstream, route) end - apisix_upstream.filter_upstream(route.value.upstream, route) + core.log.info("filter route: ", core.json.delay_encode(route, true)) + if not obj then + return + end --save sync route and operation type into a map if type(pre_route_or_size) == "number" then if pre_route_or_size == #obj.values then @@ -56,8 +67,14 @@ local function filter(route, pre_route_or_size, obj) return end + local key + if obj.single_item then + key = obj.key + else + key = short_key(obj, route.key) + end + local sync_tb = _M.sync_tb - local val if pre_route_or_size then if route.value then --update route @@ -72,12 +89,12 @@ local function filter(route, pre_route_or_size, obj) else --delete route core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) - if not sync_tb[route.value.id] then - sync_tb[route.value.id] = {op = "delete", last_route = pre_route_or_size} - elseif sync_tb[route.value.id]["op"] == "create" then - sync_tb[route.value.id] = nil - elseif sync_tb[route.value.id]["op"] == "update" then - sync_tb[route.value.id] = {op = "delete", last_route = sync_tb[route.value.id]["last_route"]} + if not sync_tb[key] then + sync_tb[key] = {op = "delete", last_route = pre_route_or_size} + elseif sync_tb[key]["op"] == "create" then + sync_tb[key] = nil + elseif sync_tb[key]["op"] == "update" then + sync_tb[key] = {op = "delete", last_route = sync_tb[route.value.id]["last_route"]} end end elseif route.value then From 4e167ca1bfbd1bbd04c17761351f053fbd400ac4 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 4 Jul 2023 17:00:33 +0800 Subject: [PATCH 13/22] improve codes for uri, uri_with_parameter --- apisix/http/router/radixtree_uri.lua | 5 ++++- apisix/http/router/radixtree_uri_with_parameter.lua | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 912f271d5299..d0a9de644078 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -30,13 +30,16 @@ local _M = {version = 0.2} local function incremental_operate_radixtree(routes) + local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, false) apisix_router.need_create_radixtree = false + for k, _ in pairs(sync_tb) do + sync_tb[k] = nil + end return end - local sync_tb = apisix_router.sync_tb local op, route, last_route, err local cur_tmp, last_tmp = {}, {} local router_opts = { diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index 018763a71764..65fecdc2bd10 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -32,13 +32,16 @@ local _M = {} local function incremental_operate_radixtree(routes) + local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, true) apisix_router.need_create_radixtree = false + for k, _ in pairs(sync_tb) do + sync_tb[k] = nil + end return end - local sync_tb = apisix_router.sync_tb local op, route, last_route, err local cur_tmp, last_tmp = {}, {} local router_opts = { From 2098b0db408654a34a91ee11032db7735957ba6f Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 4 Jul 2023 18:28:56 +0800 Subject: [PATCH 14/22] test radixtree uri done. --- apisix/http/router/radixtree_host_uri.lua | 10 ---------- apisix/http/router/radixtree_uri.lua | 10 ++++++---- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 58b351518470..a55d2751f12e 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -313,7 +313,6 @@ end local function incremental_operate_radixtree(routes) local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then - core.log.error("######11111#############", #routes) core.log.notice("create object of radixtree host uri after load_full_data or init.", #routes) create_radixtree_router(routes) apisix_router.need_create_radixtree = false @@ -323,8 +322,6 @@ local function incremental_operate_radixtree(routes) return end - core.log.error("######22222222#############") - local operate, route, last_route, err local router_opts = { no_param_match = true @@ -350,8 +347,6 @@ local function incremental_operate_radixtree(routes) local pre_rdx_r = {} local op = {add={}, upd={}, del={}} - - push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_r, last_route, pre_rdx_r) hosts = all_hosts["host"] @@ -389,21 +384,18 @@ local function incremental_operate_radixtree(routes) for _, j in ipairs(v) do core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local r_opt = route_opt[j] - core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221add!!!", k, r_opt.paths) host_router:add_route(r_opt, router_opts) end elseif k == "upd" then for _, j in ipairs(v) do core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local r_opt = route_opt[j] - core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221upd!!!", k, r_opt.paths) host_router:update_route(r_opt, r_opt, router_opts) end elseif k == "del" then for _, j in ipairs(v) do core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local pre_r_opt = pre_route_opt[j] - core.log.notice("!!!!!!!!!!!!!!!!!!!!11122221del!!!", k, pre_r_opt.paths) host_router:delete_route(pre_r_opt, router_opts) end end @@ -457,7 +449,6 @@ function _M.matching(api_ctx) if host_router then local host_uri = api_ctx.var.host - core.log.error("@@@@@@@@@@@@@@@@", host_uri:reverse()) local ok = host_router:dispatch(host_uri:reverse(), match_opts, api_ctx, match_opts) if ok then if api_ctx.real_curr_req_matched_path then @@ -472,7 +463,6 @@ function _M.matching(api_ctx) end end - core.log.error("@@@@@@@@@@@@@@@@",api_ctx.var.uri) local ok = only_uri_router:dispatch(api_ctx.var.uri, match_opts, api_ctx, match_opts) return ok end diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index d0a9de644078..6382c71eef50 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -20,6 +20,7 @@ local base_router = require("apisix.http.route") local get_services = require("apisix.http.service").services local apisix_router = require("apisix.router") local json = require("apisix.core.json") +local table = require("apisix.core.table") local cached_router_version local cached_service_version local uri_routes = {} @@ -32,6 +33,7 @@ local _M = {version = 0.2} local function incremental_operate_radixtree(routes) local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then + core.log.error("@@@@@@@@@@create_radixtree_uri_router@@@") uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, false) apisix_router.need_create_radixtree = false for k, _ in pairs(sync_tb) do @@ -52,7 +54,7 @@ local function incremental_operate_radixtree(routes) cur_tmp = {} last_tmp = {} - if route then + if route and route.value then local status = table.try_read_attr(route, "value", "status") if status and status == 0 then return @@ -89,7 +91,7 @@ local function incremental_operate_radixtree(routes) } end - if last_route then + if last_route and last_route.value then last_tmp = { id = last_route.value.id, paths = last_route.value.uris or last_route.value.uri, @@ -116,10 +118,10 @@ local function incremental_operate_radixtree(routes) return end elseif op == "delete" then - core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(last_route)) err = uri_router:delete_route(last_tmp, router_opts) if err ~= nil then - core.log.error("delete a route into radixtree failed.", json.encode(route), err) + core.log.error("delete a route into radixtree failed.", json.encode(last_route), err) return end end From b3a96213043b083f5d8660a5d174a48ae6653357 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Tue, 4 Jul 2023 19:09:33 +0800 Subject: [PATCH 15/22] tested uri with parameter done --- apisix/http/router/radixtree_uri.lua | 4 ++-- apisix/http/router/radixtree_uri_with_parameter.lua | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 6382c71eef50..6c80b704db83 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -27,13 +27,13 @@ local uri_routes = {} local uri_router local match_opts = {} + local _M = {version = 0.2} local function incremental_operate_radixtree(routes) local sync_tb = apisix_router.sync_tb if apisix_router.need_create_radixtree then - core.log.error("@@@@@@@@@@create_radixtree_uri_router@@@") uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, false) apisix_router.need_create_radixtree = false for k, _ in pairs(sync_tb) do @@ -45,7 +45,7 @@ local function incremental_operate_radixtree(routes) local op, route, last_route, err local cur_tmp, last_tmp = {}, {} local router_opts = { - no_param_match = false + no_param_match = true } for k, v in pairs(sync_tb) do op = sync_tb[k]["op"] diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index 65fecdc2bd10..806e7d107462 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -20,7 +20,7 @@ local base_router = require("apisix.http.route") local get_services = require("apisix.http.service").services local apisix_router = require("apisix.router") local json = require("apisix.core.json") -local table = require("apisix.core.table") +local table = require("apisix.core.table") local cached_router_version local cached_service_version local uri_routes = {} @@ -54,7 +54,7 @@ local function incremental_operate_radixtree(routes) cur_tmp = {} last_tmp = {} - if route then + if route and route.value then local status = table.try_read_attr(route, "value", "status") if status and status == 0 then return @@ -91,7 +91,7 @@ local function incremental_operate_radixtree(routes) } end - if last_route then + if last_route and last_route.value then last_tmp = { id = last_route.value.id, paths = last_route.value.uris or last_route.value.uri, @@ -118,10 +118,10 @@ local function incremental_operate_radixtree(routes) return end elseif op == "delete" then - core.log.notice("delete routes watched from etcd into radixtree.", json.encode(route)) + core.log.notice("delete routes watched from etcd into radixtree.", json.encode(last_route)) err = uri_router:delete_route(last_tmp, router_opts) if err ~= nil then - core.log.error("delete a route into radixtree failed.", json.encode(route), err) + core.log.error("delete a route into radixtree failed.", json.encode(last_route), err) return end end From 2ff51f7c7219937483684f316c8fe7e76bb04a2f Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Wed, 5 Jul 2023 12:31:23 +0800 Subject: [PATCH 16/22] add id in route format --- apisix/http/router/radixtree_host_uri.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index a55d2751f12e..b6a551a0cbfc 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -244,6 +244,7 @@ local function push_host_router(route, host_routes, only_uri_routes, all_hosts, if #routes == 0 then host_routes[k] = nil if op then + core.log.error("###################del####", k) table.insert(op["del"], k) end else @@ -288,6 +289,7 @@ local function create_radixtree_router(routes) local sub_router = router.new(routes) core.table.insert(host_router_routes, { + id = 1, paths = host_rev, filter_fun = function(vars, opts, ...) return sub_router:dispatch(vars.uri, opts, ...) @@ -394,7 +396,7 @@ local function incremental_operate_radixtree(routes) end elseif k == "del" then for _, j in ipairs(v) do - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j, pre_r_opt.id) local pre_r_opt = pre_route_opt[j] host_router:delete_route(pre_r_opt, router_opts) end @@ -411,7 +413,6 @@ local function incremental_operate_radixtree(routes) core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(last_route)) only_uri_router:delete_route(pre_rdx_r, router_opts) end - sync_tb[k] = nil end From 785e7f2cd879feb823a985191269e0caeb020a7d Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Wed, 5 Jul 2023 14:11:29 +0800 Subject: [PATCH 17/22] comment --- apisix/http/router/radixtree_host_uri.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index b6a551a0cbfc..5cce8e3ca321 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -244,7 +244,6 @@ local function push_host_router(route, host_routes, only_uri_routes, all_hosts, if #routes == 0 then host_routes[k] = nil if op then - core.log.error("###################del####", k) table.insert(op["del"], k) end else @@ -396,7 +395,7 @@ local function incremental_operate_radixtree(routes) end elseif k == "del" then for _, j in ipairs(v) do - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j, pre_r_opt.id) + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local pre_r_opt = pre_route_opt[j] host_router:delete_route(pre_r_opt, router_opts) end From 80493bdabb683353a5e73d19b4f540fab1464db0 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Wed, 5 Jul 2023 14:38:12 +0800 Subject: [PATCH 18/22] comment --- apisix/http/router/radixtree_host_uri.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 5cce8e3ca321..5e109e3d5fe0 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -383,20 +383,20 @@ local function incremental_operate_radixtree(routes) for k, v in pairs(op) do if k == "add" then for _, j in ipairs(v) do - core.log.notice("add the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local r_opt = route_opt[j] + core.log.notice("add the route with reverse host watched from etcd into radixtree.", r_opt.id, r_opt.paths) host_router:add_route(r_opt, router_opts) end elseif k == "upd" then for _, j in ipairs(v) do - core.log.notice("update the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local r_opt = route_opt[j] + core.log.notice("update the route with reverse host watched from etcd into radixtree.", r_opt.id, r_opt.paths) host_router:update_route(r_opt, r_opt, router_opts) end elseif k == "del" then for _, j in ipairs(v) do - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", json.encode(route), j) local pre_r_opt = pre_route_opt[j] + core.log.notice("delete the route with reverse host watched from etcd into radixtree.", pre_r_opt.id, pre_r_opt.paths) host_router:delete_route(pre_r_opt, router_opts) end end From aa0360b4e2a7f0c067716c39f9dc4c5ba6cf3889 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Wed, 5 Jul 2023 17:15:53 +0800 Subject: [PATCH 19/22] fix warnings --- apisix/http/router/radixtree_host_uri.lua | 5 ++--- apisix/http/router/radixtree_uri.lua | 5 ++++- .../router/radixtree_uri_with_parameter.lua | 5 ++++- apisix/router.lua | 19 ++++++++++--------- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 5e109e3d5fe0..6babd0e93728 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -323,14 +323,13 @@ local function incremental_operate_radixtree(routes) return end - local operate, route, last_route, err + local route, last_route local router_opts = { no_param_match = true } event.push(event.CONST.BUILD_ROUTER, routes) for k, _ in pairs(sync_tb) do - operate = sync_tb[k]["op"] route = sync_tb[k]["cur_route"] last_route = sync_tb[k]["last_route"] @@ -343,7 +342,7 @@ local function incremental_operate_radixtree(routes) local route_opt, pre_route_opt = {}, {} local all_hosts = {} - local hosts, pre_hosts = nil, nil + local hosts, pre_hosts local rdx_r = {} local pre_rdx_r = {} local op = {add={}, upd={}, del={}} diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index 6c80b704db83..d5926e9a1fbb 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -21,6 +21,7 @@ local get_services = require("apisix.http.service").services local apisix_router = require("apisix.router") local json = require("apisix.core.json") local table = require("apisix.core.table") +local event = require("apisix.core.event") local cached_router_version local cached_service_version local uri_routes = {} @@ -43,10 +44,12 @@ local function incremental_operate_radixtree(routes) end local op, route, last_route, err - local cur_tmp, last_tmp = {}, {} + local cur_tmp, last_tmp local router_opts = { no_param_match = true } + + event.push(event.CONST.BUILD_ROUTER, routes) for k, v in pairs(sync_tb) do op = sync_tb[k]["op"] route = sync_tb[k]["cur_route"] diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index 806e7d107462..3acd9f2d2a36 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -21,6 +21,7 @@ local get_services = require("apisix.http.service").services local apisix_router = require("apisix.router") local json = require("apisix.core.json") local table = require("apisix.core.table") +local event = require("apisix.core.event") local cached_router_version local cached_service_version local uri_routes = {} @@ -43,10 +44,12 @@ local function incremental_operate_radixtree(routes) end local op, route, last_route, err - local cur_tmp, last_tmp = {}, {} + local cur_tmp, last_tmp local router_opts = { no_param_match = false } + + event.push(event.CONST.BUILD_ROUTER, routes) for k, v in pairs(sync_tb) do op = sync_tb[k]["op"] route = sync_tb[k]["cur_route"] diff --git a/apisix/router.lua b/apisix/router.lua index 3e241957716c..a04c95db9436 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -17,14 +17,13 @@ local require = require local http_route = require("apisix.http.route") local apisix_upstream = require("apisix.upstream") -local core = require("apisix.core") +local core = require("apisix.core") +local json = require("apisix.core.json") local plugin_checker = require("apisix.plugin").plugin_checker local str_lower = string.lower -local error = error -local ipairs = ipairs -local table = require("apisix.core.table") -local json = require("apisix.core.json") -local sub_str = string.sub +local error = error +local ipairs = ipairs +local sub_str = string.sub local _M = {version = 0.3} @@ -82,7 +81,8 @@ local function filter(route, pre_route_or_size, obj) if not sync_tb[route.value.id] then sync_tb[route.value.id] = {op = "update", last_route = pre_route_or_size, cur_route = route} elseif sync_tb[route.value.id]["op"] == "update" then - sync_tb[route.value.id] = {op = "update", last_route = sync_tb[route.value.id]["last_route"], cur_route = route} + sync_tb[route.value.id] = {op = "update", last_route = sync_tb[route.value.id]["last_route"], + cur_route = route} elseif sync_tb[route.value.id]["op"] == "create" then sync_tb[route.value.id] = {op = "create", cur_route = route} end @@ -94,7 +94,7 @@ local function filter(route, pre_route_or_size, obj) elseif sync_tb[key]["op"] == "create" then sync_tb[key] = nil elseif sync_tb[key]["op"] == "update" then - sync_tb[key] = {op = "delete", last_route = sync_tb[route.value.id]["last_route"]} + sync_tb[key] = {op = "delete", last_route = sync_tb[key]["last_route"]} end end elseif route.value then @@ -103,7 +103,8 @@ local function filter(route, pre_route_or_size, obj) if not sync_tb[route.value.id] then sync_tb[route.value.id] = {op = "create", cur_route = route} elseif sync_tb[route.value.id]["op"] == "delete" then - sync_tb[route.value.id] = {op = "update", cur_route = route, last_route = sync_tb[route.value.id]["last_route"]} + sync_tb[route.value.id] = {op = "update", cur_route = route, + last_route = sync_tb[route.value.id]["last_route"]} end else core.log.error("invalid operation type for a route.", route.key) From 4cb466faa93e527a5fabfa146b685427c2bf3b42 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Thu, 6 Jul 2023 19:13:10 +0800 Subject: [PATCH 20/22] improve codes --- apisix/http/router/radixtree_host_uri.lua | 426 ++++++---------------- 1 file changed, 105 insertions(+), 321 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 6babd0e93728..3d1dd4f81b7e 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -20,9 +20,10 @@ local core = require("apisix.core") local event = require("apisix.core.event") local get_services = require("apisix.http.service").services local service_fetch = require("apisix.http.service").get -local apisix_router = require("apisix.router") +local ar = require("apisix.router") local table = require("apisix.core.table") local json = require("apisix.core.json") +local rdx = require("resty.radixtree") local ipairs = ipairs local type = type local tab_insert = table.insert @@ -32,245 +33,99 @@ local cached_router_version local cached_service_version local host_router local only_uri_router -local host_routes = {} -local only_uri_routes = {} local _M = {version = 0.1} -local function empty_func() end - - -local function push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_rt, pre_route, pre_rdx_rt) - if route and type(route) ~= "table" then +local function push_host_router(route, host_routes, only_uri_routes) + if type(route) ~= "table" or route.value == nil then return end - local radixtree_route, pre_radixtree_route = {}, {} - local hosts - if route and route.value then - hosts = route.value.hosts - if not hosts then - if route.value.host then - hosts = {route.value.host} - elseif route.value.service_id then - local service = service_fetch(route.value.service_id) - if not service then - core.log.error("failed to fetch service configuration by ", - "id: ", route.value.service_id) - -- we keep the behavior that missing service won't affect the route matching - else - hosts = service.value.hosts - end - end - end - - local filter_fun, err - if route.value and route.value.filter_func then - filter_fun, err = loadstring( - "return " .. route.value.filter_func, - "router#" .. route.value.id) - if not filter_fun then - core.log.error("failed to load filter function: ", err, - " route id: ", route.value.id) - return - end - - filter_fun = filter_fun() - end - - radixtree_route = { - id = route.value.id, - paths = route.value.uris or route.value.uri, - methods = route.value.methods, - priority = route.value.priority, - remote_addrs = route.value.remote_addrs - or route.value.remote_addr, - vars = route.value.vars, - filter_fun = filter_fun, - handler = function (api_ctx, match_opts) - api_ctx.matched_params = nil - api_ctx.matched_route = route - api_ctx.curr_req_matched = match_opts.matched - api_ctx.real_curr_req_matched_path = match_opts.matched._path - end - } - - if rdx_rt ~= nil then - for k, v in pairs(radixtree_route) do - rdx_rt[k] = v - end + local filter_fun, err + if route.value.filter_func then + filter_fun, err = loadstring( + "return " .. route.value.filter_func, + "router#" .. route.value.id) + if not filter_fun then + core.log.error("failed to load filter function: ", err, + " route id: ", route.value.id) + return end - end - if hosts == nil and all_hosts == nil then - core.table.insert(only_uri_routes, radixtree_route) - return + filter_fun = filter_fun() end - local pre_hosts - if pre_route and pre_route.value then - pre_hosts = pre_route.value.hosts - if not pre_hosts then - if pre_route.value.host then - pre_hosts = {pre_route.value.host} - elseif pre_route.value.service_id then - local service = service_fetch(pre_route.value.service_id) - if not service then - core.log.error("failed to fetch service configuration by ", - "id: ", pre_route.value.service_id) - -- we keep the behavior that missing service won't affect the route matching - else - pre_hosts = service.value.hosts - end - end - end - - local filter_fun, err - if pre_route.value and pre_route.value.filter_func then - filter_fun, err = loadstring( - "return " .. pre_route.value.filter_func, - "router#" .. pre_route.value.id) - if not filter_fun then - core.log.error("failed to load filter function: ", err, - " route id: ", pre_route.value.id) - return - end - - filter_fun = filter_fun() - end - - pre_radixtree_route = { - id = pre_route.value.id, - paths = pre_route.value.uris or pre_route.value.uri, - methods = pre_route.value.methods, - priority = pre_route.value.priority, - remote_addrs = pre_route.value.remote_addrs - or pre_route.value.remote_addr, - vars = pre_route.value.vars, - filter_fun = filter_fun, - handler = function (api_ctx, match_opts) - api_ctx.matched_params = nil - api_ctx.matched_route = pre_route - api_ctx.curr_req_matched = match_opts.matched - api_ctx.real_curr_req_matched_path = match_opts.matched._path - end - } - - if pre_rdx_rt ~= nil then - for k, v in pairs(pre_radixtree_route) do - pre_rdx_rt[k] = v + local hosts = route.value.hosts + if not hosts then + if route.value.host then + hosts = {route.value.host} + elseif route.value.service_id then + local service = service_fetch(route.value.service_id) + if not service then + core.log.error("failed to fetch service configuration by ", + "id: ", route.value.service_id) + -- we keep the behavior that missing service won't affect the route matching + else + hosts = service.value.hosts end end end - if all_hosts ~= nil then - all_hosts["host"] = hosts - all_hosts["pre_host"] = pre_hosts - end - - local pre_t = {} - if pre_hosts then - for i, h in ipairs(pre_hosts) do - local rev_h = h:reverse() - pre_t[rev_h] = 1 - end - end - - local t = {} - if hosts then - for i, h in ipairs(hosts) do - local rev_h = h:reverse() - t[rev_h] = 1 - end - end - - local comm = {} - for k, v in pairs(pre_t) do - if t[k] ~= nil then - tab_insert(comm, k) - pre_t[k] = nil - t[k] = nil - end - end - - for _, j in ipairs(comm) do - local routes = host_routes[j] - if routes == nil then - core.log.error("no routes array for reverse host in the map.", j) - return - end - - local found = false - for i, r in ipairs(routes) do - if r.id == radixtree_route.id then - routes[i] = radixtree_route - found = true - if op then - table.insert(op["upd"], j) - end - break - end + local radixtree_route = { + paths = route.value.uris or route.value.uri, + methods = route.value.methods, + priority = route.value.priority, + remote_addrs = route.value.remote_addrs + or route.value.remote_addr, + vars = route.value.vars, + filter_fun = filter_fun, + handler = function (api_ctx, match_opts) + api_ctx.matched_params = nil + api_ctx.matched_route = route + api_ctx.curr_req_matched = match_opts.matched + api_ctx.real_curr_req_matched_path = match_opts.matched._path end + } - if not found then - core.log.error("cannot find the route in common host's table.", j, radixtree_route.id) - return - end + if hosts == nil then + core.table.insert(only_uri_routes, radixtree_route) + return end - for k, v in pairs(pre_t) do - local routes = host_routes[k] - if routes == nil then - core.log.error("no routes array for reverse host in the map.", k) - return - end - - local found = false - for i, r in ipairs(routes) do - if r.id == pre_radixtree_route.id then - table.remove(routes, i) - found = true - break - end - end - - if not found then - core.log.error("cannot find the route in previous host's table.", k, pre_radixtree_route.id) - return - end - - if #routes == 0 then - host_routes[k] = nil - if op then - table.insert(op["del"], k) - end - else - if op then - table.insert(op["upd"], k) - end - end - end + local match_opts = {} + match_opts.method = route.value.methods[1] + match_opts.remote_addr = route.value.remote_addrs[1] or route.value.remote_addr + match_opts.vars = route.value.vars + match_opts.host = route.value.host or route.value.hosts[1] + match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4) - for k, v in pairs(t) do - local routes = host_routes[k] - if routes == nil then - host_routes[k] = {radixtree_route} - if op then - table.insert(op["add"], k) - end - else - table.insert(routes, radixtree_route) - if op then - table.insert(op["upd"], k) - end - end - end + local sr = rdx.match(match_opts.host:reverse(), match_opts)["sub_route"] + + local rev = {} + for h in ipairs(hosts) do + table.insert(rev, h:reverse()) + end + local sub_router = router.new({radixtree_route}) + core.table.insert(host_routes, { + id = route.value.id, + paths = rev, + sub_r = sub_router, + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = function (api_ctx, match_opts) + api_ctx.real_curr_req_matched_host = match_opts.matched._path + end, + metadata = {sub_route=sub_router} + }) end local function create_radixtree_router(routes) + local host_routes = {} + local only_uri_routes = {} host_router = nil routes = routes or {} @@ -282,27 +137,10 @@ local function create_radixtree_router(routes) end end - -- create router: host_router - local host_router_routes = {} - for host_rev, routes in pairs(host_routes) do - local sub_router = router.new(routes) - - core.table.insert(host_router_routes, { - id = 1, - paths = host_rev, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = function (api_ctx, match_opts) - api_ctx.real_curr_req_matched_host = match_opts.matched._path - end - }) - end - event.push(event.CONST.BUILD_ROUTER, routes) - if #host_router_routes > 0 then - host_router = router.new(host_router_routes) + if #host_routes > 0 then + host_router = router.new(host_routes) end -- create router: only_uri_router @@ -312,110 +150,56 @@ end local function incremental_operate_radixtree(routes) - local sync_tb = apisix_router.sync_tb - if apisix_router.need_create_radixtree then + if ar.need_create_radixtree then core.log.notice("create object of radixtree host uri after load_full_data or init.", #routes) create_radixtree_router(routes) - apisix_router.need_create_radixtree = false - for k, _ in pairs(sync_tb) do - sync_tb[k] = nil - end + ar.need_create_radixtree = false + table.clear(ar.sync_tb) return end - local route, last_route + local op, route, last_route local router_opts = { no_param_match = true } event.push(event.CONST.BUILD_ROUTER, routes) - for k, _ in pairs(sync_tb) do - route = sync_tb[k]["cur_route"] - last_route = sync_tb[k]["last_route"] - - if route then - local status = table.try_read_attr(route, "value", "status") - if status and status == 0 then + for k, _ in pairs(ar.sync_tb) do + op = ar.sync_tb[k]["op"] + route = ar.sync_tb[k]["cur_route"] + last_route = ar.sync_tb[k]["last_route"] + local host_routes = {} + local only_uri_routes = {} + local last_host = {} + local last_only_uri = {} + + push_host_router(route, host_routes, only_uri_routes) + push_host_router(last_route, last_host, last_only_uri) + + if #host_routes > 0 then + local cur_tmp = host_routes[1] or {paths = {}} + local last_tmp = last_host[1] or {paths = {}} + + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + local err = host_router:update_route(last_tmp, cur_tmp, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(route), err) return end - end - - local route_opt, pre_route_opt = {}, {} - local all_hosts = {} - local hosts, pre_hosts - local rdx_r = {} - local pre_rdx_r = {} - local op = {add={}, upd={}, del={}} - - push_host_router(route, host_routes, only_uri_routes, all_hosts, op, rdx_r, last_route, pre_rdx_r) - - hosts = all_hosts["host"] - if hosts ~= nil then - for _, h in ipairs(hosts) do - local host_rev = h:reverse() - local routes = host_routes[host_rev] - local sub_router = router.new(routes) - route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = empty_func, - } - end - end - - pre_hosts = all_hosts["pre_host"] - if pre_hosts ~= nil then - for _, h in ipairs(pre_hosts) do - local host_rev = h:reverse() - pre_route_opt[host_rev] = { - id = 1, - paths = host_rev, - filter_fun = empty_func, - handler = empty_func, - } - end - end + else + local cur_tmp = only_uri_routes[1] or {paths = {}} + local last_tmp = last_only_uri[1] or {paths = {}} - for k, v in pairs(op) do - if k == "add" then - for _, j in ipairs(v) do - local r_opt = route_opt[j] - core.log.notice("add the route with reverse host watched from etcd into radixtree.", r_opt.id, r_opt.paths) - host_router:add_route(r_opt, router_opts) - end - elseif k == "upd" then - for _, j in ipairs(v) do - local r_opt = route_opt[j] - core.log.notice("update the route with reverse host watched from etcd into radixtree.", r_opt.id, r_opt.paths) - host_router:update_route(r_opt, r_opt, router_opts) - end - elseif k == "del" then - for _, j in ipairs(v) do - local pre_r_opt = pre_route_opt[j] - core.log.notice("delete the route with reverse host watched from etcd into radixtree.", pre_r_opt.id, pre_r_opt.paths) - host_router:delete_route(pre_r_opt, router_opts) - end + core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) + local err = only_uri_router:update_route(last_tmp, cur_tmp, router_opts) + if err ~= nil then + core.log.error("update a route into radixtree failed.", json.encode(route), err) + return end end - if (route and (route.value and not hosts)) and (not last_route or pre_hosts) then - core.log.notice("add the route with uri watched from etcd into radixtree.", json.encode(route)) - only_uri_router:add_route(rdx_r, router_opts) - elseif (route and (route.value and not hosts)) and (last_route and not pre_hosts) then - core.log.notice("update the route with uri watched from etcd into radixtree.", json.encode(route)) - only_uri_router:update_route(pre_rdx_r, rdx_r, router_opts) - elseif (last_route and not pre_hosts) and (not (route and route.value) or hosts) then - core.log.notice("delete the route with uri watched from etcd into radixtree.", json.encode(last_route)) - only_uri_router:delete_route(pre_rdx_r, router_opts) - end - - sync_tb[k] = nil + ar.sync_tb[k] = nil end - - apisix_router.sync_tb = sync_tb end From c7dc96613b7e76ad0173ecddf44e54b7ba54c107 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Fri, 7 Jul 2023 18:38:05 +0800 Subject: [PATCH 21/22] coding done. --- apisix/http/router/radixtree_host_uri.lua | 110 +++++++++++++++------- 1 file changed, 75 insertions(+), 35 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 3d1dd4f81b7e..7b74eee9ab47 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -39,7 +39,7 @@ local _M = {version = 0.1} local function push_host_router(route, host_routes, only_uri_routes) - if type(route) ~= "table" or route.value == nil then + if route == nil or (type(route) ~= "table" or route.value == nil) then return end @@ -74,6 +74,7 @@ local function push_host_router(route, host_routes, only_uri_routes) end local radixtree_route = { + id = route.value.id, paths = route.value.uris or route.value.uri, methods = route.value.methods, priority = route.value.priority, @@ -94,31 +95,14 @@ local function push_host_router(route, host_routes, only_uri_routes) return end - local match_opts = {} - match_opts.method = route.value.methods[1] - match_opts.remote_addr = route.value.remote_addrs[1] or route.value.remote_addr - match_opts.vars = route.value.vars - match_opts.host = route.value.host or route.value.hosts[1] - match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4) - - local sr = rdx.match(match_opts.host:reverse(), match_opts)["sub_route"] - local rev = {} - for h in ipairs(hosts) do - table.insert(rev, h:reverse()) + for i, h in ipairs(hosts) do + tab_insert(rev, h:reverse()) end - local sub_router = router.new({radixtree_route}) + core.table.insert(host_routes, { - id = route.value.id, - paths = rev, - sub_r = sub_router, - filter_fun = function(vars, opts, ...) - return sub_router:dispatch(vars.uri, opts, ...) - end, - handler = function (api_ctx, match_opts) - api_ctx.real_curr_req_matched_host = match_opts.matched._path - end, - metadata = {sub_route=sub_router} + rev = rev, + route = radixtree_route }) end @@ -137,10 +121,26 @@ local function create_radixtree_router(routes) end end + local host_router_routes = {} + for i, hr in ipairs(host_routes) do + local sub_router = router.new(hr["route"]) + + core.table.insert(host_router_routes, { + id = hr["route"]["id"], + paths = hr["rev"], + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = function (api_ctx, match_opts) + api_ctx.real_curr_req_matched_host = match_opts.matched._path + end + }) + end + event.push(event.CONST.BUILD_ROUTER, routes) - if #host_routes > 0 then - host_router = router.new(host_routes) + if #host_router_routes > 0 then + host_router = router.new(host_router_routes) end -- create router: only_uri_router @@ -166,29 +166,69 @@ local function incremental_operate_radixtree(routes) event.push(event.CONST.BUILD_ROUTER, routes) for k, _ in pairs(ar.sync_tb) do op = ar.sync_tb[k]["op"] - route = ar.sync_tb[k]["cur_route"] + route = ar.sync_tb[k]["outer_routeoute"] last_route = ar.sync_tb[k]["last_route"] local host_routes = {} local only_uri_routes = {} - local last_host = {} - local last_only_uri = {} + local last_host_routes = {} + local last_only_uri_routes = {} + local outer_route, last_outer_route = {paths={}}, {paths={}} + local inner_route, last_inner_route = {}, {} + local sub_router push_host_router(route, host_routes, only_uri_routes) - push_host_router(last_route, last_host, last_only_uri) - - if #host_routes > 0 then - local cur_tmp = host_routes[1] or {paths = {}} - local last_tmp = last_host[1] or {paths = {}} + push_host_router(last_route, last_host_routes, last_only_uri_routes) + + if #host_routes > 0 or #last_host_routes > 0 then + if route then + if #host_routes > 0 then + inner_route = host_routes[1]["route"] + end + + if #last_host_routes > 0 then + last_inner_route = last_host_routes[1]["route"] + last_outer_route = { + id = last_route.value.id, + paths = last_host_routes[1]["rev"], + } + end + + local match_opts = {} + match_opts.method = route.value.methods[1] + match_opts.remote_addr = route.value.remote_addrs[1] or route.value.remote_addr + match_opts.vars = route.value.vars + match_opts.host = route.value.host or route.value.hosts[1] + match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4) + + local metadata, err = rdx.match(match_opts.host:reverse(), match_opts) + if metadata and metadata["sub_route"] then + sub_router = metadata["sub_route"] + sub_router:update_route(last_inner_route, inner_route, router_opts) + else + sub_router = router.new(inner_route) + end + + outer_route = { + id = route.value.id, + paths = host_routes[1]["rev"], + filter_fun = function(vars, opts, ...) + return sub_router:dispatch(vars.uri, opts, ...) + end, + handler = function (api_ctx, match_opts) + api_ctx.real_curr_req_matched_host = match_opts.matched._path + end + } + end core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) - local err = host_router:update_route(last_tmp, cur_tmp, router_opts) + local err = host_router:update_route(last_outer_route, outer_route, router_opts) if err ~= nil then core.log.error("update a route into radixtree failed.", json.encode(route), err) return end else local cur_tmp = only_uri_routes[1] or {paths = {}} - local last_tmp = last_only_uri[1] or {paths = {}} + local last_tmp = last_only_uri_routes[1] or {paths = {}} core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) local err = only_uri_router:update_route(last_tmp, cur_tmp, router_opts) From 6dad9c9a2a22bc6a7416cc247ceef9a3ccdd53c5 Mon Sep 17 00:00:00 2001 From: ranxuxin001 Date: Sun, 9 Jul 2023 19:26:03 +0800 Subject: [PATCH 22/22] reuse function --- apisix/http/router/radixtree_host_uri.lua | 15 +-- apisix/http/router/radixtree_uri.lua | 33 +++--- .../router/radixtree_uri_with_parameter.lua | 105 +----------------- 3 files changed, 18 insertions(+), 135 deletions(-) diff --git a/apisix/http/router/radixtree_host_uri.lua b/apisix/http/router/radixtree_host_uri.lua index 7b74eee9ab47..61eadd9871ec 100644 --- a/apisix/http/router/radixtree_host_uri.lua +++ b/apisix/http/router/radixtree_host_uri.lua @@ -193,20 +193,7 @@ local function incremental_operate_radixtree(routes) } end - local match_opts = {} - match_opts.method = route.value.methods[1] - match_opts.remote_addr = route.value.remote_addrs[1] or route.value.remote_addr - match_opts.vars = route.value.vars - match_opts.host = route.value.host or route.value.hosts[1] - match_opts.matched = core.tablepool.fetch("matched_route_record", 0, 4) - - local metadata, err = rdx.match(match_opts.host:reverse(), match_opts) - if metadata and metadata["sub_route"] then - sub_router = metadata["sub_route"] - sub_router:update_route(last_inner_route, inner_route, router_opts) - else - sub_router = router.new(inner_route) - end + sub_router = router.new(inner_route) outer_route = { id = route.value.id, diff --git a/apisix/http/router/radixtree_uri.lua b/apisix/http/router/radixtree_uri.lua index d5926e9a1fbb..9a602f201f99 100644 --- a/apisix/http/router/radixtree_uri.lua +++ b/apisix/http/router/radixtree_uri.lua @@ -18,7 +18,7 @@ local require = require local core = require("apisix.core") local base_router = require("apisix.http.route") local get_services = require("apisix.http.service").services -local apisix_router = require("apisix.router") +local ar = require("apisix.router") local json = require("apisix.core.json") local table = require("apisix.core.table") local event = require("apisix.core.event") @@ -32,35 +32,33 @@ local match_opts = {} local _M = {version = 0.2} -local function incremental_operate_radixtree(routes) - local sync_tb = apisix_router.sync_tb - if apisix_router.need_create_radixtree then +function _M.incremental_operate_radixtree(routes, no_param) + local sync_tb = ar.sync_tb + if ar.need_create_radixtree then uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, false) - apisix_router.need_create_radixtree = false - for k, _ in pairs(sync_tb) do - sync_tb[k] = nil - end + ar.need_create_radixtree = false + table.clear(ar.sync_tb) return end local op, route, last_route, err local cur_tmp, last_tmp local router_opts = { - no_param_match = true + no_param_match = no_param } event.push(event.CONST.BUILD_ROUTER, routes) - for k, v in pairs(sync_tb) do - op = sync_tb[k]["op"] - route = sync_tb[k]["cur_route"] - last_route = sync_tb[k]["last_route"] + for k, v in pairs(ar.sync_tb) do + op = ar.sync_tb[k]["op"] + route = ar.sync_tb[k]["cur_route"] + last_route = ar.sync_tb[k]["last_route"] cur_tmp = {} last_tmp = {} if route and route.value then local status = table.try_read_attr(route, "value", "status") if status and status == 0 then - return + goto CONTINUE end local filter_fun, err @@ -129,10 +127,9 @@ local function incremental_operate_radixtree(routes) end end - sync_tb[k] = nil + ar.sync_tb[k] = nil + ::CONTINUE:: end - - apisix_router.sync_tb = sync_tb end @@ -142,7 +139,7 @@ function _M.match(api_ctx) if not cached_router_version or cached_router_version ~= user_routes.conf_version or not cached_service_version or cached_service_version ~= service_version then - incremental_operate_radixtree(user_routes.values) + _M.incremental_operate_radixtree(user_routes.values,true) cached_router_version = user_routes.conf_version cached_service_version = service_version end diff --git a/apisix/http/router/radixtree_uri_with_parameter.lua b/apisix/http/router/radixtree_uri_with_parameter.lua index 3acd9f2d2a36..918b47865756 100644 --- a/apisix/http/router/radixtree_uri_with_parameter.lua +++ b/apisix/http/router/radixtree_uri_with_parameter.lua @@ -22,6 +22,7 @@ local apisix_router = require("apisix.router") local json = require("apisix.core.json") local table = require("apisix.core.table") local event = require("apisix.core.event") +local radixtree_uri = requre("apisix.http.router.radixtree_uri") local cached_router_version local cached_service_version local uri_routes = {} @@ -32,108 +33,6 @@ local match_opts = {} local _M = {} -local function incremental_operate_radixtree(routes) - local sync_tb = apisix_router.sync_tb - if apisix_router.need_create_radixtree then - uri_router = base_router.create_radixtree_uri_router(routes, uri_routes, true) - apisix_router.need_create_radixtree = false - for k, _ in pairs(sync_tb) do - sync_tb[k] = nil - end - return - end - - local op, route, last_route, err - local cur_tmp, last_tmp - local router_opts = { - no_param_match = false - } - - event.push(event.CONST.BUILD_ROUTER, routes) - for k, v in pairs(sync_tb) do - op = sync_tb[k]["op"] - route = sync_tb[k]["cur_route"] - last_route = sync_tb[k]["last_route"] - cur_tmp = {} - last_tmp = {} - - if route and route.value then - local status = table.try_read_attr(route, "value", "status") - if status and status == 0 then - return - end - - local filter_fun, err - if route.value.filter_func then - filter_fun, err = loadstring( - "return " .. route.value.filter_func, - "router#" .. route.value.id - ) - if not filter_fun then - core.log.error("failed to load filter function: ", err, " route id", route.value.id) - return - end - - filter_fun = filter_fun() - end - - cur_tmp = { - id = route.value.id, - paths = route.value.uris or route.value.uri, - methods = route.value.methods, - priority = route.value.priority, - hosts = route.value.hosts or route.value.host, - remote_addrs = route.value.remote_addrs or route.value.remote_addr, - vars = route.value.vars, - filter_fun = filter_fun, - handler = function(api_ctx, match_opts) - api_ctx.matched_params = nil - api_ctx.matched_route = route - api_ctx.curr_req_matched = match_opts.matched - end - } - end - - if last_route and last_route.value then - last_tmp = { - id = last_route.value.id, - paths = last_route.value.uris or last_route.value.uri, - methods = last_route.value.methods, - priority = last_route.value.priority, - hosts = last_route.value.hosts or last_route.value.host, - remote_addrs = last_route.value.remote_addrs or last_route.value.remote_addr, - vars = last_route.value.vars - } - end - - if op == "update" then - core.log.notice("update routes watched from etcd into radixtree.", json.encode(route)) - err = uri_router:update_route(last_tmp, cur_tmp, router_opts) - if err ~= nil then - core.log.error("update a route into radixtree failed.", json.encode(route), err) - return - end - elseif op == "create" then - core.log.notice("create routes watched from etcd into radixtree.", json.encode(route)) - err = uri_router:add_route(cur_tmp, router_opts) - if err ~= nil then - core.log.error("add routes into radixtree failed.", json.encode(route), err) - return - end - elseif op == "delete" then - core.log.notice("delete routes watched from etcd into radixtree.", json.encode(last_route)) - err = uri_router:delete_route(last_tmp, router_opts) - if err ~= nil then - core.log.error("delete a route into radixtree failed.", json.encode(last_route), err) - return - end - end - - sync_tb[k] = nil - end - - apisix_router.sync_tb = sync_tb -end function _M.match(api_ctx) @@ -142,7 +41,7 @@ function _M.match(api_ctx) if not cached_router_version or cached_router_version ~= user_routes.conf_version or not cached_service_version or cached_service_version ~= service_version then - incremental_operate_radixtree(user_routes.values) + radixtree_uri.incremental_operate_radixtree(user_routes.values) cached_router_version = user_routes.conf_version cached_service_version = service_version end