From 337d6ceda0d2ef01793c0cd67ecda1a0b17360ba Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Sun, 4 Aug 2019 02:06:42 +0800 Subject: [PATCH 1/4] feature: allowed to use different router. --- conf/config.yaml | 8 ++--- lua/apisix.lua | 36 +++---------------- lua/apisix/admin/services.lua | 6 ++-- lua/apisix/admin/upstreams.lua | 4 +-- lua/apisix/http/router.lua | 32 +++++++++++++++++ .../http/{ssl.lua => router/r3_sni.lua} | 4 +-- .../http/{route.lua => router/r3_uri.lua} | 30 ++++++++++++---- 7 files changed, 71 insertions(+), 49 deletions(-) create mode 100644 lua/apisix/http/router.lua rename lua/apisix/http/{ssl.lua => router/r3_sni.lua} (97%) rename lua/apisix/http/{route.lua => router/r3_uri.lua} (78%) diff --git a/conf/config.yaml b/conf/config.yaml index 4c853dc3d0a6..0fed84e409cc 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -6,14 +6,14 @@ apisix: enable_debug: false allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow - 127.0.0.0/24 + # port_admin: 9180 # use a separate port real_ip_header: "X-Real-IP" # http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header real_ip_from: # http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from - 127.0.0.1 - 'unix:' - # port_admin: 9180 # use a separate port - route_idx: 'uri' # how to create the route index: - # `uri`: only use `uri` for routing - # `host+uri`: use `host+uri` for routing + router: + http: 'r3_uri' + ssl: 'r3_sni' etcd: host: "http://127.0.0.1:2379" # etcd address diff --git a/lua/apisix.lua b/lua/apisix.lua index 98804f3e854d..b88b7c5e8cb5 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -2,25 +2,21 @@ local require = require local core = require("apisix.core") -local router = require("apisix.http.route").get local plugin = require("apisix.plugin") local service_fetch = require("apisix.http.service").get -local ssl_match = require("apisix.http.ssl").match local admin_init = require("apisix.admin.init") local get_var = require("resty.ngxvar").fetch -local local_conf = core.config.local_conf +local router = require("apisix.http.router") local ngx = ngx local get_method = ngx.req.get_method local ngx_exit = ngx.exit local ngx_ERROR = ngx.ERROR -local str_reverse = string.reverse local math = math -local match_opts = {} local error = error local load_balancer -local _M = {version = 0.1} +local _M = {version = 0.2} function _M.http_init() @@ -57,10 +53,8 @@ function _M.http_init_worker() require("apisix.admin.init").init_worker() - require("apisix.http.route").init_worker() + router.init_worker() require("apisix.http.service").init_worker() - require("apisix.http.ssl").init_worker() - require("apisix.plugin").init_worker() require("apisix.consumer").init_worker() end @@ -133,7 +127,7 @@ function _M.http_ssl_phase() ngx_ctx.api_ctx = api_ctx end - local ok, err = ssl_match(api_ctx) + local ok, err = router.router_ssl.match(api_ctx) if not ok then if err then core.log.error("failed to fetch ssl config: ", err) @@ -153,28 +147,8 @@ function _M.http_access_phase() end core.ctx.set_vars_meta(api_ctx) - core.table.clear(match_opts) - match_opts.method = api_ctx.var.method - - local ok - - if local_conf().apisix - and local_conf().apisix.route_idx == "host+uri" then - local host = api_ctx.var.host - host = host and str_reverse(host) or "[^/]+" - host = host .. api_ctx.var.uri - ok = router():dispatch2(nil, host, match_opts, api_ctx) - core.log.info("match string: ", host) - else - match_opts.host = api_ctx.var.host - ok = router():dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) - end - - if not ok then - core.log.info("not find any matched route") - return core.response.exit(404) - end + router.router_http.match(api_ctx) core.log.info("route: ", core.json.delay_encode(api_ctx.matched_route, true)) diff --git a/lua/apisix/admin/services.lua b/lua/apisix/admin/services.lua index 2baa9b5343a0..cb562d8c51e2 100644 --- a/lua/apisix/admin/services.lua +++ b/lua/apisix/admin/services.lua @@ -1,5 +1,5 @@ local core = require("apisix.core") -local routes = require("apisix.http.route").routes +local get_routes = require("apisix.http.router").http_routes local schema_plugin = require("apisix.admin.plugins").check_schema local tostring = tostring local ipairs = ipairs @@ -8,7 +8,7 @@ local type = type local _M = { - version = 0.1, + version = 0.2, } @@ -126,7 +126,7 @@ function _M.delete(id) return 400, {error_msg = "missing service id"} end - local routes, routes_ver = routes() + local routes, routes_ver = get_routes() core.log.info("routes: ", core.json.delay_encode(routes, true)) core.log.info("routes_ver: ", routes_ver) if routes_ver and routes then diff --git a/lua/apisix/admin/upstreams.lua b/lua/apisix/admin/upstreams.lua index caa7ce7542f4..f708826cb125 100644 --- a/lua/apisix/admin/upstreams.lua +++ b/lua/apisix/admin/upstreams.lua @@ -1,5 +1,5 @@ local core = require("apisix.core") -local get_routes = require("apisix.http.route").routes +local get_routes = require("apisix.http.router").http_routes local get_services = require("apisix.http.service").services local tostring = tostring local ipairs = ipairs @@ -8,7 +8,7 @@ local type = type local _M = { - version = 0.1, + version = 0.2, } diff --git a/lua/apisix/http/router.lua b/lua/apisix/http/router.lua new file mode 100644 index 000000000000..a87be105a0ae --- /dev/null +++ b/lua/apisix/http/router.lua @@ -0,0 +1,32 @@ +local core = require("apisix.core") +local local_conf = core.config.local_conf + + +local _M = {version = 0.1} + + +function _M.init_worker() + local conf = local_conf() + local router_http_name = "r3_uri" + local router_ssl_name = "r3_sni" + if conf and conf.apisix and conf.apisix.router then + router_http_name = conf.apisix.router.http or router_http_name + router_ssl_name = conf.apisix.router.ssl or router_ssl_name + end + + local router_http = require("apisix.http.router." .. router_http_name) + router_http.init_worker() + _M.router_http = router_http + + local router_ssl = require("apisix.http.router." .. router_ssl_name) + router_ssl:init_worker() + _M.router_ssl = router_ssl +end + + +function _M.http_routes() + return _M.router_http.routes() +end + + +return _M diff --git a/lua/apisix/http/ssl.lua b/lua/apisix/http/router/r3_sni.lua similarity index 97% rename from lua/apisix/http/ssl.lua rename to lua/apisix/http/router/r3_sni.lua index cca1089caf56..7e9ed57a764e 100644 --- a/lua/apisix/http/ssl.lua +++ b/lua/apisix/http/router/r3_sni.lua @@ -103,12 +103,12 @@ function _M.match(api_ctx) local r3, err = core.lrucache.global("/ssl", ssl.conf_version, create_r3_router, ssl.values) if not r3 then - return false, "gailed to fetch ssl router: " .. err + return false, "failed to fetch ssl router: " .. err end local sni = ngx_ssl.server_name() if type(sni) ~= "string" then - return false, "gailed to fetch SNI: " .. err + return false, "failed to fetch SNI: " .. err end core.log.debug("sni: ", sni) diff --git a/lua/apisix/http/route.lua b/lua/apisix/http/router/r3_uri.lua similarity index 78% rename from lua/apisix/http/route.lua rename to lua/apisix/http/router/r3_uri.lua index f7357cc78744..8a0959425d99 100644 --- a/lua/apisix/http/route.lua +++ b/lua/apisix/http/router/r3_uri.lua @@ -4,6 +4,7 @@ local require = require local r3router = require("resty.r3") local core = require("apisix.core") local plugin = require("apisix.plugin") +local local_conf = core.config.local_conf local ipairs = ipairs local type = type local error = error @@ -34,9 +35,8 @@ local function create_r3_router(routes) end end - local local_conf = core.config.local_conf() - local route_idx = local_conf and local_conf.apisix and - local_conf.apisix.route_idx + local conf = local_conf() + local route_idx = conf and conf.apisix and conf.apisix.route_idx for _, route in ipairs(routes) do if type(route) == "table" then @@ -85,10 +85,26 @@ local function create_r3_router(routes) end -function _M.get() - core.log.info("routes conf_version: ", routes.conf_version) - return core.lrucache.global("/routes", routes.conf_version, - create_r3_router, routes.values) + local match_opts = {} +function _M.match(api_ctx) + local router, err = core.lrucache.global("/routes", routes.conf_version, + create_r3_router, routes.values) + if not router then + core.log.error("failed to fetch http router: ", err) + return core.response.exit(404) + end + + core.table.clear(match_opts) + match_opts.method = api_ctx.var.method + match_opts.host = api_ctx.var.host + + local ok = router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) + if not ok then + core.log.info("not find any matched route") + return core.response.exit(404) + end + + return true end From 507e51f774ea434c81c207b8da9c39d28a438478 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Sun, 4 Aug 2019 02:18:29 +0800 Subject: [PATCH 2/4] feature: supported to match route by `host + uri`. --- lua/apisix.lua | 1 + lua/apisix/http/router.lua | 1 + lua/apisix/http/router/r3_host_uri.lua | 119 +++++++++++++++++++++++++ lua/apisix/http/router/r3_uri.lua | 49 ++-------- t/node/match-host-uri.t | 2 +- 5 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 lua/apisix/http/router/r3_host_uri.lua diff --git a/lua/apisix.lua b/lua/apisix.lua index b88b7c5e8cb5..816a9799f651 100644 --- a/lua/apisix.lua +++ b/lua/apisix.lua @@ -52,6 +52,7 @@ function _M.http_init_worker() load_balancer = require("apisix.http.balancer").run require("apisix.admin.init").init_worker() + require("apisix.http.balancer").init_worker() router.init_worker() require("apisix.http.service").init_worker() diff --git a/lua/apisix/http/router.lua b/lua/apisix/http/router.lua index a87be105a0ae..f6316099527d 100644 --- a/lua/apisix/http/router.lua +++ b/lua/apisix/http/router.lua @@ -1,3 +1,4 @@ +local require = require local core = require("apisix.core") local local_conf = core.config.local_conf diff --git a/lua/apisix/http/router/r3_host_uri.lua b/lua/apisix/http/router/r3_host_uri.lua new file mode 100644 index 000000000000..1cb823b9b413 --- /dev/null +++ b/lua/apisix/http/router/r3_host_uri.lua @@ -0,0 +1,119 @@ +-- Copyright (C) Yuansheng Wang + +local require = require +local r3router = require("resty.r3") +local core = require("apisix.core") +local plugin = require("apisix.plugin") +local ipairs = ipairs +local type = type +local error = error +local str_reverse = string.reverse +local routes + + +local _M = {version = 0.1} + + + local empty_tab = {} + local route_items +local function create_r3_router(routes) + routes = routes or empty_tab + + local api_routes = plugin.api_routes() + route_items = core.table.new(#api_routes + #routes, 0) + local idx = 0 + + for _, route in ipairs(api_routes) do + if type(route) == "table" then + idx = idx + 1 + route_items[idx] = { + path = route.uri, + handler = route.handler, + method = route.methods, + } + end + end + + for _, route in ipairs(routes) do + if type(route) == "table" then + idx = idx + 1 + + local host = route.value.host + if not host then + host = [=[{domain:[^/]+}]=] + + else + host = str_reverse(host) + if host:sub(#host) == "*" then + host = host:sub(1, #host - 1) .. "{prefix:.*}" + end + end + + core.log.info("route rule: ", host .. route.value.uri) + route_items[idx] = { + path = host .. route.value.uri, + method = route.value.methods, + handler = function (params, api_ctx) + api_ctx.matched_params = params + api_ctx.matched_route = route + end + } + end + end + + core.log.info("route items: ", core.json.delay_encode(route_items, true)) + local r3 = r3router.new(route_items) + r3:compile() + return r3 +end + + + local match_opts = {} +function _M.match(api_ctx) + local router, err = core.lrucache.global("/routes", routes.conf_version, + create_r3_router, routes.values) + if not router then + core.log.error("failed to fetch http router: ", err) + return core.response.exit(404) + end + + core.table.clear(match_opts) + match_opts.method = api_ctx.var.method + + local host = api_ctx.var.host + host = host and str_reverse(host) or "[^/]+" + host = host .. api_ctx.var.uri + core.log.info("match string: ", host) + + local ok = router:dispatch2(nil, host, match_opts, api_ctx) + if not ok then + core.log.info("not find any matched route") + return core.response.exit(404) + end + + return true +end + + +function _M.routes() + if not routes then + return nil, nil + end + + return routes.values, routes.conf_version +end + + +function _M.init_worker() + local err + routes, err = core.config.new("/routes", { + automatic = true, + item_schema = core.schema.route + }) + if not routes then + error("failed to create etcd instance for fetching routes : " .. err) + end +end + + +return _M diff --git a/lua/apisix/http/router/r3_uri.lua b/lua/apisix/http/router/r3_uri.lua index 8a0959425d99..d8cbf7d3b3e5 100644 --- a/lua/apisix/http/router/r3_uri.lua +++ b/lua/apisix/http/router/r3_uri.lua @@ -4,11 +4,9 @@ local require = require local r3router = require("resty.r3") local core = require("apisix.core") local plugin = require("apisix.plugin") -local local_conf = core.config.local_conf local ipairs = ipairs local type = type local error = error -local str_reverse = string.reverse local routes @@ -35,46 +33,18 @@ local function create_r3_router(routes) end end - local conf = local_conf() - local route_idx = conf and conf.apisix and conf.apisix.route_idx - for _, route in ipairs(routes) do if type(route) == "table" then idx = idx + 1 - if route_idx == "host+uri" then - local host = route.value.host - if not host then - host = [=[{domain:[^/]+}]=] - - else - host = str_reverse(host) - if host:sub(#host) == "*" then - host = host:sub(1, #host - 1) .. "{prefix:.*}" - end + route_items[idx] = { + path = route.value.uri, + method = route.value.methods, + host = route.value.host, + handler = function (params, api_ctx) + api_ctx.matched_params = params + api_ctx.matched_route = route end - - core.log.info("route rule: ", host .. route.value.uri) - route_items[idx] = { - path = host .. route.value.uri, - method = route.value.methods, - handler = function (params, api_ctx) - api_ctx.matched_params = params - api_ctx.matched_route = route - end - } - - else - route_items[idx] = { - path = route.value.uri, - method = route.value.methods, - host = route.value.host, - handler = function (params, api_ctx) - api_ctx.matched_params = params - api_ctx.matched_route = route - end - } - end - + } end end @@ -127,9 +97,6 @@ function _M.init_worker() if not routes then error("failed to create etcd instance for fetching routes : " .. err) end - - - require("apisix.http.balancer").init_worker() end diff --git a/t/node/match-host-uri.t b/t/node/match-host-uri.t index e86a2a0f9f7a..a6bf07ff8e24 100644 --- a/t/node/match-host-uri.t +++ b/t/node/match-host-uri.t @@ -18,7 +18,7 @@ sub read_file($) { our $yaml_config = read_file("conf/config.yaml"); $yaml_config =~ s/node_listen: 9080/node_listen: 1984/; $yaml_config =~ s/enable_heartbeat: true/enable_heartbeat: false/; -$yaml_config =~ s/route_idx: 'uri'/route_idx: 'host+uri'/; +$yaml_config =~ s/http: 'r3_uri'/http: 'r3_host_uri'/; run_tests(); From 364f72a93fd34bfd3366cb93db28cc39d8b6a399 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Sun, 4 Aug 2019 09:54:30 +0800 Subject: [PATCH 3/4] doc(architecture): added new doc about `router`. --- conf/config.yaml | 3 +- doc/architecture-design-cn.md | 18 ++++- lua/apisix/http/router/r3_host_uri.lua | 106 +++++++++++++++---------- t/node/match-host-uri.t | 91 +++++++++++++++++++-- 4 files changed, 165 insertions(+), 53 deletions(-) diff --git a/conf/config.yaml b/conf/config.yaml index 0fed84e409cc..98014dce9d0f 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -12,7 +12,8 @@ apisix: - 127.0.0.1 - 'unix:' router: - http: 'r3_uri' + http: 'r3_uri' # r3_uri: match route by uri(base on r3) + # r3_host_uri: match route by host + uri(base on r3) ssl: 'r3_sni' etcd: diff --git a/doc/architecture-design-cn.md b/doc/architecture-design-cn.md index c4b8e8a93ece..684fe29e9810 100644 --- a/doc/architecture-design-cn.md +++ b/doc/architecture-design-cn.md @@ -6,6 +6,7 @@ - [**Consumer**](#consumer) - [**Plugin**](#plugin) - [**Upstream**](#upstream) +- [**Router**](#router) - [**Debug mode**](#Debug-mode) ## apisix @@ -320,11 +321,26 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -X PUT -d ' }' ``` -更多细节可以参考[健康检查的文档](health-check.md). +更多细节可以参考[健康检查的文档](health-check.md)。 [返回目录](#目录) +## Router + +APISIX 区别于其他 API 网关的一大特点是允许用户选择不同路由实现来更好匹配自由业务。这样可以更好的在性能、自由之间做最佳选择。 + +在本地配置 `conf/config.yaml` 中设置最符合自身业务需求的路由,可获得更好的匹配效率。 + +* `apisix.router.http`: HTTP 请求路由。 + * `r3_uri`: 默认值,只使用 `uri` 作为主索引(基于 r3 引擎)。 + * `r3_host_uri`: 使用 `host + uri` 作为主索引(基于 r3 引擎),对当前请求会同时匹配 host 和 uri。 + +* `apisix.router.ssl`: SSL 加载匹配路由。 + * `r3_sni`: 默认值,使用 `SNI` (Server Name Indication) 作为主索引(基于 r3 引擎)。 + +[返回目录](#目录) + ## Debug mode 开启调试模式后,会在请求应答时,输出更多的内部信息,比如加载了哪些插件等。 diff --git a/lua/apisix/http/router/r3_host_uri.lua b/lua/apisix/http/router/r3_host_uri.lua index 1cb823b9b413..656ff7035d4f 100644 --- a/lua/apisix/http/router/r3_host_uri.lua +++ b/lua/apisix/http/router/r3_host_uri.lua @@ -13,20 +13,17 @@ local routes local _M = {version = 0.1} - - local empty_tab = {} - local route_items -local function create_r3_router(routes) - routes = routes or empty_tab - + local api_routes = {} + local api_router +local function create_api_router() local api_routes = plugin.api_routes() - route_items = core.table.new(#api_routes + #routes, 0) - local idx = 0 + core.table.clear(api_routes) + local idx = 0 for _, route in ipairs(api_routes) do if type(route) == "table" then idx = idx + 1 - route_items[idx] = { + api_routes[idx] = { path = route.uri, handler = route.handler, method = route.methods, @@ -34,35 +31,57 @@ local function create_r3_router(routes) end end - for _, route in ipairs(routes) do - if type(route) == "table" then - idx = idx + 1 + api_router = r3router.new(api_routes) + api_router:compile() + return true +end - local host = route.value.host - if not host then - host = [=[{domain:[^/]+}]=] - - else - host = str_reverse(host) - if host:sub(#host) == "*" then - host = host:sub(1, #host - 1) .. "{prefix:.*}" - end - end - - core.log.info("route rule: ", host .. route.value.uri) - route_items[idx] = { - path = host .. route.value.uri, - method = route.value.methods, - handler = function (params, api_ctx) - api_ctx.matched_params = params - api_ctx.matched_route = route - end - } + + local req_routes = {} + local req_routes_idx = 0 +local function push_valid_route(route) + if type(route) ~= "table" then + return + end + + local host = route.value.host + if not host then + core.log.error("missing `host` field in route: ", + core.json.delay_encode(route)) + return + end + + host = str_reverse(host) + if host:sub(#host) == "*" then + host = host:sub(1, #host - 1) .. "{host_prefix}" + end + + core.log.info("route rule: ", host .. route.value.uri) + req_routes_idx = req_routes_idx + 1 + req_routes[req_routes_idx] = { + path = "/" .. host .. route.value.uri, + method = route.value.methods, + handler = function (params, api_ctx) + api_ctx.matched_params = params + api_ctx.matched_route = route end + } + + return +end + +local function create_r3_router(routes) + create_api_router() + + core.table.clear(req_routes) + req_routes_idx = 0 + + for _, route in ipairs(routes or {}) do + push_valid_route(route) end - core.log.info("route items: ", core.json.delay_encode(route_items, true)) - local r3 = r3router.new(route_items) + core.log.info("route items: ", core.json.delay_encode(req_routes, true)) + local r3 = r3router.new(req_routes) r3:compile() return r3 end @@ -80,18 +99,19 @@ function _M.match(api_ctx) core.table.clear(match_opts) match_opts.method = api_ctx.var.method - local host = api_ctx.var.host - host = host and str_reverse(host) or "[^/]+" - host = host .. api_ctx.var.uri - core.log.info("match string: ", host) + local host_uri = "/" .. str_reverse(api_ctx.var.host) .. api_ctx.var.uri + local ok = router:dispatch2(nil, host_uri, match_opts, api_ctx) + if ok then + return true + end - local ok = router:dispatch2(nil, host, match_opts, api_ctx) - if not ok then - core.log.info("not find any matched route") - return core.response.exit(404) + ok = router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) + if ok then + return true end - return true + core.log.info("not find any matched route") + return core.response.exit(404) end diff --git a/t/node/match-host-uri.t b/t/node/match-host-uri.t index a6bf07ff8e24..4ff725410ea2 100644 --- a/t/node/match-host-uri.t +++ b/t/node/match-host-uri.t @@ -135,11 +135,12 @@ moc.oof/hello "methods": ["GET"], "upstream": { "nodes": { - "127.0.0.1:1980": 1 + "127.0.0.1:1981": 1 }, "type": "roundrobin" }, - "uri": "/hello" + "host": "foo.com", + "uri": "/server_port" }]] ) @@ -161,10 +162,10 @@ passed === TEST 8: /not_found --- request -GET /hello2 +GET /hello --- yaml_config eval: $::yaml_config --- more_headers -Host: not_found.com +Host: foo.com --- error_code: 404 --- response_body eval qr/404 Not Found/ @@ -175,23 +176,97 @@ qr/404 Not Found/ === TEST 9: hit routes --- request -GET /hello +GET /server_port --- yaml_config eval: $::yaml_config --- more_headers Host: foo.com +--- response_body_like eval +qr/1981/ +--- no_error_log +[error] + + + +=== TEST 10: set route(id: 2) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "upstream": { + "nodes": { + "127.0.0.1:1981": 1 + }, + "type": "roundrobin" + }, + "host": "foo.com", + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- yaml_config eval: $::yaml_config +--- request +GET /t --- response_body -hello world +passed --- no_error_log [error] ---- LAST -=== TEST 10: hit routes +=== TEST 11: /not_found +--- request +GET /hello2 +--- yaml_config eval: $::yaml_config +--- error_code: 404 +--- response_body eval +qr/404 Not Found/ +--- no_error_log +[error] + + + +=== TEST 12: hit routes --- request GET /hello --- yaml_config eval: $::yaml_config +--- more_headers +Host: foo.com --- response_body hello world --- no_error_log [error] + + + +=== TEST 13: delete route(id: 2) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_DELETE + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- yaml_config eval: $::yaml_config +--- request +GET /t +--- response_body +passed +--- no_error_log +[error] From b74e6178f03d6eca492dd56c89cf85e691388e67 Mon Sep 17 00:00:00 2001 From: Yuansheng Date: Sun, 4 Aug 2019 22:52:16 +0800 Subject: [PATCH 4/4] optimize(router): used local cache variable to avoid one lrucache call. --- lua/apisix/http/router/r3_host_uri.lua | 86 ++++++++++++++------------ lua/apisix/http/router/r3_uri.lua | 59 +++++++++--------- t/node/match-host-uri.t | 8 +-- 3 files changed, 77 insertions(+), 76 deletions(-) diff --git a/lua/apisix/http/router/r3_host_uri.lua b/lua/apisix/http/router/r3_host_uri.lua index 656ff7035d4f..76e4868df89b 100644 --- a/lua/apisix/http/router/r3_host_uri.lua +++ b/lua/apisix/http/router/r3_host_uri.lua @@ -8,37 +8,35 @@ local ipairs = ipairs local type = type local error = error local str_reverse = string.reverse -local routes +local user_routes +local cached_version -local _M = {version = 0.1} +local _M = {version = 0.2} - local api_routes = {} - local api_router -local function create_api_router() - local api_routes = plugin.api_routes() - core.table.clear(api_routes) + local only_uri_routes = {} + local only_uri_router +local function create_only_uri_router() + local routes = plugin.api_routes() - local idx = 0 - for _, route in ipairs(api_routes) do + for _, route in ipairs(routes) do if type(route) == "table" then - idx = idx + 1 - api_routes[idx] = { + core.table.insert(only_uri_routes, { path = route.uri, handler = route.handler, method = route.methods, - } + }) end end - api_router = r3router.new(api_routes) - api_router:compile() + only_uri_router = r3router.new(only_uri_routes) + only_uri_router:compile() return true end - local req_routes = {} - local req_routes_idx = 0 + local host_uri_routes = {} + local host_uri_router local function push_valid_route(route) if type(route) ~= "table" then return @@ -46,8 +44,14 @@ local function push_valid_route(route) local host = route.value.host if not host then - core.log.error("missing `host` field in route: ", - core.json.delay_encode(route)) + core.table.insert(only_uri_routes, { + path = route.value.uri, + method = route.value.methods, + handler = function (params, api_ctx) + api_ctx.matched_params = params + api_ctx.matched_route = route + end + }) return end @@ -57,42 +61,44 @@ local function push_valid_route(route) end core.log.info("route rule: ", host .. route.value.uri) - req_routes_idx = req_routes_idx + 1 - req_routes[req_routes_idx] = { + core.table.insert(host_uri_routes, { path = "/" .. host .. route.value.uri, method = route.value.methods, handler = function (params, api_ctx) api_ctx.matched_params = params api_ctx.matched_route = route end - } + }) return end local function create_r3_router(routes) - create_api_router() - - core.table.clear(req_routes) - req_routes_idx = 0 + core.table.clear(only_uri_routes) + core.table.clear(host_uri_routes) for _, route in ipairs(routes or {}) do push_valid_route(route) end - core.log.info("route items: ", core.json.delay_encode(req_routes, true)) - local r3 = r3router.new(req_routes) - r3:compile() - return r3 + create_only_uri_router() + + core.log.info("route items: ", + core.json.delay_encode(host_uri_routes, true)) + host_uri_router = r3router.new(host_uri_routes) + host_uri_router:compile() end local match_opts = {} function _M.match(api_ctx) - local router, err = core.lrucache.global("/routes", routes.conf_version, - create_r3_router, routes.values) - if not router then - core.log.error("failed to fetch http router: ", err) + if not cached_version or cached_version ~= user_routes.conf_version then + create_r3_router(user_routes.values) + cached_version = user_routes.conf_version + end + + if not host_uri_router then + core.log.error("failed to fetch valid `host+uri` router: ") return core.response.exit(404) end @@ -100,12 +106,12 @@ function _M.match(api_ctx) match_opts.method = api_ctx.var.method local host_uri = "/" .. str_reverse(api_ctx.var.host) .. api_ctx.var.uri - local ok = router:dispatch2(nil, host_uri, match_opts, api_ctx) + local ok = host_uri_router:dispatch2(nil, host_uri, match_opts, api_ctx) if ok then return true end - ok = router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) + ok = only_uri_router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) if ok then return true end @@ -116,22 +122,22 @@ end function _M.routes() - if not routes then + if not user_routes then return nil, nil end - return routes.values, routes.conf_version + return user_routes.values, user_routes.conf_version end function _M.init_worker() local err - routes, err = core.config.new("/routes", { + user_routes, err = core.config.new("/routes", { automatic = true, item_schema = core.schema.route }) - if not routes then - error("failed to create etcd instance for fetching routes : " .. err) + if not user_routes then + error("failed to create etcd instance for fetching /routes : " .. err) end end diff --git a/lua/apisix/http/router/r3_uri.lua b/lua/apisix/http/router/r3_uri.lua index d8cbf7d3b3e5..556b7c238284 100644 --- a/lua/apisix/http/router/r3_uri.lua +++ b/lua/apisix/http/router/r3_uri.lua @@ -7,36 +7,34 @@ local plugin = require("apisix.plugin") local ipairs = ipairs local type = type local error = error -local routes +local user_routes +local cached_version local _M = {version = 0.1} - local empty_tab = {} - local route_items + local uri_routes = {} + local uri_router local function create_r3_router(routes) - routes = routes or empty_tab + routes = routes or {} local api_routes = plugin.api_routes() - route_items = core.table.new(#api_routes + #routes, 0) - local idx = 0 + core.table.clear(uri_routes) for _, route in ipairs(api_routes) do if type(route) == "table" then - idx = idx + 1 - route_items[idx] = { + core.table.insert(uri_routes, { path = route.uri, handler = route.handler, method = route.methods, - } + }) end end for _, route in ipairs(routes) do if type(route) == "table" then - idx = idx + 1 - route_items[idx] = { + core.table.insert(uri_routes, { path = route.value.uri, method = route.value.methods, host = route.value.host, @@ -44,23 +42,25 @@ local function create_r3_router(routes) api_ctx.matched_params = params api_ctx.matched_route = route end - } + }) end end - core.log.info("route items: ", core.json.delay_encode(route_items, true)) - local r3 = r3router.new(route_items) - r3:compile() - return r3 + core.log.info("route items: ", core.json.delay_encode(uri_routes, true)) + uri_router = r3router.new(uri_routes) + uri_router:compile() end local match_opts = {} function _M.match(api_ctx) - local router, err = core.lrucache.global("/routes", routes.conf_version, - create_r3_router, routes.values) - if not router then - core.log.error("failed to fetch http router: ", err) + if not cached_version or cached_version ~= user_routes.conf_version then + create_r3_router(user_routes.values) + cached_version = user_routes.conf_version + end + + if not uri_router then + core.log.error("failed to fetch valid `uri` router: ") return core.response.exit(404) end @@ -68,7 +68,7 @@ function _M.match(api_ctx) match_opts.method = api_ctx.var.method match_opts.host = api_ctx.var.host - local ok = router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) + local ok = uri_router:dispatch2(nil, api_ctx.var.uri, match_opts, api_ctx) if not ok then core.log.info("not find any matched route") return core.response.exit(404) @@ -79,23 +79,22 @@ end function _M.routes() - if not routes then + if not user_routes then return nil, nil end - return routes.values, routes.conf_version + return user_routes.values, user_routes.conf_version end function _M.init_worker() local err - routes, err = core.config.new("/routes", - { - automatic = true, - item_schema = core.schema.route - }) - if not routes then - error("failed to create etcd instance for fetching routes : " .. err) + user_routes, err = core.config.new("/routes", { + automatic = true, + item_schema = core.schema.route + }) + if not user_routes then + error("failed to create etcd instance for fetching /routes : " .. err) end end diff --git a/t/node/match-host-uri.t b/t/node/match-host-uri.t index 4ff725410ea2..b73e657e6619 100644 --- a/t/node/match-host-uri.t +++ b/t/node/match-host-uri.t @@ -139,7 +139,6 @@ moc.oof/hello }, "type": "roundrobin" }, - "host": "foo.com", "uri": "/server_port" }]] ) @@ -164,8 +163,6 @@ passed --- request GET /hello --- yaml_config eval: $::yaml_config ---- more_headers -Host: foo.com --- error_code: 404 --- response_body eval qr/404 Not Found/ @@ -179,7 +176,7 @@ qr/404 Not Found/ GET /server_port --- yaml_config eval: $::yaml_config --- more_headers -Host: foo.com +Host: anydomain.com --- response_body_like eval qr/1981/ --- no_error_log @@ -202,7 +199,6 @@ qr/1981/ }, "type": "roundrobin" }, - "host": "foo.com", "uri": "/hello" }]] ) @@ -240,7 +236,7 @@ qr/404 Not Found/ GET /hello --- yaml_config eval: $::yaml_config --- more_headers -Host: foo.com +Host: anydomain.com --- response_body hello world --- no_error_log