[WIP] [OSPP] [2.2.x] feat: Add outlier decection function (#3464)
* add outlier decection functionpull/3449/head^2
parent
181ff019f0
commit
f42f8b8915
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>outlierdetection-service-consumer</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-governance-routing</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>outlierdetection-service-provider-1</artifactId>
|
||||
<name>Spring Cloud Governance OutlierDetection Service Provider Example</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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.");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 17071
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: outlierdetection-service-provider-example
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>outlierdetection-service-provider-2</artifactId>
|
||||
<name>Spring Cloud Governance OutlierDetection Service Provider Example</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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 + ".";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 17072
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: outlierdetection-service-provider-example
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-examples</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>outlierdetection-service-provider-3</artifactId>
|
||||
<name>Spring Cloud Governance OutlierDetection Service Provider Example</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@ -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 + ". ";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
server:
|
||||
port: 17073
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: outlierdetection-service-provider-example
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:8848
|
@ -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
|
@ -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<String, Map<String, ServiceInstanceInfo>> 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<String, ServiceInstanceInfo> instanceInfoMap = new ConcurrentHashMap<>();
|
||||
instanceInfoMap.put(instance.getIp() + ":" + instance.getPort(), sif);
|
||||
globalServiceCache.put(targetName, instanceInfoMap);
|
||||
}
|
||||
else {
|
||||
globalServiceCache.forEach((serviceName, instanceMap) -> {
|
||||
Map<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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<String, Map<String, ServiceInstanceInfo>> getAll() {
|
||||
|
||||
return globalServiceCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get service instance list by service name.
|
||||
* @param targetServiceName service name.
|
||||
* @return instance list.
|
||||
*/
|
||||
public static Map<String, ServiceInstanceInfo> getInstanceByServiceName(
|
||||
String targetServiceName) {
|
||||
|
||||
Map<String, ServiceInstanceInfo> 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<ServiceInstanceInfo> sif = null;
|
||||
|
||||
globalServiceCache.keySet().forEach((serviceName) -> {
|
||||
Map<String, ServiceInstanceInfo> instanceInfoMap = globalServiceCache.get(serviceName);
|
||||
sif.set(instanceInfoMap.get(instanceName));
|
||||
});
|
||||
|
||||
return sif.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get no health instance list.
|
||||
*
|
||||
* @return list
|
||||
*/
|
||||
public static List<ServiceInstanceInfo> getCalledErrorInstance() {
|
||||
|
||||
List<ServiceInstanceInfo> res = new ArrayList<>();
|
||||
|
||||
globalServiceCache.forEach((serviceName, instanceMap) -> {
|
||||
Map<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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<String, ServiceInstanceInfo> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<HttpMessageConverters> 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue