diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/pom.xml
new file mode 100644
index 000000000..22dcb59ba
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-examples
+ ${revision}
+ ../../../../pom.xml
+
+
+ outlierdetection-service-consumer
+ jar
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-governance-routing
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/OutlierDetectionConsumerApplication.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/OutlierDetectionConsumerApplication.java
new file mode 100644
index 000000000..e637ca097
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/OutlierDetectionConsumerApplication.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+/**
+ * @author xqw
+ */
+
+@EnableFeignClients
+@EnableDiscoveryClient
+@SpringBootApplication
+public class OutlierDetectionConsumerApplication {
+
+ public static void main(String[] args) {
+
+ SpringApplication.run(OutlierDetectionConsumerApplication.class);
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/api/FeignService.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/api/FeignService.java
new file mode 100644
index 000000000..ba2754dd0
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/api/FeignService.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example.api;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * @author xqw
+ */
+
+@FeignClient(name = "outlierdetection-service-provider-example")
+public interface FeignService {
+
+ @GetMapping("/test")
+ String test();
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/controller/OutlierDetectionController.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/controller/OutlierDetectionController.java
new file mode 100644
index 000000000..26998973a
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/java/com/alibaba/cloud/example/controller/OutlierDetectionController.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example.controller;
+
+import javax.annotation.Resource;
+
+import com.alibaba.cloud.example.api.FeignService;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xqw
+ */
+
+@RestController
+public class OutlierDetectionController {
+
+ @Resource
+ private FeignService feignService;
+
+ @GetMapping("/test")
+ public String test() {
+
+ return feignService.test();
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/resources/application.yml
new file mode 100644
index 000000000..17705ca90
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer/src/main/resources/application.yml
@@ -0,0 +1,22 @@
+server:
+ port: 17070
+
+spring:
+ application:
+ name: outlierdetection-service-consumer-example
+
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 127.0.0.1:8848
+
+ governance:
+ routing:
+ # enable outlier detected
+ enableOutlierDetected: true
+ # 最小健康比,摘除上限
+ minHealthPercent: 0.5
+ # 恢复时间间隔
+ recoverInterval: 3000
+ # 恢复时间累加值
+ baseEjectionTime: 3000
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/pom.xml
new file mode 100644
index 000000000..4a9807e99
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-examples
+ ${revision}
+ ../../../../pom.xml
+
+
+ outlierdetection-service-provider-1
+ Spring Cloud Governance OutlierDetection Service Provider Example
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider1Application.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider1Application.java
new file mode 100644
index 000000000..56d3b349b
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider1Application.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example;
+
+import com.alibaba.cloud.nacos.registry.NacosRegistration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xqw
+ */
+
+@RestController
+@EnableDiscoveryClient
+@SpringBootApplication
+public class OutlierDetectionProvider1Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OutlierDetectionProvider1Application.class, args);
+ }
+
+ @Autowired
+ NacosRegistration nacosRegistration;
+
+ @GetMapping("/test")
+ public String test() {
+
+ throw new RuntimeException("mock service-provider-1 exception.");
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/resources/application.yml
new file mode 100644
index 000000000..b205ded4e
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1/src/main/resources/application.yml
@@ -0,0 +1,11 @@
+server:
+ port: 17071
+
+spring:
+ application:
+ name: outlierdetection-service-provider-example
+
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 127.0.0.1:8848
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/pom.xml
new file mode 100644
index 000000000..8d841cfc9
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-examples
+ ${revision}
+ ../../../../pom.xml
+
+
+ outlierdetection-service-provider-2
+ Spring Cloud Governance OutlierDetection Service Provider Example
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider2Application.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider2Application.java
new file mode 100644
index 000000000..b1a884f09
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider2Application.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example;
+
+import com.alibaba.cloud.nacos.registry.NacosRegistration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xqw
+ */
+
+@RestController
+@EnableDiscoveryClient
+@SpringBootApplication
+public class OutlierDetectionProvider2Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OutlierDetectionProvider2Application.class, args);
+ }
+
+ @Autowired
+ NacosRegistration nacosRegistration;
+
+ @GetMapping("/test")
+ public String test() {
+
+ String host = nacosRegistration.getHost();
+ int port = nacosRegistration.getPort();
+ return "Route in " + host + ": " + port + ".";
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/resources/application.yml
new file mode 100644
index 000000000..12f4b4f36
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2/src/main/resources/application.yml
@@ -0,0 +1,11 @@
+server:
+ port: 17072
+
+spring:
+ application:
+ name: outlierdetection-service-provider-example
+
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 127.0.0.1:8848
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/pom.xml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/pom.xml
new file mode 100644
index 000000000..3a4559305
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-examples
+ ${revision}
+ ../../../../pom.xml
+
+
+ outlierdetection-service-provider-3
+ Spring Cloud Governance OutlierDetection Service Provider Example
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider3Application.java b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider3Application.java
new file mode 100644
index 000000000..a3cd0e10f
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/java/com/alibaba/cloud/example/OutlierDetectionProvider3Application.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.example;
+
+import com.alibaba.cloud.nacos.registry.NacosRegistration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author xqw
+ */
+
+@RestController
+@EnableDiscoveryClient
+@SpringBootApplication
+public class OutlierDetectionProvider3Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(OutlierDetectionProvider3Application.class, args);
+ }
+
+ @Autowired
+ NacosRegistration nacosRegistration;
+
+ @GetMapping("/test")
+ public String test() {
+
+ String host = nacosRegistration.getHost();
+ int port = nacosRegistration.getPort();
+ return "Route in " + host + ": " + port + ". ";
+ }
+
+}
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/resources/application.yml b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/resources/application.yml
new file mode 100644
index 000000000..fc722b809
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3/src/main/resources/application.yml
@@ -0,0 +1,11 @@
+server:
+ port: 17073
+
+spring:
+ application:
+ name: outlierdetection-service-provider-example
+
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 127.0.0.1:8848
diff --git a/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/request.sh b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/request.sh
new file mode 100644
index 000000000..56822c148
--- /dev/null
+++ b/spring-cloud-alibaba-examples/governance-example/label-routing-example/outlierdetection-example/request.sh
@@ -0,0 +1,5 @@
+curl 127.0.0.1:17070/test
+
+# curl 127.0.0.1:17071/test
+
+# curl 127.0.0.1:17072/test
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml
index a7b5aecb6..4d5f06bb4 100644
--- a/spring-cloud-alibaba-examples/pom.xml
+++ b/spring-cloud-alibaba-examples/pom.xml
@@ -63,6 +63,10 @@
governance-example/label-routing-example/consumer-example
governance-example/label-routing-example/default-provider-version-example
governance-example/label-routing-example/provider-version-example
+ governance-example/label-routing-example/outlierdetection-example/outlierdetection-servcie-consumer
+ governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-1
+ governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-2
+ governance-example/label-routing-example/outlierdetection-example/outlierdetection-service-provider-3
governance-example/authentication-example/istio-authentication-provider-mvc-example
governance-example/authentication-example/istio-authentication-provider-webflux-example
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml
index 0eadf696b..308a4b67f 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/pom.xml
@@ -55,6 +55,18 @@
true
+
+ com.alibaba.middleware
+ metrics-core-api
+ 2.0.6
+
+
+
+ com.alibaba.middleware
+ metrics-core-impl
+ 2.0.6
+
+
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/RoutingProperties.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/RoutingProperties.java
index 4de143f73..cda71cc52 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/RoutingProperties.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/RoutingProperties.java
@@ -40,6 +40,14 @@ public class RoutingProperties {
*/
private String rule;
+ private boolean enableOutlierDetected;
+
+ private long baseEjectionTime;
+
+ private double minHealthPercent;
+
+ private int recoverInterval;
+
@PostConstruct
public void init() {
if (StringUtils.isEmpty(rule)) {
@@ -47,6 +55,38 @@ public class RoutingProperties {
}
}
+ public boolean isEnableOutlierDetected() {
+ return enableOutlierDetected;
+ }
+
+ public void setEnableOutlierDetected(boolean enableOutlierDetected) {
+ this.enableOutlierDetected = enableOutlierDetected;
+ }
+
+ public long getBaseEjectionTime() {
+ return baseEjectionTime;
+ }
+
+ public void setBaseEjectionTime(long baseEjectionTime) {
+ this.baseEjectionTime = baseEjectionTime;
+ }
+
+ public double getMinHealthPercent() {
+ return minHealthPercent;
+ }
+
+ public void setMinHealthPercent(double minHealthPercent) {
+ this.minHealthPercent = minHealthPercent;
+ }
+
+ public int getRecoverInterval() {
+ return recoverInterval;
+ }
+
+ public void setRecoverInterval(int recoverInterval) {
+ this.recoverInterval = recoverInterval;
+ }
+
public String getRule() {
return rule;
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/cache/GlobalInstanceStatusListCache.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/cache/GlobalInstanceStatusListCache.java
new file mode 100644
index 000000000..2f2060659
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/cache/GlobalInstanceStatusListCache.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.alibaba.cloud.routing.model.ServiceInstanceInfo;
+import com.alibaba.nacos.api.naming.pojo.Instance;
+
+/**
+ * @author xqw
+ * @author 550588941@qq.com
+ */
+
+public final class GlobalInstanceStatusListCache {
+
+ private GlobalInstanceStatusListCache() {
+ }
+
+ /**
+ * Global instance cache.
+ * String: service name.
+ * String: ip + port.
+ * ServiceInstanceInfo: 服务实例信息.
+ */
+ private static final Map> globalServiceCache = new ConcurrentHashMap<>();
+
+ /**
+ * Set instance cache.
+ *
+ * @param targetName service name.
+ * @param instance service instance object {ip + port}.
+ * @param sif service instance info.
+ */
+ public static void set(String targetName, Instance instance,
+ ServiceInstanceInfo sif) {
+ if (globalServiceCache.isEmpty()) {
+ Map instanceInfoMap = new ConcurrentHashMap<>();
+ instanceInfoMap.put(instance.getIp() + ":" + instance.getPort(), sif);
+ globalServiceCache.put(targetName, instanceInfoMap);
+ }
+ else {
+ globalServiceCache.forEach((serviceName, instanceMap) -> {
+ Map instanceInfoMap;
+ if (serviceName == targetName) {
+ // serviceName is exist.
+ instanceInfoMap = globalServiceCache.get(targetName);
+ instanceInfoMap.put(instance.getIp() + ":" + instance.getIp(), sif);
+ }
+ else {
+ instanceInfoMap = new ConcurrentHashMap<>();
+ instanceInfoMap.put(instance.getIp() + ":" + instance.getPort(), sif);
+ }
+ globalServiceCache.put(targetName, instanceInfoMap);
+ });
+ }
+ }
+
+ /**
+ * Check instance in cache.
+ *
+ * @param target target.
+ * @param instance instance object.
+ * @return boolean
+ */
+ public static boolean checkContainersInstance(String target, Instance instance) {
+
+ AtomicBoolean flag = new AtomicBoolean(false);
+
+ Map instanceInfoMap = globalServiceCache.get(target);
+ if (instanceInfoMap.isEmpty()) {
+ return flag.get();
+ }
+ else {
+ instanceInfoMap.forEach((instanceName, sif) -> {
+ flag.set(instanceName == instance.getPort() + ":" + instance.getIp());
+ });
+ }
+
+ return flag.get();
+ }
+
+ /**
+ * Return GlobalCache Object.
+ * @return return global instance cache
+ */
+ public static Map> getAll() {
+
+ return globalServiceCache;
+ }
+
+ /**
+ * Get service instance list by service name.
+ * @param targetServiceName service name.
+ * @return instance list.
+ */
+ public static Map getInstanceByServiceName(
+ String targetServiceName) {
+
+ Map instanceInfoMap = globalServiceCache.get(targetServiceName);
+ if (instanceInfoMap.isEmpty()) {
+ return null;
+ }
+
+ return instanceInfoMap;
+ }
+
+ /**
+ * Get instance info by global cache.
+ * @param instanceName instance name
+ * @return sif object
+ */
+ public static ServiceInstanceInfo getInstanceByInstanceName(String instanceName) {
+
+ AtomicReference sif = null;
+
+ globalServiceCache.keySet().forEach((serviceName) -> {
+ Map instanceInfoMap = globalServiceCache.get(serviceName);
+ sif.set(instanceInfoMap.get(instanceName));
+ });
+
+ return sif.get();
+ }
+
+ /**
+ * Get no health instance list.
+ *
+ * @return list
+ */
+ public static List getCalledErrorInstance() {
+
+ List res = new ArrayList<>();
+
+ globalServiceCache.forEach((serviceName, instanceMap) -> {
+ Map instanceInfoMap = globalServiceCache.get(serviceName);
+ instanceInfoMap.forEach((instanceName, sif) -> {
+ ServiceInstanceInfo serviceInstanceInfo = instanceInfoMap.get(instanceName);
+ if (serviceInstanceInfo.getConsecutiveErrors() != null) {
+ res.add(serviceInstanceInfo);
+ }
+ });
+ });
+
+ return res;
+ }
+
+ /**
+ * getServiceUpperLimitRatioNum.
+ *
+ * @param targetServiceName target service name.
+ * @param minHealthPercent minHealthPercent
+ * @return max remove instance num
+ */
+ public static int getServiceUpperLimitRatioNum(String targetServiceName,
+ double minHealthPercent) {
+
+ int serviceInstanceTotal;
+ Map instanceInfoMap = globalServiceCache.get(targetServiceName);
+ serviceInstanceTotal = instanceInfoMap.size();
+
+ return (int) Math.floor(serviceInstanceTotal * minHealthPercent);
+ }
+
+ /**
+ * Get all instance nums.
+ * @param targetServiceName target service name.
+ * @return remove instance num
+ */
+ public static int getInstanceNumByTargetServiceName(String targetServiceName) {
+
+ int serviceInstanceTotal;
+ Map instanceInfoMap = globalServiceCache.get(targetServiceName);
+ serviceInstanceTotal = instanceInfoMap.size();
+
+ return serviceInstanceTotal;
+ }
+
+ /**
+ * Get no health nums.
+ * @param targetServiceName target service name.
+ * @return remove instance num
+ */
+ public static int getRemoveInstanceNum(String targetServiceName) {
+
+ AtomicInteger serviceInstanceTotal = new AtomicInteger();
+
+ Map instanceInfoMap = globalServiceCache.get(targetServiceName);
+ instanceInfoMap.forEach((instanceName, sif) -> {
+ ServiceInstanceInfo serviceInstanceInfo = instanceInfoMap.get(instanceName);
+ if (!(serviceInstanceInfo.isStatus())) {
+ serviceInstanceTotal.getAndIncrement();
+ }
+ });
+
+ return serviceInstanceTotal.get();
+ }
+
+ public static void setInstanceInfoByInstanceNames(ServiceInstanceInfo sif) {
+
+ String instanceName = sif.getInstance().getIp() + ":"
+ + sif.getInstance().getPort();
+
+ globalServiceCache.forEach((serviceName, instanceMap) -> {
+ Map instanceInfoMap = globalServiceCache.get(serviceName);
+ instanceInfoMap.put(instanceName, sif);
+ });
+
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ for (String serviceName : globalServiceCache.keySet()) {
+ stringBuilder.append("Service: ").append(serviceName).append("\n");
+ Map innerMap = globalServiceCache.get(serviceName);
+
+ for (String instanceId : innerMap.keySet()) {
+ ServiceInstanceInfo instanceInfo = innerMap.get(instanceId);
+ stringBuilder.append(" Instance: ").append(instanceInfo).append("\n");
+ }
+
+ stringBuilder.append("\n");
+ }
+ return stringBuilder.toString();
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/configuration/OutlierDetectionConfiguration.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/configuration/OutlierDetectionConfiguration.java
new file mode 100644
index 000000000..bfff19a7c
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/configuration/OutlierDetectionConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.configuration;
+
+import com.alibaba.cloud.routing.decorator.OutlierDetectionFeignClientDecorator;
+import com.alibaba.cloud.routing.recover.OutlierDetectionRecover;
+import feign.Client;
+
+import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
+import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
+import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author xqw
+ * @author 550588941@qq.com
+ */
+
+@Configuration(proxyBeanMethods = false)
+public class OutlierDetectionConfiguration {
+
+ @Bean
+ public Client outlierDetectionFeignClientDecorator(
+ CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory,
+ SpringClientFactory clientFactory) {
+
+ return new LoadBalancerFeignClient(
+ new OutlierDetectionFeignClientDecorator(new Client.Default(null, null)),
+ cachingSpringLoadBalancerFactory, clientFactory);
+ }
+
+ @Bean
+ public OutlierDetectionRecover outlierDetectionRecover() {
+
+ return new OutlierDetectionRecover();
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/constants/OutlierDetectionConstants.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/constants/OutlierDetectionConstants.java
new file mode 100644
index 000000000..f1b7764cd
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/constants/OutlierDetectionConstants.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.constants;
+
+/**
+ * @author xqw
+ */
+public final class OutlierDetectionConstants {
+
+ private OutlierDetectionConstants() {
+ }
+
+ /**
+ * Instance error called max.
+ */
+ public static final int instanceErrorCalledMax = 5;
+
+ /**
+ * Response Code.
+ */
+ public enum ResponseCode {
+
+ /**
+ * Access successful.
+ */
+ _200(200, "Access successful"),
+ /**
+ * The data types do not match.
+ */
+ _400(400, "The data types do not match"),
+ /**
+ * The server rejected the request.
+ */
+ _403(403, "The server rejected the request"),
+ /**
+ * The server could not find the requested web page and entered the link incorrectly.
+ */
+ _404(404, "The server could not find the requested web page and entered the link incorrectly"),
+ /**
+ * The server encountered an error and could not complete the request.
+ */
+ _500(500, "The server encountered an error and could not complete the request"),
+ /**
+ * The server, acting as a gateway or proxy, receives an invalid response from the upstream server.
+ */
+ _502(502, "The server, acting as a gateway or proxy, receives an invalid response from the upstream server");
+
+ /**
+ * Code.
+ */
+ private Integer code;
+
+ /**
+ * info.
+ */
+ private String info;
+
+ ResponseCode(Integer code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+ public Integer getCode() {
+ return code;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignClientDecorator.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignClientDecorator.java
new file mode 100644
index 000000000..7bf0a0c73
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignClientDecorator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.decorator;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.cloud.routing.cache.GlobalInstanceStatusListCache;
+import com.alibaba.cloud.routing.constants.OutlierDetectionConstants;
+import com.alibaba.cloud.routing.model.ServiceInstanceInfo;
+import feign.Client;
+import feign.Request;
+import feign.Response;
+
+/**
+ * @author xqw
+ * @author 550588941@qq.com
+ */
+
+public class OutlierDetectionFeignClientDecorator implements Client {
+
+ private final Client delegate;
+
+ public OutlierDetectionFeignClientDecorator(Client delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public Response execute(Request request, Request.Options options) throws IOException {
+
+ Response response = delegate.execute(request, options);
+
+ parseResponse(request, response);
+
+ return response;
+ }
+
+ private void parseResponse(Request request, Response response)
+ throws MalformedURLException {
+
+ URL url = new URL(request.url());
+ String instanceName = url.getHost() + ":" + url.getPort();
+ ServiceInstanceInfo sif = GlobalInstanceStatusListCache
+ .getInstanceByInstanceName(instanceName);
+
+ // The use of metrics is still under investigation.
+ // FastCompass counter = MetricManager.getFastCompass("sca-instance"
+ // , MetricName.build(instanceName + ".counter"));
+ // long start = System.currentTimeMillis();
+
+ if (response.status() == OutlierDetectionConstants.ResponseCode._500.getCode()) {
+ // long duration = System.currentTimeMillis() - start;
+ // counter.record(duration,"error");
+ // sif.setCompass(counter);
+ AtomicInteger consecutiveErrors = sif.getConsecutiveErrors();
+ if (Objects.isNull(consecutiveErrors)) {
+ consecutiveErrors = new AtomicInteger(1);
+ } else {
+ int andIncrement = sif.getConsecutiveErrors().get();
+ andIncrement ++;
+ consecutiveErrors = new AtomicInteger(andIncrement);
+ }
+ sif.setConsecutiveErrors(consecutiveErrors);
+ sif.setRemoveTime(System.currentTimeMillis());
+ System.err.println("设置服务错误次数之后的全局缓存数据:" + GlobalInstanceStatusListCache.getAll());
+ }
+ else {
+ // long duration = System.currentTimeMillis() - start;
+ // counter.record(duration, "success");
+ // sif.setCompass(counter);
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignResponseDecoder.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignResponseDecoder.java
new file mode 100644
index 000000000..f81c759a4
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/decorator/OutlierDetectionFeignResponseDecoder.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.decorator;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import feign.FeignException;
+import feign.Response;
+import feign.Util;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author xqw
+ * @author 550588941@qq.com
+ */
+
+@Component
+public class OutlierDetectionFeignResponseDecoder extends SpringDecoder {
+
+ public OutlierDetectionFeignResponseDecoder(
+ ObjectFactory messageConverters) {
+
+ super(messageConverters);
+ }
+
+ @Override
+ public Object decode(Response response, Type type)
+ throws IOException, FeignException {
+
+ Reader reader = response.body().asReader(StandardCharsets.UTF_8);
+ if (Objects.isNull(Util.toString(reader))) {
+
+ return super.decode(
+ response.toBuilder()
+ .body(Util.toString(reader), StandardCharsets.UTF_8).build(),
+ type);
+ }
+
+ return Util.toString(reader);
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/model/ServiceInstanceInfo.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/model/ServiceInstanceInfo.java
new file mode 100644
index 000000000..f6984013c
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/model/ServiceInstanceInfo.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.model;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.metrics.FastCompass;
+import com.alibaba.nacos.api.naming.pojo.Instance;
+
+/**
+ * @author xqw
+ * @author 550588941@qq.com
+ */
+
+public class ServiceInstanceInfo {
+
+ /**
+ * Current instance info.
+ */
+ private Instance instance;
+
+ /**
+ * Metrics data.
+ */
+ private FastCompass compass;
+
+ /**
+ * Instance remove time, It increases according to the number of removals.
+ */
+ private Long removeTime;
+
+ /**
+ * The percentage of services removed over a period of time. get data from metrics.
+ */
+ private double removalRatio;
+
+ /**
+ * Instance status.
+ */
+ private boolean status;
+
+ private AtomicInteger consecutiveErrors;
+
+ public AtomicInteger getConsecutiveErrors() {
+ return consecutiveErrors;
+ }
+
+ public void setConsecutiveErrors(AtomicInteger consecutiveErrors) {
+ this.consecutiveErrors = consecutiveErrors;
+ }
+
+ public boolean isStatus() {
+ return status;
+ }
+
+ public void setStatus(boolean status) {
+ this.status = status;
+ }
+
+ public Long getRemoveTime() {
+ return removeTime;
+ }
+
+ public void setRemoveTime(Long removeTime) {
+ this.removeTime = removeTime;
+ }
+
+ public double getRemovalRatio() {
+ return removalRatio;
+ }
+
+ public void setRemovalRatio(double removalRatio) {
+ this.removalRatio = removalRatio;
+ }
+
+ public Instance getInstance() {
+ return instance;
+ }
+
+ public void setInstance(Instance instance) {
+ this.instance = instance;
+ }
+
+ public FastCompass getCompass() {
+ return compass;
+ }
+
+ public void setCompass(FastCompass compass) {
+ this.compass = compass;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("ServiceInstanceInfo{");
+ sb.append("instance=").append(instance);
+ sb.append(", compass=").append(compass);
+ sb.append(", removeTime=").append(removeTime);
+ sb.append(", removalRatio=").append(removalRatio);
+ sb.append(", status=").append(status);
+ sb.append(", consecutiveErrors=").append(consecutiveErrors);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/recover/OutlierDetectionRecover.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/recover/OutlierDetectionRecover.java
new file mode 100644
index 000000000..ce81e8654
--- /dev/null
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/recover/OutlierDetectionRecover.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2022-2023 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+
+package com.alibaba.cloud.routing.recover;
+
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import com.alibaba.cloud.routing.RoutingProperties;
+import com.alibaba.cloud.routing.cache.GlobalInstanceStatusListCache;
+import com.alibaba.cloud.routing.model.ServiceInstanceInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author xqw
+ */
+
+public class OutlierDetectionRecover {
+
+ @Autowired
+ private RoutingProperties routingProperties;
+
+ private static final Logger log = LoggerFactory
+ .getLogger(OutlierDetectionRecover.class);
+
+ // move to spring config
+ private static final boolean enabledInstanceRecoverTask = true;
+
+ public void updateInstanceStatus(String targetServiceName) {
+
+ if (enabledInstanceRecoverTask) {
+ log.info(
+ "The instance recover task is started. Please pay attention to the service status.");
+ Timer timer = new Timer();
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ recover(targetServiceName);
+ }
+ }, 0, routingProperties.getRecoverInterval());
+ }
+ }
+
+ /**
+ * Recover instance and error count,setting consecutiveErrors And When.
+ * consecutiveErrors to 5, down this forever.
+ */
+ private void recover(String targetServiceName) {
+
+ List calledErrorInstance = GlobalInstanceStatusListCache
+ .getCalledErrorInstance();
+ double minHealthPercent = routingProperties.getMinHealthPercent();
+ int removeUpperLimitNum = GlobalInstanceStatusListCache
+ .getServiceUpperLimitRatioNum(targetServiceName, minHealthPercent);
+ int unHealthInstanceNum = GlobalInstanceStatusListCache.getRemoveInstanceNum(targetServiceName);
+ long baseEjectionTime = routingProperties.getBaseEjectionTime();
+
+ log.info(
+ "最大移除上限数:" + removeUpperLimitNum +
+ ",不健康实例数:" + unHealthInstanceNum +
+ ",缓存中 " + targetServiceName + " 的服务实例数:"
+ + GlobalInstanceStatusListCache.getInstanceNumByTargetServiceName(targetServiceName) );
+
+ for (ServiceInstanceInfo sif : calledErrorInstance) {
+
+ // 判断错误率是否合格? use metrics!
+ if (sif.getConsecutiveErrors().get() == 2) {
+ log.error("错误次数达到上限,进入摘除逻辑...");
+
+ // 判断是否达到上限比
+ if (!(removeUpperLimitNum == unHealthInstanceNum)) {
+ log.info("通过摘除上限比判断,准备摘除...");
+ // 摘除
+ sif.setStatus(false);
+ sif.setRemoveTime(System.currentTimeMillis());
+ log.info("成功摘除:" + GlobalInstanceStatusListCache.getAll());
+ }
+
+ GlobalInstanceStatusListCache.setInstanceInfoByInstanceNames(sif);
+ }
+ else {
+
+ log.info("错误率条件不成立,进入实例恢复....");
+
+ System.out.println(sif);
+
+ // 不健康 当前的时间 - 上次摘除的时间 == 恢复时间
+ long current = System.currentTimeMillis();
+ long removeTime = sif.getRemoveTime();
+
+ if ((current - removeTime) > baseEjectionTime) {
+ // 恢复实例 设置健康状态 status ——> true
+ sif.setStatus(true);
+ }
+
+ GlobalInstanceStatusListCache.setInstanceInfoByInstanceNames(sif);
+ }
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/ribbon/RoutingLoadBalanceRule.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/ribbon/RoutingLoadBalanceRule.java
index f1ec4a61a..fd051061c 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/ribbon/RoutingLoadBalanceRule.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/java/com/alibaba/cloud/routing/ribbon/RoutingLoadBalanceRule.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import javax.servlet.http.HttpServletRequest;
@@ -33,10 +34,14 @@ import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.cloud.routing.RoutingProperties;
+import com.alibaba.cloud.routing.cache.GlobalInstanceStatusListCache;
+import com.alibaba.cloud.routing.model.ServiceInstanceInfo;
import com.alibaba.cloud.routing.publish.TargetServiceChangedPublisher;
+import com.alibaba.cloud.routing.recover.OutlierDetectionRecover;
import com.alibaba.cloud.routing.repository.RoutingDataRepository;
import com.alibaba.cloud.routing.util.ConditionMatchUtil;
import com.alibaba.cloud.routing.util.LoadBalanceUtil;
+import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.loadbalancer.AbstractServerPredicate;
@@ -104,6 +109,9 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
@Autowired
private RoutingDataRepository routingDataRepository;
+ @Autowired
+ private OutlierDetectionRecover recover;
+
@Autowired
private RoutingProperties routingProperties;
@@ -121,19 +129,24 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
final HashMap> routeData = routingDataRepository
.getRouteRule(targetServiceName);
if (routeData == null) {
+
+ // if outlier detection function is enabled.
+ if (routingProperties.isEnableOutlierDetected()) {
+
+ return chooseServerByOutlier(targetServiceName);
+ }
+
return LoadBalanceUtil.loadBalanceByOrdinaryRule(loadBalancer, key,
routingProperties.getRule());
}
- // Get instances from register-center.
- String group = this.nacosDiscoveryProperties.getGroup();
- final NamingService namingService = nacosServiceManager.getNamingService();
- final List instances = namingService
- .selectInstances(targetServiceName, group, true);
- if (CollectionUtils.isEmpty(instances)) {
- LOG.warn("no instance in service {} ", targetServiceName);
- return null;
- }
+ List instances = getInsanceList(targetServiceName);
+
+ // set instance cache
+ setGlobalInstanceCache(targetServiceName, instances);
+
+ // instance recover
+ recover.updateInstanceStatus(targetServiceName);
// Filter by route rules,the result will be kept in versionSet and weightMap.
HashSet versionSet = new HashSet<>();
@@ -173,11 +186,13 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
instanceMap.put(version, instanceList);
}
}
- return chooseServerByWeight(instanceMap, fallbackWeightMap, weightArray);
+ return chooseServerByWeight(targetServiceName, instanceMap,
+ fallbackWeightMap, weightArray);
}
// Routing with Weight algorithm.
- return chooseServerByWeight(instanceMap, weightMap, weightArray);
+ return chooseServerByWeight(targetServiceName, instanceMap, weightMap,
+ weightArray);
}
catch (Exception e) {
@@ -186,6 +201,84 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
}
}
+ private Server chooseServerByOutlier(String targetServiceName) {
+
+ try {
+
+ List insanceList = getInsanceList(targetServiceName);
+
+ // set instance cache
+ setGlobalInstanceCache(targetServiceName, insanceList);
+
+ // instance recover
+ recover.updateInstanceStatus(targetServiceName);
+
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // return instance called for health instance list
+ return new NacosServer(Objects.requireNonNull(getInstance(targetServiceName)));
+
+ }
+
+ /**
+ * Get instance from global instance cache.
+ *
+ * @param targetServiceName service name.
+ */
+ private Instance getInstance(String targetServiceName) {
+
+ Map instanceByServiceName = GlobalInstanceStatusListCache
+ .getInstanceByServiceName(targetServiceName);
+ List instanceInfos = new ArrayList<>();
+
+ instanceByServiceName.forEach((k1, v1) -> {
+ if (v1.isStatus()) {
+ instanceInfos.add(v1);
+ }
+ });
+
+ if (instanceInfos.isEmpty()) {
+ LOG.warn(
+ "The number of available instances in the global service instance cache list is 0, "
+ + "no instance called. please check instance status!");
+ System.out.println(GlobalInstanceStatusListCache.getAll());
+ return null;
+ }
+
+ int randomInt = ThreadLocalRandom.current().nextInt(instanceInfos.size());
+ return instanceInfos.get(randomInt).getInstance();
+ }
+
+ /**
+ * Get instance from global instance cache.
+ * @param targetServiceName service name.
+ */
+ private List getInstanceList(String targetServiceName) {
+
+ Map instanceByServiceName = GlobalInstanceStatusListCache
+ .getInstanceByServiceName(targetServiceName);
+ List instances = new ArrayList<>();
+
+ instanceByServiceName.forEach((k1, v1) -> {
+ if (v1.isStatus()) {
+ instances.add(v1.getInstance());
+ }
+ });
+
+ if (instances.isEmpty()) {
+ LOG.warn(
+ "The number of available instances in the global service instance cache list is 0, "
+ + "no instance called. please check instance status!");
+ System.out.println(GlobalInstanceStatusListCache.getAll());
+ return null;
+ }
+
+ return instances;
+ }
+
@Override
public AbstractServerPredicate getPredicate() {
return this.predicate;
@@ -365,8 +458,12 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
}
}
- private Server chooseServerByWeight(final HashMap> instanceMap,
+ private Server chooseServerByWeight(String targetServiceName,
+ final HashMap> instanceMap,
final HashMap weightMap, final double[] weightArray) {
+
+ getInstanceList(targetServiceName);
+
int index = 0;
double sum = 0.0D;
List instances = new ArrayList<>();
@@ -396,4 +493,36 @@ public class RoutingLoadBalanceRule extends PredicateBasedRule {
return new NacosServer(instances.get(chooseServiceIndex));
}
+ /**
+ * Set instance cache.
+ */
+ private void setGlobalInstanceCache(String target, List instances) {
+ for (Instance instance : instances) {
+ if (!GlobalInstanceStatusListCache.checkContainersInstance(target,
+ instance)) {
+ ServiceInstanceInfo serviceInstanceInfo = new ServiceInstanceInfo();
+ serviceInstanceInfo.setInstance(instance);
+ serviceInstanceInfo.setStatus(true);
+ serviceInstanceInfo.setRemovalRatio(0.0);
+ GlobalInstanceStatusListCache.set(target, instance, serviceInstanceInfo);
+ }
+ }
+ }
+
+ private List getInsanceList(String targetServiceName)
+ throws NacosException {
+
+ // Get instances from register-center.
+ String group = this.nacosDiscoveryProperties.getGroup();
+ NamingService namingService = nacosServiceManager.getNamingService();
+ List instances = namingService.selectInstances(targetServiceName, group,
+ true);
+ if (CollectionUtils.isEmpty(instances)) {
+ LOG.warn("no instance in service {} ", targetServiceName);
+ return null;
+ }
+
+ return instances;
+ }
+
}
diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories
index 9c0a97812..a7d8f353b 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-governance-routing/src/main/resources/META-INF/spring.factories
@@ -2,4 +2,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.cloud.routing.RoutingAutoConfiguration,\
com.alibaba.cloud.routing.feign.RoutingFeignInterceptorAutoConfiguration,\
com.alibaba.cloud.routing.ribbon.RoutingLoadBalanceRuleAutoConfiguration,\
- com.alibaba.cloud.routing.RoutingPropertiesAutoConfiguration
+ com.alibaba.cloud.routing.RoutingPropertiesAutoConfiguration,\
+ com.alibaba.cloud.routing.configuration.OutlierDetectionConfiguration