diff --git a/pom.xml b/pom.xml
index 84224ba..4295f4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,7 @@
1.17.5
1.6.3
2.12.6.1
+ 1.7.1
@@ -159,6 +160,14 @@
${sentinel.version}
provided
+
+
+ io.github.resilience4j
+ resilience4j-spring-boot2
+ ${resilience4j-spring.version}
+ provided
+
+
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitAutoConfiguration.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitAutoConfiguration.java
index 34c3824..bbf5d5f 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitAutoConfiguration.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitAutoConfiguration.java
@@ -4,14 +4,23 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
+import com.alibaba.csp.sentinel.SphU;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeRuleRegister;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.Resilience4jDegradeRuleRegister;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.SentinelDegradeRuleRegister;
+import io.github.resilience4j.circuitbreaker.CircuitBreaker;
+import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
@@ -29,7 +38,6 @@
import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitFactoryBean;
import com.github.lianjiatech.retrofit.spring.boot.core.ServiceInstanceChooser;
import com.github.lianjiatech.retrofit.spring.boot.degrade.BaseResourceNameParser;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.RetrofitDegradeRuleInitializer;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.GlobalAndNetworkInterceptorFinder;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.ServiceInstanceChooserInterceptor;
import com.github.lianjiatech.retrofit.spring.boot.retry.BaseRetryInterceptor;
@@ -71,7 +79,7 @@ public GlobalAndNetworkInterceptorFinder globalAndNetworkInterceptorFinder() {
@Bean
@ConditionalOnMissingBean
- public RetrofitConfigBean retrofitConfigBean() throws IllegalAccessException, InstantiationException {
+ public RetrofitConfigBean retrofitConfigBean(ObjectProvider degradeRuleRegisterObjectProvider) throws IllegalAccessException, InstantiationException {
RetrofitConfigBean retrofitConfigBean =
new RetrofitConfigBean(retrofitProperties, globalAndNetworkInterceptorFinder());
// Initialize the connection pool
@@ -121,9 +129,28 @@ public RetrofitConfigBean retrofitConfigBean() throws IllegalAccessException, In
Class extends BaseResourceNameParser> resourceNameParser = degrade.getResourceNameParser();
retrofitConfigBean.setResourceNameParser(resourceNameParser.newInstance());
+ // degrade register
+ retrofitConfigBean.setDegradeRuleRegister(degradeRuleRegisterObjectProvider.getIfAvailable());
+
return retrofitConfigBean;
}
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnClass(SphU.class)
+ @ConditionalOnProperty(name = "retrofit.degrade.degrade-type", havingValue = "sentinel")
+ public DegradeRuleRegister sentinelDegradeRuleRegister(){
+ return new SentinelDegradeRuleRegister();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnClass(CircuitBreaker.class)
+ @ConditionalOnProperty(name = "retrofit.degrade.degrade-type", havingValue = "resilience4j")
+ public DegradeRuleRegister resilience4JDegradeRuleRegister(CircuitBreakerRegistry circuitBreakerRegistry){
+ return new Resilience4jDegradeRuleRegister(circuitBreakerRegistry);
+ }
+
@Bean
@ConditionalOnMissingBean
@@ -145,11 +172,6 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
this.applicationContext = applicationContext;
}
- @Bean
- public RetrofitDegradeRuleInitializer retrofitDegradeRuleInitializer() {
- return new RetrofitDegradeRuleInitializer(retrofitProperties.getDegrade());
- }
-
@Configuration
@Import({AutoConfiguredRetrofitScannerRegistrar.class})
@ConditionalOnMissingBean(RetrofitFactoryBean.class)
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitConfigBean.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitConfigBean.java
index 4f55b4f..fac4639 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitConfigBean.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/config/RetrofitConfigBean.java
@@ -4,6 +4,7 @@
import java.util.Map;
import com.github.lianjiatech.retrofit.spring.boot.degrade.BaseResourceNameParser;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeRuleRegister;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.GlobalAndNetworkInterceptorFinder;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.GlobalInterceptor;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.NetworkInterceptor;
@@ -37,6 +38,8 @@ public class RetrofitConfigBean {
private BaseResourceNameParser resourceNameParser;
+ private DegradeRuleRegister degradeRuleRegister;
+
public RetrofitProperties getRetrofitProperties() {
return retrofitProperties;
}
@@ -105,4 +108,12 @@ public BaseResourceNameParser getResourceNameParser() {
public void setResourceNameParser(BaseResourceNameParser resourceNameParser) {
this.resourceNameParser = resourceNameParser;
}
+
+ public DegradeRuleRegister getDegradeRuleRegister() {
+ return degradeRuleRegister;
+ }
+
+ public void setDegradeRuleRegister(DegradeRuleRegister degradeRuleRegister) {
+ this.degradeRuleRegister = degradeRuleRegister;
+ }
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
index f97c8de..8dc853a 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java
@@ -6,14 +6,11 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -36,14 +33,6 @@
import com.github.lianjiatech.retrofit.spring.boot.config.LogProperty;
import com.github.lianjiatech.retrofit.spring.boot.config.RetrofitConfigBean;
import com.github.lianjiatech.retrofit.spring.boot.config.RetrofitProperties;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.BaseResourceNameParser;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.Degrade;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeStrategy;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeType;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.FallbackFactory;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.RetrofitDegradeRule;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.RetrofitDegradeRuleInitializer;
-import com.github.lianjiatech.retrofit.spring.boot.degrade.SentinelDegradeInterceptor;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BaseLoggingInterceptor;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.ErrorDecoderInterceptor;
@@ -73,7 +62,7 @@ public class RetrofitFactoryBean implements FactoryBean, EnvironmentAware,
private static final Map, CallAdapter.Factory> CALL_ADAPTER_FACTORIES_CACHE =
new HashMap<>(4);
- private Class retrofitInterface;
+ private final Class retrofitInterface;
private Environment environment;
@@ -83,7 +72,7 @@ public class RetrofitFactoryBean implements FactoryBean, EnvironmentAware,
private ApplicationContext applicationContext;
- private RetrofitClient retrofitClient;
+ private final RetrofitClient retrofitClient;
private static final Map, Converter.Factory> CONVERTER_FACTORIES_CACHE =
new HashMap<>(4);
@@ -122,40 +111,52 @@ public T getObject() throws Exception {
);
}
+ /**
+ * 加载熔断配置,熔断粒度可控制到方法级别
+ */
private void loadDegradeRules() {
- // 读取熔断配置
- Method[] methods = retrofitInterface.getMethods();
- for (Method method : methods) {
- if (method.isDefault()) {
- continue;
- }
- int modifiers = method.getModifiers();
- if (Modifier.isStatic(modifiers)) {
- continue;
- }
- // 获取熔断配置
- Degrade degrade;
- if (method.isAnnotationPresent(Degrade.class)) {
- degrade = method.getAnnotation(Degrade.class);
- } else {
- degrade = retrofitInterface.getAnnotation(Degrade.class);
- }
-
- if (degrade == null) {
- continue;
- }
+ DegradeProperty degradeProperty = retrofitProperties.getDegrade();
+ DegradeRuleRegister degradeRuleRegister = retrofitConfigBean.getDegradeRuleRegister();
- DegradeStrategy degradeStrategy = degrade.degradeStrategy();
- BaseResourceNameParser resourceNameParser = retrofitConfigBean.getResourceNameParser();
- String resourceName = resourceNameParser.parseResourceName(method, environment);
+ if (!degradeProperty.isEnable()) {
+ return;
+ }
+ Assert.notNull(degradeRuleRegister, "[DegradeRuleRegister] not found bean instance");
+ Method[] methods = retrofitInterface.getMethods();
+ List retrofitDegradeRuleList = Arrays.stream(methods)
+ .map(this::convertSentinelRule)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ degradeRuleRegister.batchRegister(retrofitDegradeRuleList);
+ }
- RetrofitDegradeRule degradeRule = new RetrofitDegradeRule();
- degradeRule.setCount(degrade.count());
- degradeRule.setDegradeStrategy(degradeStrategy);
- degradeRule.setTimeWindow(degrade.timeWindow());
- degradeRule.setResourceName(resourceName);
- RetrofitDegradeRuleInitializer.addRetrofitDegradeRule(degradeRule);
+ /**
+ * 提取熔断规则,优先级为方法>类>默认
+ * @param method method
+ * @return RetrofitDegradeRule
+ */
+ private RetrofitDegradeRule convertSentinelRule(Method method) {
+ if (method.isDefault()) {
+ return null;
}
+ int modifiers = method.getModifiers();
+ if (Modifier.isStatic(modifiers)) {
+ return null;
+ }
+ // 获取熔断配置
+ Degrade degrade;
+ if (method.isAnnotationPresent(Degrade.class)) {
+ degrade = method.getAnnotation(Degrade.class);
+ } else {
+ degrade = retrofitInterface.getAnnotation(Degrade.class);
+ }
+ BaseResourceNameParser resourceNameParser = retrofitConfigBean.getResourceNameParser();
+ String resourceName = resourceNameParser.parseResourceName(method, environment);
+ RetrofitDegradeRule degradeRule = new RetrofitDegradeRule();
+ degradeRule.setCount(Optional.ofNullable(degrade).map(Degrade::count).orElse(null));
+ degradeRule.setTimeWindow(Optional.ofNullable(degrade).map(Degrade::timeWindow).orElse(null));
+ degradeRule.setResourceName(resourceName);
+ return degradeRule;
}
/**
@@ -277,26 +278,14 @@ private synchronized OkHttpClient getOkHttpClient(Class> retrofitClientInterfa
}
// add DegradeInterceptor
+ // TODO 这里稍微有点问题,开启熔断则所有实例都会加熔断拦截器,但是拦截器升不生效则取决于是否配置了@Degrade注解,可不可以把这里做成有一个全局默认配置,有@Degrade则走单独配置?
DegradeProperty degradeProperty = retrofitProperties.getDegrade();
if (degradeProperty.isEnable()) {
- DegradeType degradeType = degradeProperty.getDegradeType();
- switch (degradeType) {
- case SENTINEL: {
- try {
- Class.forName("com.alibaba.csp.sentinel.SphU");
- SentinelDegradeInterceptor sentinelDegradeInterceptor = new SentinelDegradeInterceptor();
- sentinelDegradeInterceptor.setEnvironment(environment);
- sentinelDegradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser());
- okHttpClientBuilder.addInterceptor(sentinelDegradeInterceptor);
- } catch (ClassNotFoundException e) {
- logger.warn("com.alibaba.csp.sentinel not found! No SentinelDegradeInterceptor is set.");
- }
- break;
- }
- default: {
- throw new IllegalArgumentException("Not currently supported! degradeType=" + degradeType);
- }
- }
+ DegradeInterceptor degradeInterceptor = new DegradeInterceptor();
+ degradeInterceptor.setEnvironment(environment);
+ degradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser());
+ degradeInterceptor.setDegradeRuleRegister(retrofitConfigBean.getDegradeRuleRegister());
+ okHttpClientBuilder.addInterceptor(degradeInterceptor);
}
// add ServiceInstanceChooserInterceptor
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Degrade.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Degrade.java
index febad15..bef4491 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Degrade.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Degrade.java
@@ -3,7 +3,8 @@
import java.lang.annotation.*;
/**
- * @author 陈添明
+ * 应仅采用异常比例模式来控制熔断,超时导致的报错应在okhttp这一层做
+ * @author 陈添明 yukdawn@gmail.com
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@@ -11,17 +12,12 @@
public @interface Degrade {
/**
- * RT threshold or exception ratio threshold count.
+ * 异常比例
*/
- double count();
+ float count();
/**
- * Degrade recover timeout (in seconds) when degradation occurs.
+ * 时间窗口size,单位:秒
*/
int timeWindow() default 5;
-
- /**
- * Degrade strategy (0: average RT, 1: exception ratio).
- */
- DegradeStrategy degradeStrategy() default DegradeStrategy.AVERAGE_RT;
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java
similarity index 72%
rename from src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java
rename to src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java
index 547af27..1519bbb 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java
@@ -12,12 +12,18 @@
/**
* @author 陈添明
*/
-public abstract class BaseDegradeInterceptor implements Interceptor {
+public class DegradeInterceptor implements Interceptor {
private Environment environment;
private BaseResourceNameParser resourceNameParser;
+ protected DegradeRuleRegister degradeRuleRegister;
+
+ public void setDegradeRuleRegister(DegradeRuleRegister degradeRuleRegister) {
+ this.degradeRuleRegister = degradeRuleRegister;
+ }
+
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@@ -47,5 +53,8 @@ public Response intercept(Chain chain) throws IOException {
* @throws IOException IOException
*
*/
- protected abstract Response degradeIntercept(String resourceName, Chain chain) throws RetrofitBlockException, IOException;
+ protected Response degradeIntercept(String resourceName, Chain chain) throws RetrofitBlockException, IOException {
+ Request request = chain.request();
+ return this.degradeRuleRegister.exec(resourceName, () -> chain.proceed(request));
+ }
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java
new file mode 100644
index 0000000..742260c
--- /dev/null
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java
@@ -0,0 +1,32 @@
+package com.github.lianjiatech.retrofit.spring.boot.degrade;
+
+import java.io.IOException;
+import java.util.List;
+
+import okhttp3.Response;
+
+/**
+ * @author yukdawn@gmail.com 2022/4/5 23:14
+ */
+public interface DegradeRuleRegister {
+
+ /**
+ * 批量注册规则
+ * @param retrofitDegradeRuleList 规则描述对象集合
+ */
+ void batchRegister(List retrofitDegradeRuleList);
+
+ /**
+ * 使用规则代理执行目标方法
+ * @param resourceName 资源名称
+ * @param func 目标方法
+ * @return okhttp响应
+ * @throws IOException IOException
+ */
+ Response exec(String resourceName, DegradeProxyMethod func) throws IOException;
+
+ @FunctionalInterface
+ interface DegradeProxyMethod{
+ R get() throws IOException;
+ }
+}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeStrategy.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeStrategy.java
deleted file mode 100644
index d33ed4c..0000000
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeStrategy.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.lianjiatech.retrofit.spring.boot.degrade;
-
-/**
- * @author 陈添明
- */
-public enum DegradeStrategy {
-
- /**
- * average RT
- */
- AVERAGE_RT,
-
- /**
- * exception ratio
- */
- EXCEPTION_RATIO,
-}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeType.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeType.java
index 0b25069..fb3900a 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeType.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeType.java
@@ -7,4 +7,6 @@ public enum DegradeType {
SENTINEL,
+ RESILIENCE4J;
+
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Resilience4jDegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Resilience4jDegradeRuleRegister.java
new file mode 100644
index 0000000..644517a
--- /dev/null
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Resilience4jDegradeRuleRegister.java
@@ -0,0 +1,89 @@
+package com.github.lianjiatech.retrofit.spring.boot.degrade;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
+import io.github.resilience4j.circuitbreaker.CircuitBreaker;
+import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
+import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
+import io.github.resilience4j.core.StopWatch;
+import okhttp3.Response;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * @author yukdawn@gmail.com 2022/4/5 23:15
+ */
+public class Resilience4jDegradeRuleRegister implements DegradeRuleRegister{
+
+ private final CircuitBreakerRegistry circuitBreakerRegistry;
+
+ public Resilience4jDegradeRuleRegister(CircuitBreakerRegistry circuitBreakerRegistry) {
+ this.circuitBreakerRegistry = circuitBreakerRegistry;
+ }
+
+ @Override
+ public void batchRegister(List ruleList) {
+ if (CollectionUtils.isEmpty(ruleList)){
+ return;
+ }
+ for (RetrofitDegradeRule rule : ruleList) {
+ circuitBreakerRegistry.circuitBreaker(rule.getResourceName(), this.convert(rule));
+ }
+
+ }
+
+ @Override
+ public Response exec(String resourceName, DegradeProxyMethod func) throws IOException {
+ CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(resourceName);
+ System.out.println("当前断路器状态: "+ circuitBreaker.getState());
+
+ final StopWatch stopWatch = StopWatch.start();
+ try {
+ circuitBreaker.acquirePermission();
+ final Response response = func.get();
+ circuitBreaker.onResult(stopWatch.stop().toNanos(), TimeUnit.NANOSECONDS, response);
+ return response;
+ }catch (CallNotPermittedException e){
+ throw new RetrofitBlockException(e);
+ } catch (Exception exception) {
+ circuitBreaker.onError(stopWatch.stop().toNanos(), TimeUnit.NANOSECONDS, exception);
+ throw exception;
+ }
+ }
+
+ private CircuitBreakerConfig convert(RetrofitDegradeRule rule){
+ // add degrade rule
+ CircuitBreakerConfig.Builder circuitBreakerBuilder = CircuitBreakerConfig.from(newInstanceByDefault());
+ if (Objects.nonNull(rule.getCount())){
+ circuitBreakerBuilder.failureRateThreshold(rule.getCount());
+ }
+ if (Objects.nonNull(rule.getTimeWindow())){
+ circuitBreakerBuilder.slidingWindowSize(rule.getTimeWindow());
+ }
+ return circuitBreakerBuilder.build();
+ }
+
+ public CircuitBreakerConfig newInstanceByDefault(){
+ // 断路器配置
+ return CircuitBreakerConfig.custom()
+ // 滑动窗口的类型为时间窗口
+ .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
+ // 时间窗口的大小为60秒
+ .slidingWindowSize(60)
+ // 在单位时间窗口内最少需要10次调用才能开始进行统计计算
+ .minimumNumberOfCalls(10)
+ // 在单位时间窗口内调用失败率达到60%后会启动断路器
+ .failureRateThreshold(60)
+ // 允许断路器自动由打开状态转换为半开状态
+ .enableAutomaticTransitionFromOpenToHalfOpen()
+ // 在半开状态下允许进行正常调用的次数
+ .permittedNumberOfCallsInHalfOpenState(5)
+ // 断路器打开状态转换为半开状态需要等待60秒
+ .waitDurationInOpenState(Duration.ofSeconds(60))
+ .build();
+ }
+}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java
index 757a4d4..c78ce23 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java
@@ -7,11 +7,9 @@ public class RetrofitDegradeRule {
private String resourceName;
- private double count;
+ private Float count;
- private int timeWindow;
-
- private DegradeStrategy degradeStrategy;
+ private Integer timeWindow;
public String getResourceName() {
return resourceName;
@@ -21,27 +19,20 @@ public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
- public double getCount() {
+ public Float getCount() {
return count;
}
- public void setCount(double count) {
+ public void setCount(Float count) {
this.count = count;
}
- public int getTimeWindow() {
+ public Integer getTimeWindow() {
return timeWindow;
}
- public void setTimeWindow(int timeWindow) {
+ public void setTimeWindow(Integer timeWindow) {
this.timeWindow = timeWindow;
}
- public DegradeStrategy getDegradeStrategy() {
- return degradeStrategy;
- }
-
- public void setDegradeStrategy(DegradeStrategy degradeStrategy) {
- this.degradeStrategy = degradeStrategy;
- }
}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRuleInitializer.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRuleInitializer.java
deleted file mode 100644
index 84a5798..0000000
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRuleInitializer.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.github.lianjiatech.retrofit.spring.boot.degrade;
-
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
-import com.github.lianjiatech.retrofit.spring.boot.config.DegradeProperty;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.boot.context.event.ApplicationReadyEvent;
-import org.springframework.context.ApplicationListener;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * @author 陈添明
- */
-public class RetrofitDegradeRuleInitializer implements ApplicationListener {
-
- private final static Logger logger = LoggerFactory.getLogger(RetrofitDegradeRuleInitializer.class);
-
- private final DegradeProperty degradeProperty;
-
-
- private static List LIST = new CopyOnWriteArrayList<>();
-
- public RetrofitDegradeRuleInitializer(DegradeProperty degradeProperty) {
- this.degradeProperty = degradeProperty;
- }
-
-
- public static void addRetrofitDegradeRule(RetrofitDegradeRule degradeRule) {
- if (degradeRule == null) {
- return;
- }
- LIST.add(degradeRule);
- }
-
-
- /**
- * Handle an application event.
- *
- * @param event the event to respond to
- */
- @Override
- public void onApplicationEvent(ApplicationReadyEvent event) {
- if (!degradeProperty.isEnable()) {
- return;
- }
-
- DegradeType degradeType = degradeProperty.getDegradeType();
- switch (degradeType) {
- case SENTINEL: {
- try {
- Class.forName("com.alibaba.csp.sentinel.SphU");
- List rules = new ArrayList<>();
-
- for (RetrofitDegradeRule degradeRule : LIST) {
- DegradeStrategy degradeStrategy = degradeRule.getDegradeStrategy();
- int grade;
- switch (degradeStrategy) {
- case AVERAGE_RT: {
- grade = 0;
- break;
- }
- case EXCEPTION_RATIO: {
- grade = 1;
- break;
- }
- default: {
- throw new IllegalArgumentException("Not currently supported! degradeStrategy=" + degradeStrategy);
- }
- }
- String resourceName = degradeRule.getResourceName();
- // add degrade rule
- DegradeRule rule = new DegradeRule()
- .setGrade(grade)
- // Max allowed response time
- .setCount(degradeRule.getCount())
- // Retry timeout (in second)
- .setTimeWindow(degradeRule.getTimeWindow());
- rule.setResource(resourceName);
- rules.add(rule);
- }
- DegradeRuleManager.loadRules(rules);
-
- } catch (Exception e) {
- logger.warn("com.alibaba.csp.sentinel not found! No SentinelDegradeInterceptor is set.");
- }
- break;
- }
- default: {
- throw new IllegalArgumentException("Not currently supported! degradeType=" + degradeType);
- }
-
- }
- }
-}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeInterceptor.java
deleted file mode 100644
index 8fdf478..0000000
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeInterceptor.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.github.lianjiatech.retrofit.spring.boot.degrade;
-
-import com.alibaba.csp.sentinel.*;
-import com.alibaba.csp.sentinel.slots.block.BlockException;
-import okhttp3.Request;
-import okhttp3.Response;
-
-import java.io.IOException;
-
-/**
- * @author 陈添明
- */
-public class SentinelDegradeInterceptor extends BaseDegradeInterceptor {
-
- /**
- * 熔断拦截处理
- *
- * @param chain 请求执行链
- * @return 请求响应
- * @throws RetrofitBlockException 如果触发熔断,抛出RetrofitBlockException异常!
- */
- @Override
- protected Response degradeIntercept(String resourceName, Chain chain) throws RetrofitBlockException, IOException {
- Request request = chain.request();
- Entry entry = null;
- try {
- entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
- return chain.proceed(request);
- } catch (BlockException e) {
- throw new RetrofitBlockException(e);
- } finally {
- if (entry != null) {
- entry.exit();
- }
- }
- }
-}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java
new file mode 100644
index 0000000..c1935cb
--- /dev/null
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java
@@ -0,0 +1,69 @@
+package com.github.lianjiatech.retrofit.spring.boot.degrade;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.ResourceTypeConstants;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import okhttp3.Response;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * @author yukdawn@gmail.com 2022/4/5 23:15
+ */
+public class SentinelDegradeRuleRegister implements DegradeRuleRegister{
+
+ @Override
+ public void batchRegister(List retrofitDegradeRuleList) {
+ if (CollectionUtils.isEmpty(retrofitDegradeRuleList)){
+ return;
+ }
+ DegradeRuleManager.loadRules(retrofitDegradeRuleList.stream().map(this::convert).collect(Collectors.toList()));
+ }
+
+ @Override
+ public Response exec(String resourceName, DegradeProxyMethod func) throws IOException {
+ Entry entry = null;
+ try {
+ entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
+ return func.get();
+ } catch (BlockException e) {
+ throw new RetrofitBlockException(e);
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ }
+ }
+
+ private DegradeRule convert(RetrofitDegradeRule retrofitDegradeRule){
+ // add degrade rule
+ DegradeRule rule = defaultRuleNewInstance();
+ if (Objects.nonNull(retrofitDegradeRule.getTimeWindow())){
+ rule.setTimeWindow(retrofitDegradeRule.getTimeWindow());
+ }
+ rule.setCount(rule.getTimeWindow() *
+ Optional.ofNullable(retrofitDegradeRule.getCount()).orElse(0.6F));
+ rule.setResource(retrofitDegradeRule.getResourceName());
+ return rule;
+ }
+
+ public DegradeRule defaultRuleNewInstance(){
+ return new DegradeRule()
+ // 使用异常数量策略,做个中转实现异常比例,异常比例策略在sentinel的实现为每秒检测,不是根据时间窗口来
+ .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT)
+ // 异常比例
+ .setCount(60*0.6)
+ // 时间窗口
+ .setTimeWindow(60);
+ }
+}
diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/DefaultRetryInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/DefaultRetryInterceptor.java
index f99df58..5bc0a11 100644
--- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/DefaultRetryInterceptor.java
+++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/retry/DefaultRetryInterceptor.java
@@ -42,6 +42,7 @@ protected Response retryIntercept(int maxRetries, int intervalMs, RetryRule[] re
if (shouldThrowEx(retryRuleSet, e)) {
throw new RuntimeException(e);
} else {
+ // TODO 这里应该还把最后一次的原始错误也要抛出来
if (!retryStrategy.shouldRetry()) {
// 最后一次还没成功,抛出异常
throw new RuntimeException("Retry Failed: Total " + maxRetries
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitStarterTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitStarterTest.java
index 8af32c7..3b4465b 100644
--- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitStarterTest.java
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/RetrofitStarterTest.java
@@ -8,7 +8,10 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+import com.github.lianjiatech.retrofit.spring.boot.test.http.HttpApi4;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -56,6 +59,9 @@ public class RetrofitStarterTest {
@Autowired
private HttpApi3 httpApi3;
+ @Autowired
+ private HttpApi4 httpApi4;
+
private static final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
@@ -417,4 +423,32 @@ public void testBoolean() {
System.out.println(apiBoolean);
}
+ @Test
+ public void testDegrade() {
+ IntStream.range(0, 100).parallel().forEach((i) ->{
+ try {
+ Person mockPerson = new Person().setId(1L)
+ .setName("test")
+ .setAge(10);
+ Result mockResult = new Result<>()
+ .setCode(0)
+ .setMsg("ok")
+ .setData(mockPerson);
+ MockResponse response = new MockResponse()
+ .setResponseCode(200)
+ .addHeader("Content-Type", "application/text; charset=utf-8")
+ .addHeader("Cache-Control", "no-cache")
+ .setBody(objectMapper.writeValueAsString(mockResult))
+ .setHeadersDelay(5, TimeUnit.SECONDS);
+ server.enqueue(response);
+ System.out.println(httpApi4.getPerson(2L).getCode());
+ }catch (Exception e){
+ System.out.println("抛出异常:" + e.getMessage());
+ }finally {
+ System.out.println("当前请求轮次: "+ (i+1));
+ }
+ });
+
+ }
+
}
diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java
new file mode 100644
index 0000000..e6b12b8
--- /dev/null
+++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java
@@ -0,0 +1,52 @@
+package com.github.lianjiatech.retrofit.spring.boot.test.http;
+
+import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.Degrade;
+import com.github.lianjiatech.retrofit.spring.boot.degrade.FallbackFactory;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person;
+import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import retrofit2.http.GET;
+import retrofit2.http.Query;
+
+/**
+ * @author 陈添明
+ */
+@RetrofitClient(baseUrl = "${test.baseUrl}", fallbackFactory = HttpApi4.HttpDegradeFallbackFactory.class)
+@Degrade(count = 0.5F)
+public interface HttpApi4 {
+
+ /**
+ * .
+ *
+ * @param id .
+ * @return .
+ */
+ @GET("degrade/person")
+ Result getPerson(@Query("id") Long id);
+
+ @Service
+ public class HttpDegradeFallbackFactory implements FallbackFactory {
+ Logger log = LoggerFactory.getLogger(HttpDegradeFallbackFactory.class);
+ /**
+ * Returns an instance of the fallback appropriate for the given cause
+ *
+ * @param cause fallback cause
+ * @return 实现了retrofit接口的实例。an instance that implements the retrofit interface.
+ */
+ @Override
+ public HttpApi4 create(Throwable cause) {
+ log.error("触发熔断了! ", cause.getMessage(), cause);
+ return id -> {
+ Result fallback = new Result<>();
+ fallback.setCode(100)
+ .setMsg("fallback")
+ .setData(new Person());
+ return fallback;
+ };
+ }
+ }
+
+}
diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index 70495cb..3115af8 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -54,7 +54,7 @@ retrofit:
# 是否启用熔断降级
enable: true
# 熔断降级实现方式
- degrade-type: sentinel
+ degrade-type: resilience4j
# 熔断资源名称解析器
resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
# 全局连接超时时间