diff --git a/apisix/balancer.lua b/apisix/balancer.lua index 145856f959bb..36f4f32d4b28 100644 --- a/apisix/balancer.lua +++ b/apisix/balancer.lua @@ -192,6 +192,7 @@ local function pick_server(route, ctx) local version = ctx.upstream_version local key = ctx.upstream_key local checker = fetch_healthchecker(up_conf, healthcheck_parent, version) + ctx.up_checker = checker ctx.balancer_try_count = (ctx.balancer_try_count or 0) + 1 if checker and ctx.balancer_try_count > 1 then diff --git a/apisix/init.lua b/apisix/init.lua index 354290dc4c2a..99f75e2ddbbc 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -567,9 +567,64 @@ function _M.http_body_filter_phase() end -function _M.http_log_phase() +local function healcheck_passive(api_ctx) + local checker = api_ctx.up_checker + if not checker then + return + end + + local up_conf = api_ctx.upstream_conf + local passive = up_conf.checks.passive + if not passive then + return + end + + core.log.info("enabled healthcheck passive") + local host = up_conf.checks and up_conf.checks.active + and up_conf.checks.active.host + local port = up_conf.checks and up_conf.checks.active + and up_conf.checks.active.port + + local resp_status = ngx.status + local http_statuses = passive and passive.healthy and + passive.healthy.http_statuses + core.log.info("passive.healthy.http_statuses: ", + core.json.delay_encode(http_statuses)) + if http_statuses then + for i, status in ipairs(http_statuses) do + if resp_status == status then + checker:report_http_status(api_ctx.balancer_ip, + port or api_ctx.balancer_port, + host, + resp_status) + end + end + end + local http_statuses = passive and passive.unhealthy and + passive.unhealthy.http_statuses + core.log.info("passive.unhealthy.http_statuses: ", + core.json.delay_encode(http_statuses)) + if not http_statuses then + return + end + + for i, status in ipairs(http_statuses) do + for i, status in ipairs(http_statuses) do + if resp_status == status then + checker:report_http_status(api_ctx.balancer_ip, + port or api_ctx.balancer_port, + host, + resp_status) + end + end + end +end + + +function _M.http_log_phase() local api_ctx = common_phase("log") + healcheck_passive(api_ctx) if api_ctx.uri_parse_param then core.tablepool.release("uri_parse_param", api_ctx.uri_parse_param) diff --git a/t/node/healthcheck-passive.t b/t/node/healthcheck-passive.t new file mode 100644 index 000000000000..edc6017d78e1 --- /dev/null +++ b/t/node/healthcheck-passive.t @@ -0,0 +1,121 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +use t::APISIX 'no_plan'; + +master_on(); +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); +worker_connections(256); + +run_tests(); + +__DATA__ + +=== TEST 1: set route(only passive) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/server_port", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 0, + "127.0.0.1:1": 1 + }, + "retries": 0, + "checks": { + "active": { + "http_path": "/status", + "host": "foo.com", + "healthy": { + "interval": 100, + "successes": 1 + }, + "unhealthy": { + "interval": 100, + "http_failures": 2 + } + }, + "passive": { + "healthy": { + "http_statuses": [200, 201], + "successes": 3 + }, + "unhealthy": { + "http_statuses": [502], + "http_failures": 1, + "tcp_failures": 1 + } + } + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: hit routes (two healthy nodes) +--- config + location /t { + content_by_lua_block { + ngx.sleep(1) -- wait for sync + + local json_sort = require("lib.json_sort") + local http = require("resty.http") + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/server_port" + + local ports_count = {} + for i = 1, 6 do + local httpc = http.new() + local res, err = httpc:request_uri(uri, {method = "GET", keepalive = false}) + if not res then + ngx.say(err) + return + end + + local status = tostring(res.status) + ports_count[status] = (ports_count[status] or 0) + 1 + end + + ngx.say(json_sort.encode(ports_count)) + ngx.exit(200) + } + } +--- request +GET /t +--- response_body +{"200":5,"502":1} +--- error_log +(upstream#/apisix/routes/1) unhealthy HTTP increment (1/1)