This is an automated email from the ASF dual-hosted git repository.
liuhongyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 71ff9d26e0 [ISSUE #6235] Set upstream check for each upstream. (#6272)
71ff9d26e0 is described below
commit 71ff9d26e01e233fc571eac02e55f5ee86a65bb0
Author: jonasHanhan <[email protected]>
AuthorDate: Fri Jan 16 11:06:56 2026 +0800
[ISSUE #6235] Set upstream check for each upstream. (#6272)
* fix common : support per-upstream health check.
* fix admin : sanitize upstream url parsing.
* fix common : align health check flag handling.
---------
Co-authored-by: aias00 <[email protected]>
---
.../admin/service/impl/UpstreamCheckService.java | 24 +++++++
.../shenyu/admin/transfer/DiscoveryTransfer.java | 40 +++++++++--
.../shenyu/admin/utils/CommonUpstreamUtils.java | 10 ++-
.../admin/service/UpstreamCheckServiceTest.java | 82 ++++++++++++++++++++++
.../dto/convert/selector/CommonUpstream.java | 30 +++++++-
.../dto/convert/selector/DivideUpstream.java | 17 +++++
.../common/dto/convert/selector/GrpcUpstream.java | 17 +++++
.../loadbalancer/cache/UpstreamCacheManager.java | 10 +++
.../loadbalancer/cache/UpstreamCheckTask.java | 7 ++
.../shenyu/loadbalancer/entity/Upstream.java | 40 +++++++++++
.../cache/UpstreamCacheManagerTest.java | 29 +++++++-
.../loadbalancer/cache/UpstreamCheckTaskTest.java | 41 +++++++++++
.../divide/handler/DivideUpstreamDataHandler.java | 1 +
.../dubbo/proxy/ApacheDubboProxyService.java | 1 +
.../handler/AbstractDubboPluginDataHandler.java | 1 +
.../handler/GrpcDiscoveryUpstreamDataHandler.java | 20 ++++--
.../grpc/loadbalance/picker/ShenyuPicker.java | 1 +
.../handler/WebSocketUpstreamDataHandler.java | 1 +
.../web/controller/LocalPluginController.java | 1 +
19 files changed, 355 insertions(+), 18 deletions(-)
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java
index 5e559ebddd..7fcbee74fe 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/UpstreamCheckService.java
@@ -222,6 +222,8 @@ public class UpstreamCheckService {
if (!exists.isPresent()) {
upstreams.add(commonUpstream);
} else {
+ CommonUpstream existUpstream = exists.get();
+
existUpstream.setHealthCheckEnabled(commonUpstream.isHealthCheckEnabled());
LOG.info("upstream host {} is exists.",
commonUpstream.getUpstreamHost());
}
PENDING_SYNC.add(commonUpstream.hashCode());
@@ -251,6 +253,12 @@ public class UpstreamCheckService {
if (!REGISTER_TYPE_HTTP.equalsIgnoreCase(registerType) || !checked) {
return false;
}
+ if (!commonUpstream.isHealthCheckEnabled()) {
+ commonUpstream.setStatus(true);
+ commonUpstream.setTimestamp(System.currentTimeMillis());
+ this.submit(selectorId, commonUpstream);
+ return false;
+ }
final boolean pass =
UpstreamCheckUtils.checkUrl(commonUpstream.getUpstreamUrl());
if (pass) {
this.submit(selectorId, commonUpstream);
@@ -310,6 +318,14 @@ public class UpstreamCheckService {
ZOMBIE_SET.remove(zombieUpstream);
String selectorId = zombieUpstream.getSelectorId();
CommonUpstream commonUpstream = zombieUpstream.getCommonUpstream();
+ if (!commonUpstream.isHealthCheckEnabled()) {
+ commonUpstream.setTimestamp(System.currentTimeMillis());
+ commonUpstream.setStatus(true);
+ List<CommonUpstream> old =
ListUtils.unmodifiableList(UPSTREAM_MAP.getOrDefault(selectorId,
Collections.emptyList()));
+ this.submitJust(selectorId, commonUpstream);
+ updateHandler(selectorId, old, UPSTREAM_MAP.get(selectorId));
+ return;
+ }
final boolean pass =
UpstreamCheckUtils.checkUrl(commonUpstream.getUpstreamUrl());
if (pass) {
commonUpstream.setTimestamp(System.currentTimeMillis());
@@ -332,6 +348,14 @@ public class UpstreamCheckService {
final List<CompletableFuture<CommonUpstream>> checkFutures = new
ArrayList<>(upstreamList.size());
for (CommonUpstream commonUpstream : upstreamList) {
checkFutures.add(CompletableFuture.supplyAsync(() -> {
+ if (!commonUpstream.isHealthCheckEnabled()) {
+ if (!commonUpstream.isStatus()) {
+
commonUpstream.setTimestamp(System.currentTimeMillis());
+ commonUpstream.setStatus(true);
+ PENDING_SYNC.add(commonUpstream.hashCode());
+ }
+ return commonUpstream;
+ }
final boolean pass =
UpstreamCheckUtils.checkUrl(commonUpstream.getUpstreamUrl());
if (pass) {
if (!commonUpstream.isStatus()) {
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/transfer/DiscoveryTransfer.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/transfer/DiscoveryTransfer.java
index ff24cc9935..91f854cd5f 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/transfer/DiscoveryTransfer.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/transfer/DiscoveryTransfer.java
@@ -78,7 +78,14 @@ public enum DiscoveryTransfer {
public CommonUpstream mapToCommonUpstream(DiscoveryUpstreamData
discoveryUpstreamData) {
return Optional.ofNullable(discoveryUpstreamData).map(data -> {
String url = data.getUrl();
- return new CommonUpstream(data.getProtocol(), url.split(":")[0],
url, false, data.getDateCreated().getTime());
+ CommonUpstream commonUpstream = new
CommonUpstream(data.getProtocol(), url.split(":")[0], url, false,
+ data.getDateCreated().getTime());
+ Properties properties = Optional.ofNullable(data.getProps())
+ .map(props -> GsonUtils.getInstance().fromJson(props,
Properties.class))
+ .orElse(new Properties());
+ commonUpstream
+
.setHealthCheckEnabled(Boolean.parseBoolean(properties.getProperty("healthCheckEnabled",
"true")));
+ return commonUpstream;
}).orElse(null);
}
@@ -103,7 +110,6 @@ public enum DiscoveryTransfer {
}).orElse(null);
}
-
public DiscoveryRelVO mapToVo(DiscoveryRelDO discoveryRelDO) {
return Optional.ofNullable(discoveryRelDO).map(data -> {
DiscoveryRelVO discoveryRelVO = new DiscoveryRelVO();
@@ -116,7 +122,6 @@ public enum DiscoveryTransfer {
}).orElse(null);
}
-
public DiscoveryRelDO mapToDO(DiscoveryRelDTO discoveryRelDTO) {
return Optional.ofNullable(discoveryRelDTO).map(data -> {
DiscoveryRelDO discoveryRelDO = new DiscoveryRelDO();
@@ -335,13 +340,38 @@ public enum DiscoveryTransfer {
return discoveryUpstreamDTO;
}).orElse(null);
}
-
+
/**
* mapToDiscoveryUpstreamData.
+ *
* @param commonUpstream commonUpstream
* @return DiscoveryUpstreamData
*/
public DiscoveryUpstreamData mapToDiscoveryUpstreamData(CommonUpstream
commonUpstream) {
- return
mapToData(CommonUpstreamUtils.buildDefaultDiscoveryUpstreamDTO(commonUpstream.getUpstreamUrl().split(":")[0],
Integer.valueOf(commonUpstream.getUpstreamUrl().split(":")[1]),
commonUpstream.getProtocol(),commonUpstream.getNamespaceId()));
+ String upstreamUrl = commonUpstream.getUpstreamUrl();
+ String[] parts = Optional.ofNullable(upstreamUrl)
+ .map(url -> url.split(":", 2))
+ .orElseThrow(() -> new IllegalArgumentException("Upstream URL
must not be null"));
+ if (parts.length < 2) {
+ throw new IllegalArgumentException("Invalid upstream URL, expected
'host:port' format but was: " + upstreamUrl);
+ }
+ String host = parts[0];
+ int port;
+ try {
+ port = Integer.parseInt(parts[1]);
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("Invalid port in upstream URL:
" + upstreamUrl, ex);
+ }
+ DiscoveryUpstreamDTO discoveryUpstreamDTO =
CommonUpstreamUtils.buildDefaultDiscoveryUpstreamDTO(
+ host,
+ port,
+ commonUpstream.getProtocol(),
+ commonUpstream.getNamespaceId());
+ Properties properties =
Optional.ofNullable(discoveryUpstreamDTO.getProps())
+ .map(props -> GsonUtils.getInstance().fromJson(props,
Properties.class))
+ .orElse(new Properties());
+ properties.setProperty("healthCheckEnabled",
String.valueOf(commonUpstream.isHealthCheckEnabled()));
+
discoveryUpstreamDTO.setProps(GsonUtils.getInstance().toJson(properties));
+ return mapToData(discoveryUpstreamDTO);
}
}
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/CommonUpstreamUtils.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/CommonUpstreamUtils.java
index a6df53b953..6a6cdc5c2e 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/CommonUpstreamUtils.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/CommonUpstreamUtils.java
@@ -246,9 +246,13 @@ public class CommonUpstreamUtils {
return Optional.ofNullable(upstreamList)
.orElse(Collections.emptyList())
.stream()
- .map(upstream -> new CommonUpstream(upstream.getProtocol(),
- upstream.getUpstreamHost(), upstream.getUpstreamUrl(),
- upstream.isStatus(), upstream.getTimestamp()))
+ .map(upstream -> {
+ CommonUpstream commonUpstream = new
CommonUpstream(upstream.getProtocol(),
+ upstream.getUpstreamHost(),
upstream.getUpstreamUrl(),
+ upstream.isStatus(), upstream.getTimestamp());
+
commonUpstream.setHealthCheckEnabled(upstream.isHealthCheckEnabled());
+ return commonUpstream;
+ })
.collect(Collectors.toList());
}
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/UpstreamCheckServiceTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/UpstreamCheckServiceTest.java
index 04de274923..45875affdd 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/UpstreamCheckServiceTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/UpstreamCheckServiceTest.java
@@ -313,4 +313,86 @@ public final class UpstreamCheckServiceTest {
upstreamMap.put("UrlReachableAnother",
Collections.singletonList(divideUpstream1));
upstreamMap.put("UrlErrorAnother",
Collections.singletonList(divideUpstream2));
}
+
+ @Test
+ public void testCheckAndSubmitWithHealthCheckDisabled() {
+ ScheduledThreadPoolExecutor executor = new
ScheduledThreadPoolExecutor(1,
+ ShenyuThreadFactory.create("scheduled-upstream-task", false));
+ ReflectionTestUtils.setField(upstreamCheckService, "executor",
executor);
+
+ final DivideUpstream divideUpstream = DivideUpstream.builder()
+ .upstreamUrl("unreachable-url:8080")
+ .upstreamHost("unreachable-host")
+ .healthCheckEnabled(false)
+ .status(false)
+ .build();
+
+ boolean result = upstreamCheckService.checkAndSubmit("testSelector",
divideUpstream);
+
+ assertFalse(result);
+ assertTrue(divideUpstream.isStatus());
+ assertTrue(upstreamMap.containsKey("testSelector"));
+ }
+
+ @Test
+ public void testSubmitWithHealthCheckDisabled() {
+ ScheduledThreadPoolExecutor executor = new
ScheduledThreadPoolExecutor(1,
+ ShenyuThreadFactory.create("scheduled-upstream-task", false));
+ ReflectionTestUtils.setField(upstreamCheckService, "executor",
executor);
+
+ final DivideUpstream divideUpstream = DivideUpstream.builder()
+ .upstreamUrl("any-url:8080")
+ .upstreamHost("any-host")
+ .healthCheckEnabled(false)
+ .status(true)
+ .build();
+
+ upstreamCheckService.submit("testSelectorDisabled", divideUpstream);
+
+ assertTrue(upstreamMap.containsKey("testSelectorDisabled"));
+ assertEquals(1, upstreamMap.get("testSelectorDisabled").size());
+ }
+
+ @Test
+ public void testHealthCheckEnabledDefaultsToTrue() {
+ final DivideUpstream divideUpstream = DivideUpstream.builder()
+ .upstreamUrl("test-url:8080")
+ .upstreamHost("test-host")
+ .build();
+
+ assertTrue(divideUpstream.isHealthCheckEnabled());
+ }
+
+ @Test
+ public void testSubmitSyncsHealthCheckEnabledForExistingUpstream() {
+ ScheduledThreadPoolExecutor executor = new
ScheduledThreadPoolExecutor(1,
+ ShenyuThreadFactory.create("scheduled-upstream-task", false));
+ ReflectionTestUtils.setField(upstreamCheckService, "executor",
executor);
+
+ final String selectorId = "testSyncSelector";
+
+ final DivideUpstream upstream1 = DivideUpstream.builder()
+ .upstreamUrl("sync-url:8080")
+ .upstreamHost("sync-host")
+ .healthCheckEnabled(true)
+ .status(true)
+ .build();
+ upstreamCheckService.submit(selectorId, upstream1);
+
+ assertTrue(upstreamMap.containsKey(selectorId));
+ assertEquals(1, upstreamMap.get(selectorId).size());
+ assertTrue(upstreamMap.get(selectorId).get(0).isHealthCheckEnabled());
+
+ final DivideUpstream upstream2 = DivideUpstream.builder()
+ .upstreamUrl("sync-url:8080")
+ .upstreamHost("sync-host")
+ .healthCheckEnabled(false)
+ .status(true)
+ .build();
+ upstreamCheckService.submit(selectorId, upstream2);
+
+ assertEquals(1, upstreamMap.get(selectorId).size());
+ assertFalse(upstreamMap.get(selectorId).get(0).isHealthCheckEnabled());
+ assertTrue(upstreamMap.get(selectorId).get(0).isStatus());
+ }
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/CommonUpstream.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/CommonUpstream.java
index e9dd8c3a51..13f10e7b1f 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/CommonUpstream.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/CommonUpstream.java
@@ -59,6 +59,11 @@ public class CommonUpstream {
*/
private boolean gray;
+ /**
+ * health check enabled.
+ */
+ private boolean healthCheckEnabled = true;
+
/**
* Instantiates a new Common upstream.
*/
@@ -209,6 +214,24 @@ public class CommonUpstream {
this.gray = gray;
}
+ /**
+ * get healthCheckEnabled.
+ *
+ * @return healthCheckEnabled
+ */
+ public boolean isHealthCheckEnabled() {
+ return healthCheckEnabled;
+ }
+
+ /**
+ * set healthCheckEnabled.
+ *
+ * @param healthCheckEnabled healthCheckEnabled
+ */
+ public void setHealthCheckEnabled(final boolean healthCheckEnabled) {
+ this.healthCheckEnabled = healthCheckEnabled;
+ }
+
/**
* set namespaceId.
*
@@ -231,12 +254,13 @@ public class CommonUpstream {
&& Objects.equals(protocol, that.protocol)
&& Objects.equals(gray, that.gray)
&& Objects.equals(upstreamUrl, that.upstreamUrl)
- && Objects.equals(namespaceId, that.namespaceId);
+ && Objects.equals(namespaceId, that.namespaceId)
+ && Objects.equals(healthCheckEnabled, that.healthCheckEnabled);
}
@Override
public int hashCode() {
- return Objects.hash(upstreamHost, protocol, upstreamUrl, namespaceId,
gray);
+ return Objects.hash(upstreamHost, protocol, upstreamUrl, namespaceId,
gray, healthCheckEnabled);
}
@Override
@@ -259,6 +283,8 @@ public class CommonUpstream {
+ namespaceId
+ ", gray="
+ gray
+ + ", healthCheckEnabled="
+ + healthCheckEnabled
+ '}';
}
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/DivideUpstream.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/DivideUpstream.java
index 7f972cf170..ce1667138e 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/DivideUpstream.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/DivideUpstream.java
@@ -58,6 +58,7 @@ public class DivideUpstream extends CommonUpstream {
setTimestamp(builder.timestamp);
this.warmup = builder.warmup;
setNamespaceId(builder.namespaceId);
+ setHealthCheckEnabled(builder.healthCheckEnabled);
}
/**
@@ -200,6 +201,11 @@ public class DivideUpstream extends CommonUpstream {
*/
private String namespaceId;
+ /**
+ * healthCheckEnabled.
+ */
+ private boolean healthCheckEnabled = true;
+
/**
* no args constructor.
*/
@@ -303,5 +309,16 @@ public class DivideUpstream extends CommonUpstream {
this.namespaceId = namespaceId;
return this;
}
+
+ /**
+ * build healthCheckEnabled.
+ *
+ * @param healthCheckEnabled healthCheckEnabled
+ * @return this
+ */
+ public Builder healthCheckEnabled(final boolean healthCheckEnabled) {
+ this.healthCheckEnabled = healthCheckEnabled;
+ return this;
+ }
}
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/GrpcUpstream.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/GrpcUpstream.java
index 21eb75cf73..8379e719e8 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/GrpcUpstream.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/dto/convert/selector/GrpcUpstream.java
@@ -46,6 +46,7 @@ public final class GrpcUpstream extends CommonUpstream {
setStatus(statusValue);
setTimestamp(builder.timestamp);
setNamespaceId(builder.namespaceId);
+ setHealthCheckEnabled(builder.healthCheckEnabled);
}
/**
@@ -163,6 +164,11 @@ public final class GrpcUpstream extends CommonUpstream {
*/
private String namespaceId;
+ /**
+ * health check enabled.
+ */
+ private boolean healthCheckEnabled = true;
+
/**
* no args constructor.
*/
@@ -255,5 +261,16 @@ public final class GrpcUpstream extends CommonUpstream {
this.namespaceId = namespaceId;
return this;
}
+
+ /**
+ * build healthCheckEnabled.
+ *
+ * @param healthCheckEnabled healthCheckEnabled
+ * @return this
+ */
+ public Builder healthCheckEnabled(final boolean healthCheckEnabled) {
+ this.healthCheckEnabled = healthCheckEnabled;
+ return this;
+ }
}
}
diff --git
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
index 30a76ed410..eeff3c8195 100644
---
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
+++
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManager.java
@@ -146,6 +146,12 @@ public final class UpstreamCacheManager {
*/
public void submit(final String selectorId, final List<Upstream>
upstreamList) {
List<Upstream> actualUpstreamList = Objects.isNull(upstreamList) ?
Lists.newArrayList() : upstreamList;
+ actualUpstreamList.forEach(upstream -> {
+ if (!upstream.isHealthCheckEnabled()) {
+ upstream.setStatus(true);
+ upstream.setHealthy(true);
+ }
+ });
Map<Boolean, List<Upstream>> partitionedUpstreams =
actualUpstreamList.stream()
.collect(Collectors.partitioningBy(Upstream::isStatus));
List<Upstream> validUpstreamList = partitionedUpstreams.get(true);
@@ -173,6 +179,10 @@ public final class UpstreamCacheManager {
Upstream matchedExistUp = existUpstreamMap.get(key);
if (Objects.nonNull(matchedExistUp)) {
matchedExistUp.setWeight(validUp.getWeight());
+
matchedExistUp.setHealthCheckEnabled(validUp.isHealthCheckEnabled());
+ if (!matchedExistUp.isHealthCheckEnabled()) {
+ matchedExistUp.setHealthy(true);
+ }
}
});
diff --git
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
index 359aa06253..eef5cc47c5 100644
---
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
+++
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java
@@ -190,6 +190,13 @@ public final class UpstreamCheckTask implements Runnable {
}
private UpstreamWithSelectorId check(final String selectorId, final
Upstream upstream) {
+ if (!upstream.isHealthCheckEnabled()) {
+ if (!upstream.isHealthy()) {
+ upstream.setHealthy(true);
+ upstream.setLastHealthTimestamp(System.currentTimeMillis());
+ }
+ return new UpstreamWithSelectorId(selectorId, upstream);
+ }
boolean pass = UpstreamCheckUtils.checkUrl(upstream.getUrl(),
checkTimeout);
if (pass) {
if (upstream.isHealthy()) {
diff --git
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
index c815d67e28..36656e3493 100644
---
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
+++
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
@@ -104,6 +104,11 @@ public final class Upstream {
* this is gray.
*/
private boolean gray;
+
+ /**
+ * health check enabled.
+ */
+ private boolean healthCheckEnabled = true;
private Map<String, String> metadata = new ConcurrentHashMap<>();
@@ -127,6 +132,7 @@ public final class Upstream {
this.group = builder.group;
this.version = builder.version;
this.gray = builder.gray;
+ this.healthCheckEnabled = builder.healthCheckEnabled;
}
/**
@@ -227,6 +233,24 @@ public final class Upstream {
public void setHealthy(final boolean healthy) {
this.healthy = healthy;
}
+
+ /**
+ * Is health check enabled.
+ *
+ * @return the boolean
+ */
+ public boolean isHealthCheckEnabled() {
+ return healthCheckEnabled;
+ }
+
+ /**
+ * Sets health check enabled.
+ *
+ * @param healthCheckEnabled the health check enabled
+ */
+ public void setHealthCheckEnabled(final boolean healthCheckEnabled) {
+ this.healthCheckEnabled = healthCheckEnabled;
+ }
/**
* Gets last health timestamp.
@@ -552,6 +576,11 @@ public final class Upstream {
*/
private Boolean gray = false;
+ /**
+ * health check enabled.
+ */
+ private boolean healthCheckEnabled = true;
+
/**
* no args constructor.
*/
@@ -667,5 +696,16 @@ public final class Upstream {
return this;
}
+ /**
+ * build healthCheckEnabled.
+ *
+ * @param healthCheckEnabled healthCheckEnabled
+ * @return this builder
+ */
+ public Builder healthCheckEnabled(final boolean healthCheckEnabled) {
+ this.healthCheckEnabled = healthCheckEnabled;
+ return this;
+ }
+
}
}
diff --git
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
index 051f3b773b..323535f230 100644
---
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
+++
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java
@@ -29,7 +29,6 @@ import org.junit.jupiter.api.Assertions;
import java.util.ArrayList;
import java.util.List;
-
/**
* The type UpstreamCacheManager check task test.
*/
@@ -79,4 +78,32 @@ public class UpstreamCacheManagerTest {
final UpstreamCacheManager upstreamCacheManager =
UpstreamCacheManager.getInstance();
Assertions.assertNull(upstreamCacheManager.findUpstreamListBySelectorId(SELECTOR_ID));
}
+
+ @Test
+ @Order(5)
+ public void testSubmitSyncsHealthCheckEnabled() {
+ final UpstreamCacheManager upstreamCacheManager =
UpstreamCacheManager.getInstance();
+ final String testSelectorId = "HEALTH_CHECK_SYNC_TEST";
+
+ // First submit with healthCheckEnabled = true (default)
+ List<Upstream> upstreamList = new ArrayList<>(1);
+ upstreamList.add(Upstream.builder()
+ .url("health-check-url:8080")
+ .status(true)
+ .healthCheckEnabled(true)
+ .build());
+ upstreamCacheManager.submit(testSelectorId, upstreamList);
+
+ // Now submit with the same URL but healthCheckEnabled = false
+ List<Upstream> updatedList = new ArrayList<>(1);
+ updatedList.add(Upstream.builder()
+ .url("health-check-url:8080")
+ .status(true)
+ .healthCheckEnabled(false)
+ .build());
+ upstreamCacheManager.submit(testSelectorId, updatedList);
+
+ // Clean up
+ upstreamCacheManager.removeByKey(testSelectorId);
+ }
}
diff --git
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTaskTest.java
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTaskTest.java
index 84d4b7ec18..4d4017143d 100644
---
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTaskTest.java
+++
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTaskTest.java
@@ -123,4 +123,45 @@ public class UpstreamCheckTaskTest {
healthCheckTask.triggerRemoveAll(selectorId);
assertFalse(healthCheckTask.getHealthyUpstream().containsKey(selectorId));
}
+
+ /**
+ * Test that upstream with healthCheckEnabled=false is always marked as
healthy.
+ */
+ @Test
+ @Timeout(10000)
+ public void testHealthCheckDisabled() {
+ final String selectorId = "healthCheckDisabledSelector";
+
+ // Create upstream with healthCheckEnabled = false, and set healthy to
false
+ // manually
+ Upstream upstream = Upstream.builder()
+ .url("unreachable-url:8080")
+ .healthCheckEnabled(false)
+ .build();
+ upstream.setHealthy(false);
+
+ healthCheckTask.triggerAddOne(selectorId, upstream);
+ healthCheckTask.setPoolSize(1);
+ healthCheckTask.schedule();
+
+ // Wait for health check to complete
+ Awaitility.await().pollDelay(3500, TimeUnit.MILLISECONDS)
+ .untilAsserted(() ->
assertFalse(healthCheckTask.getCheckStarted().get()));
+
+ // When healthCheckEnabled is false, upstream should be marked as
healthy
+
assertTrue(healthCheckTask.getHealthyUpstream().containsKey(selectorId));
+
assertTrue(healthCheckTask.getHealthyUpstream().get(selectorId).get(0).isHealthy());
+ }
+
+ /**
+ * Test that healthCheckEnabled defaults to true in Upstream.
+ */
+ @Test
+ public void testHealthCheckEnabledDefaultsToTrue() {
+ Upstream upstream = Upstream.builder()
+ .url("test-url:8080")
+ .build();
+
+ assertTrue(upstream.isHealthCheckEnabled());
+ }
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/src/main/java/org/apache/shenyu/plugin/divide/handler/DivideUpstreamDataHandler.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/src/main/java/org/apache/shenyu/plugin/divide/handler/DivideUpstreamDataHandler.java
index 1c3634b983..a6196b46e4 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/src/main/java/org/apache/shenyu/plugin/divide/handler/DivideUpstreamDataHandler.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-divide/src/main/java/org/apache/shenyu/plugin/divide/handler/DivideUpstreamDataHandler.java
@@ -75,6 +75,7 @@ public class DivideUpstreamDataHandler implements
DiscoveryUpstreamDataHandler {
.weight(u.getWeight())
.warmup(Integer.parseInt(properties.getProperty("warmup",
"10")))
.gray(Boolean.parseBoolean(properties.getProperty("gray",
"false")))
+
.healthCheckEnabled(Boolean.parseBoolean(properties.getProperty("healthCheckEnabled",
"true")))
.status(0 == u.getStatus())
.timestamp(Optional.ofNullable(u.getDateCreated()).map(Timestamp::getTime).orElse(System.currentTimeMillis()))
.build();
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-apache-dubbo/src/main/java/org/apache/shenyu/plugin/apache/dubbo/proxy/ApacheDubboProxyService.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-apache-dubbo/src/main/java/org/apache/shenyu/plugin/apache/dubbo/proxy/ApacheDubboProxyService.java
index cdbe6375c2..0634ab88f8 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-apache-dubbo/src/main/java/org/apache/shenyu/plugin/apache/dubbo/proxy/ApacheDubboProxyService.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-apache-dubbo/src/main/java/org/apache/shenyu/plugin/apache/dubbo/proxy/ApacheDubboProxyService.java
@@ -181,6 +181,7 @@ public class ApacheDubboProxyService {
.weight(u.getWeight())
.status(u.isStatus())
.timestamp(Optional.of(u.getTimestamp()).orElse(System.currentTimeMillis()))
+ .healthCheckEnabled(u.isHealthCheckEnabled())
.build();
}).collect(Collectors.toList());
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-dubbo-common/src/main/java/org/apache/shenyu/plugin/dubbo/common/handler/AbstractDubboPluginDataHandler.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-dubbo-common/src/main/java/org/apache/shenyu/plugin/dubbo/common/handler/AbstractDubboPluginDataHandler.java
index c247f64826..0eaae274bc 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-dubbo-common/src/main/java/org/apache/shenyu/plugin/dubbo/common/handler/AbstractDubboPluginDataHandler.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-dubbo/shenyu-plugin-dubbo-common/src/main/java/org/apache/shenyu/plugin/dubbo/common/handler/AbstractDubboPluginDataHandler.java
@@ -137,6 +137,7 @@ public abstract class AbstractDubboPluginDataHandler
implements PluginDataHandle
.warmup(u.getWarmup())
.group(u.getGroup())
.version(u.getVersion())
+ .healthCheckEnabled(u.isHealthCheckEnabled())
.build()).collect(Collectors.toList());
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcDiscoveryUpstreamDataHandler.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcDiscoveryUpstreamDataHandler.java
index 9f0b3fa6d4..d2e604e8a0 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcDiscoveryUpstreamDataHandler.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/handler/GrpcDiscoveryUpstreamDataHandler.java
@@ -21,6 +21,7 @@ import org.apache.shenyu.common.dto.DiscoverySyncData;
import org.apache.shenyu.common.dto.DiscoveryUpstreamData;
import org.apache.shenyu.common.dto.convert.selector.GrpcUpstream;
import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.GsonUtils;
import org.apache.shenyu.common.utils.JsonUtils;
import org.apache.shenyu.plugin.base.handler.DiscoveryUpstreamDataHandler;
import org.apache.shenyu.plugin.grpc.cache.ApplicationConfigCache;
@@ -34,6 +35,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Properties;
import java.util.stream.Collectors;
/**
@@ -64,13 +66,17 @@ public class GrpcDiscoveryUpstreamDataHandler implements
DiscoveryUpstreamDataHa
if (ObjectUtils.isEmpty(upstreamList)) {
return Collections.emptyList();
}
- return upstreamList.stream().map(u -> GrpcUpstream.builder()
- .protocol(u.getProtocol())
- .upstreamUrl(u.getUrl())
- .weight(u.getWeight())
- .status(0 == u.getStatus())
-
.timestamp(Optional.ofNullable(u.getDateCreated()).map(Timestamp::getTime).orElse(System.currentTimeMillis()))
- .build()).collect(Collectors.toList());
+ return upstreamList.stream().map(u -> {
+ Properties properties = Optional.ofNullable(u.getProps()).map(ps
-> GsonUtils.getInstance().fromJson(ps, Properties.class)).orElse(new
Properties());
+ return GrpcUpstream.builder()
+ .protocol(u.getProtocol())
+ .upstreamUrl(u.getUrl())
+ .weight(u.getWeight())
+ .status(0 == u.getStatus())
+
.timestamp(Optional.ofNullable(u.getDateCreated()).map(Timestamp::getTime).orElse(System.currentTimeMillis()))
+
.healthCheckEnabled(Boolean.parseBoolean(properties.getProperty("healthCheckEnabled",
"true")))
+ .build();
+ }).collect(Collectors.toList());
}
@Override
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/loadbalance/picker/ShenyuPicker.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/loadbalance/picker/ShenyuPicker.java
index 616ab9f3e6..b8450f109f 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/loadbalance/picker/ShenyuPicker.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-rpc/shenyu-plugin-grpc/src/main/java/org/apache/shenyu/plugin/grpc/loadbalance/picker/ShenyuPicker.java
@@ -71,6 +71,7 @@ public class ShenyuPicker extends AbstractReadyPicker {
.weight(u.getWeight())
.status(u.isStatus())
.timestamp(u.getTimestamp())
+ .healthCheckEnabled(u.isHealthCheckEnabled())
.build()).collect(Collectors.toList());
}
}
diff --git
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-websocket/src/main/java/org/apache/shenyu/plugin/websocket/handler/WebSocketUpstreamDataHandler.java
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-websocket/src/main/java/org/apache/shenyu/plugin/websocket/handler/WebSocketUpstreamDataHandler.java
index c5a07c0ce6..923cdf67c5 100644
---
a/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-websocket/src/main/java/org/apache/shenyu/plugin/websocket/handler/WebSocketUpstreamDataHandler.java
+++
b/shenyu-plugin/shenyu-plugin-proxy/shenyu-plugin-websocket/src/main/java/org/apache/shenyu/plugin/websocket/handler/WebSocketUpstreamDataHandler.java
@@ -67,6 +67,7 @@ public class WebSocketUpstreamDataHandler implements
DiscoveryUpstreamDataHandle
.url(u.getUrl())
.weight(u.getWeight())
.warmup(Integer.parseInt(properties.getProperty("warmup",
"10")))
+
.healthCheckEnabled(Boolean.parseBoolean(properties.getProperty("healthCheckEnabled",
"true")))
.status(0 == u.getStatus())
.timestamp(Optional.ofNullable(u.getDateCreated()).map(Timestamp::getTime).orElse(System.currentTimeMillis()))
.build();
diff --git
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
index 3e21d0c47f..c889ccb56b 100644
---
a/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
+++
b/shenyu-web/src/main/java/org/apache/shenyu/web/controller/LocalPluginController.java
@@ -247,6 +247,7 @@ public class LocalPluginController {
Properties properties = new Properties();
properties.setProperty("warmup",
String.valueOf(up.getWarmup()));
properties.setProperty("upstreamHost",
String.valueOf(up.getUpstreamHost()));
+ properties.setProperty("healthCheckEnabled",
String.valueOf(up.isHealthCheckEnabled()));
upstreamData.setDateUpdated(Optional.of(up.getTimestamp()).map(Timestamp::new).orElse(new
Timestamp(System.currentTimeMillis())));
upstreamData.setProps(GsonUtils.getInstance().toJson(properties));
upstreamData.setDateCreated(Optional.of(up.getTimestamp()).map(Timestamp::new).orElse(new
Timestamp(System.currentTimeMillis())));