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

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


The following commit(s) were added to refs/heads/master by this push:
     new 9718c69d6 fix: request failure during reload after any Eureka node 
fails (#12906)
9718c69d6 is described below

commit 9718c69d6698945a1dc69057fbb1df5b9b94b1a9
Author: YYYoung <[email protected]>
AuthorDate: Fri Jan 16 10:54:50 2026 +0800

    fix: request failure during reload after any Eureka node fails (#12906)
---
 apisix/discovery/eureka/init.lua | 88 ++++++++++++++++++++++++----------------
 t/discovery/eureka.t             | 50 +++++++++++++++++++++++
 2 files changed, 102 insertions(+), 36 deletions(-)

diff --git a/apisix/discovery/eureka/init.lua b/apisix/discovery/eureka/init.lua
index df72a5269..2625524d9 100644
--- a/apisix/discovery/eureka/init.lua
+++ b/apisix/discovery/eureka/init.lua
@@ -39,34 +39,37 @@ local _M = {
 }
 
 
-local function service_info()
-    local host = local_conf.discovery and
+-- build all eureka endpoints from config
+local function build_endpoints()
+    local host_list = local_conf.discovery and
         local_conf.discovery.eureka and local_conf.discovery.eureka.host
-    if not host then
+    if not host_list or #host_list == 0 then
         log.error("do not set eureka.host")
-        return
-    end
-
-    local basic_auth
-    -- TODO Add health check to get healthy nodes.
-    local url = host[math_random(#host)]
-    local auth_idx = str_find(url, "@")
-    if auth_idx then
-        local protocol_idx = str_find(url, "://")
-        local protocol = string_sub(url, 1, protocol_idx + 2)
-        local user_and_password = string_sub(url, protocol_idx + 3, auth_idx - 
1)
-        local other = string_sub(url, auth_idx + 1)
-        url = protocol .. other
-        basic_auth = "Basic " .. ngx.encode_base64(user_and_password)
-    end
-    if local_conf.discovery.eureka.prefix then
-        url = url .. local_conf.discovery.eureka.prefix
-    end
-    if string_sub(url, #url) ~= "/" then
-        url = url .. "/"
+        return nil
+    end
+
+    local built_endpoints = core.table.new(#host_list, 0)
+    for _, h in ipairs(host_list) do
+        local url = h
+        local basic_auth
+        local auth_idx = str_find(url, "@")
+        if auth_idx then
+            local protocol_idx = str_find(url, "://")
+            local protocol = string_sub(url, 1, protocol_idx + 2)
+            local user_and_password = string_sub(url, protocol_idx + 3, 
auth_idx - 1)
+            local other = string_sub(url, auth_idx + 1)
+            url = protocol .. other
+            basic_auth = "Basic " .. ngx.encode_base64(user_and_password)
+        end
+        if local_conf.discovery.eureka.prefix then
+            url = url .. local_conf.discovery.eureka.prefix
+        end
+        if string_sub(url, #url) ~= "/" then
+            url = url .. "/"
+        end
+        core.table.insert(built_endpoints, { url = url, auth = basic_auth })
     end
-
-    return url, basic_auth
+    return built_endpoints
 end
 
 
@@ -145,26 +148,37 @@ local function fetch_full_registry(premature)
         return
     end
 
-    local request_uri, basic_auth = service_info()
-    if not request_uri then
+    local endpoints = build_endpoints()
+    if not endpoints or #endpoints == 0 then
         return
     end
 
-    local res, err = request(request_uri, basic_auth, "GET", "apps")
-    if not res then
-        log.error("failed to fetch registry", err)
-        return
+    -- try endpoints from random position, failover on error
+    local selected_endpoint
+    local selected_body
+    local num_endpoints = #endpoints
+    local start = math_random(num_endpoints)
+    for i = 0, num_endpoints - 1 do
+        local endpoint = endpoints[(start + i - 1) % num_endpoints + 1]
+        local r, e = request(endpoint.url, endpoint.auth, "GET", "apps")
+        if r and r.body and r.status == 200 then
+            selected_endpoint = endpoint
+            selected_body = r.body
+            break
+        end
+        log.error("failed to fetch registry from ", endpoint.url, ": ",
+                 e or (r and ("status=" .. tostring(r.status)) or "unknown"))
     end
 
-    if not res.body or res.status ~= 200 then
-        log.error("failed to fetch registry, status = ", res.status)
+    if not selected_endpoint then
+        log.error("failed to fetch registry from all eureka hosts, ",
+                  "no healthy endpoint found, tried ", num_endpoints, " 
host(s)")
         return
     end
 
-    local json_str = res.body
-    local data, err = core.json.decode(json_str)
+    local data, err = core.json.decode(selected_body)
     if not data then
-        log.error("invalid response body: ", json_str, " err: ", err)
+        log.error("invalid response body: ", selected_body, " err: ", err)
         return
     end
     local apps = data.applications.application
@@ -192,6 +206,8 @@ local function fetch_full_registry(premature)
         end
     end
     applications = up_apps
+    log.info("successfully updated service registry, services count=",
+             core.table.nkeys(up_apps), "; source=", selected_endpoint.url)
 end
 
 
diff --git a/t/discovery/eureka.t b/t/discovery/eureka.t
index 384ba7ba6..da76912d3 100644
--- a/t/discovery/eureka.t
+++ b/t/discovery/eureka.t
@@ -115,3 +115,53 @@ default_weight:80.
 fetch_interval:10.
 eureka uri:http://127.0.0.1:8761/eureka/.
 connect_timeout:1500, send_timeout:1500, read_timeout:1500.
+
+
+
+=== TEST 4: fallback to next eureka host when current host fails
+--- yaml_config
+apisix:
+    node_listen: 1984
+deployment:
+    role: data_plane
+    role_data_plane:
+        config_provider: yaml
+discovery:
+    eureka:
+        host:
+            - "http://127.0.0.1:20997";
+            - "http://127.0.0.1:8761";
+        prefix: "/eureka/"
+        fetch_interval: 1
+        weight: 80
+        timeout:
+            connect: 1500
+            send: 1500
+            read: 1500
+--- apisix_yaml
+routes:
+    -
+        uri: /eureka/*
+        upstream:
+            service_name: APISIX-EUREKA
+            discovery_type: eureka
+            type: roundrobin
+#END
+--- http_config
+    server {
+        listen 20997;
+
+        location / {
+            return 502;
+        }
+    }
+--- request
+GET /eureka/apps/APISIX-EUREKA
+--- response_body_like
+.*<name>APISIX-EUREKA</name>.*
+--- error_log
+failed to fetch registry from http://127.0.0.1:20997/eureka/: status=502
+successfully updated service registry
+--- no_error_log
+failed to fetch registry from all eureka hosts
+failed to fetch registry from http://127.0.0.1:8761/eureka/

Reply via email to