diff --git a/pom.xml b/pom.xml index 4295f4c..84224ba 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,6 @@ 1.17.5 1.6.3 2.12.6.1 - 1.7.1 @@ -160,14 +159,6 @@ ${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 bbf5d5f..34c3824 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,23 +4,14 @@ 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; @@ -38,6 +29,7 @@ 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; @@ -79,7 +71,7 @@ public GlobalAndNetworkInterceptorFinder globalAndNetworkInterceptorFinder() { @Bean @ConditionalOnMissingBean - public RetrofitConfigBean retrofitConfigBean(ObjectProvider degradeRuleRegisterObjectProvider) throws IllegalAccessException, InstantiationException { + public RetrofitConfigBean retrofitConfigBean() throws IllegalAccessException, InstantiationException { RetrofitConfigBean retrofitConfigBean = new RetrofitConfigBean(retrofitProperties, globalAndNetworkInterceptorFinder()); // Initialize the connection pool @@ -129,28 +121,9 @@ public RetrofitConfigBean retrofitConfigBean(ObjectProvider Class 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 @@ -172,6 +145,11 @@ 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 fac4639..4f55b4f 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,7 +4,6 @@ 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; @@ -38,8 +37,6 @@ public class RetrofitConfigBean { private BaseResourceNameParser resourceNameParser; - private DegradeRuleRegister degradeRuleRegister; - public RetrofitProperties getRetrofitProperties() { return retrofitProperties; } @@ -108,12 +105,4 @@ 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 8dc853a..f97c8de 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,11 +6,14 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.util.*; +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.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; @@ -33,6 +36,14 @@ 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; @@ -62,7 +73,7 @@ public class RetrofitFactoryBean implements FactoryBean, EnvironmentAware, private static final Map, CallAdapter.Factory> CALL_ADAPTER_FACTORIES_CACHE = new HashMap<>(4); - private final Class retrofitInterface; + private Class retrofitInterface; private Environment environment; @@ -72,7 +83,7 @@ public class RetrofitFactoryBean implements FactoryBean, EnvironmentAware, private ApplicationContext applicationContext; - private final RetrofitClient retrofitClient; + private RetrofitClient retrofitClient; private static final Map, Converter.Factory> CONVERTER_FACTORIES_CACHE = new HashMap<>(4); @@ -111,52 +122,40 @@ public T getObject() throws Exception { ); } - /** - * 加载熔断配置,熔断粒度可控制到方法级别 - */ private void loadDegradeRules() { - DegradeProperty degradeProperty = retrofitProperties.getDegrade(); - DegradeRuleRegister degradeRuleRegister = retrofitConfigBean.getDegradeRuleRegister(); - - 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); - } + 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); + } - /** - * 提取熔断规则,优先级为方法>类>默认 - * @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); + if (degrade == null) { + continue; + } + + DegradeStrategy degradeStrategy = degrade.degradeStrategy(); + BaseResourceNameParser resourceNameParser = retrofitConfigBean.getResourceNameParser(); + String resourceName = resourceNameParser.parseResourceName(method, environment); + + RetrofitDegradeRule degradeRule = new RetrofitDegradeRule(); + degradeRule.setCount(degrade.count()); + degradeRule.setDegradeStrategy(degradeStrategy); + degradeRule.setTimeWindow(degrade.timeWindow()); + degradeRule.setResourceName(resourceName); + RetrofitDegradeRuleInitializer.addRetrofitDegradeRule(degradeRule); } - 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; } /** @@ -278,14 +277,26 @@ private synchronized OkHttpClient getOkHttpClient(Class retrofitClientInterfa } // add DegradeInterceptor - // TODO 这里稍微有点问题,开启熔断则所有实例都会加熔断拦截器,但是拦截器升不生效则取决于是否配置了@Degrade注解,可不可以把这里做成有一个全局默认配置,有@Degrade则走单独配置? DegradeProperty degradeProperty = retrofitProperties.getDegrade(); if (degradeProperty.isEnable()) { - DegradeInterceptor degradeInterceptor = new DegradeInterceptor(); - degradeInterceptor.setEnvironment(environment); - degradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser()); - degradeInterceptor.setDegradeRuleRegister(retrofitConfigBean.getDegradeRuleRegister()); - okHttpClientBuilder.addInterceptor(degradeInterceptor); + 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); + } + } } // add ServiceInstanceChooserInterceptor diff --git a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java similarity index 72% rename from src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java rename to src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java index 1519bbb..547af27 100644 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeInterceptor.java +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/BaseDegradeInterceptor.java @@ -12,18 +12,12 @@ /** * @author 陈添明 */ -public class DegradeInterceptor implements Interceptor { +public abstract class BaseDegradeInterceptor 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; } @@ -53,8 +47,5 @@ public Response intercept(Chain chain) throws IOException { * @throws IOException IOException * */ - protected Response degradeIntercept(String resourceName, Chain chain) throws RetrofitBlockException, IOException { - Request request = chain.request(); - return this.degradeRuleRegister.exec(resourceName, () -> chain.proceed(request)); - } + protected abstract Response degradeIntercept(String resourceName, Chain chain) throws RetrofitBlockException, IOException; } 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 bef4491..febad15 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,8 +3,7 @@ import java.lang.annotation.*; /** - * 应仅采用异常比例模式来控制熔断,超时导致的报错应在okhttp这一层做 - * @author 陈添明 yukdawn@gmail.com + * @author 陈添明 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) @@ -12,12 +11,17 @@ public @interface Degrade { /** - * 异常比例 + * RT threshold or exception ratio threshold count. */ - float count(); + double count(); /** - * 时间窗口size,单位:秒 + * Degrade recover timeout (in seconds) when degradation occurs. */ 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/DegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java deleted file mode 100644 index 742260c..0000000 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java +++ /dev/null @@ -1,32 +0,0 @@ -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 new file mode 100644 index 0000000..d33ed4c --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeStrategy.java @@ -0,0 +1,17 @@ +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 fb3900a..0b25069 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,6 +7,4 @@ 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 deleted file mode 100644 index 644517a..0000000 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Resilience4jDegradeRuleRegister.java +++ /dev/null @@ -1,89 +0,0 @@ -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 c78ce23..757a4d4 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,9 +7,11 @@ public class RetrofitDegradeRule { private String resourceName; - private Float count; + private double count; - private Integer timeWindow; + private int timeWindow; + + private DegradeStrategy degradeStrategy; public String getResourceName() { return resourceName; @@ -19,20 +21,27 @@ public void setResourceName(String resourceName) { this.resourceName = resourceName; } - public Float getCount() { + public double getCount() { return count; } - public void setCount(Float count) { + public void setCount(double count) { this.count = count; } - public Integer getTimeWindow() { + public int getTimeWindow() { return timeWindow; } - public void setTimeWindow(Integer timeWindow) { + public void setTimeWindow(int 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 new file mode 100644 index 0000000..84a5798 --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRuleInitializer.java @@ -0,0 +1,98 @@ +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 new file mode 100644 index 0000000..8fdf478 --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeInterceptor.java @@ -0,0 +1,37 @@ +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 deleted file mode 100644 index c1935cb..0000000 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java +++ /dev/null @@ -1,69 +0,0 @@ -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 5bc0a11..f99df58 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,7 +42,6 @@ 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 3b4465b..8af32c7 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,10 +8,7 @@ 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; @@ -59,9 +56,6 @@ 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); @@ -423,32 +417,4 @@ 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 deleted file mode 100644 index e6b12b8..0000000 --- a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java +++ /dev/null @@ -1,52 +0,0 @@ -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 3115af8..70495cb 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -54,7 +54,7 @@ retrofit: # 是否启用熔断降级 enable: true # 熔断降级实现方式 - degrade-type: resilience4j + degrade-type: sentinel # 熔断资源名称解析器 resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser # 全局连接超时时间