From 6fe63fafefc6d020697b0a1c1b21bd98566a770d Mon Sep 17 00:00:00 2001 From: yukdawn Date: Wed, 6 Apr 2022 00:16:24 +0800 Subject: [PATCH 1/4] =?UTF-8?q?add:=20=E7=86=94=E6=96=AD=E9=99=90=E6=B5=81?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E7=89=88=E4=BC=98=E5=8C=96=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 ++ .../config/RetrofitAutoConfiguration.java | 13 +- .../boot/config/RetrofitConfigBean.java | 11 ++ .../spring/boot/core/RetrofitFactoryBean.java | 116 +++++++++++------- ...terceptor.java => DegradeInterceptor.java} | 13 +- .../boot/degrade/DegradeRuleRegister.java | 21 ++++ .../spring/boot/degrade/DegradeType.java | 2 + .../RetrofitDegradeRuleInitializer.java | 98 --------------- .../degrade/SentinelDegradeInterceptor.java | 37 ------ .../degrade/SentinelDegradeRuleRegister.java | 88 +++++++++++++ 10 files changed, 217 insertions(+), 190 deletions(-) rename src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/{BaseDegradeInterceptor.java => DegradeInterceptor.java} (72%) create mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java delete mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRuleInitializer.java delete mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeInterceptor.java create mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java diff --git a/pom.xml b/pom.xml index 84224ba..d8e8eda 100644 --- a/pom.xml +++ b/pom.xml @@ -159,6 +159,14 @@ ${sentinel.version} provided + + + io.github.resilience4j + resilience4j-spring + 1.7.1 + 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..a03f300 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,11 +4,13 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeRuleRegister; 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.ConditionalOnMissingBean; @@ -29,7 +31,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 +72,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,6 +122,9 @@ public RetrofitConfigBean retrofitConfigBean() throws IllegalAccessException, In Class resourceNameParser = degrade.getResourceNameParser(); retrofitConfigBean.setResourceNameParser(resourceNameParser.newInstance()); + // degrade register + retrofitConfigBean.setDegradeRuleRegister(degradeRuleRegisterObjectProvider.getIfAvailable()); + return retrofitConfigBean; } @@ -145,11 +149,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..5aa0140 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,74 @@ public T getObject() throws Exception { ); } + /** + * 加载熔断配置,熔断粒度可控制到方法级别 + */ private void loadDegradeRules() { - // 读取熔断配置 - Method[] methods = retrofitInterface.getMethods(); - for (Method method : methods) { - if (method.isDefault()) { - continue; + DegradeProperty degradeProperty = retrofitProperties.getDegrade(); + DegradeRuleRegister degradeRuleRegister = retrofitConfigBean.getDegradeRuleRegister(); + + if (!degradeProperty.isEnable()) { + return; + } + Assert.notNull(degradeRuleRegister, "[DegradeRuleRegister] not found bean instance"); + DegradeType degradeType = degradeProperty.getDegradeType(); + switch (degradeType) { + case SENTINEL: { + Method[] methods = retrofitInterface.getMethods(); + List retrofitDegradeRuleList = + Arrays.stream(methods).map(this::convertSentinelRule).filter(Objects::nonNull).collect(Collectors.toList()); + degradeRuleRegister.batchRegister(retrofitDegradeRuleList); + break; } - int modifiers = method.getModifiers(); - if (Modifier.isStatic(modifiers)) { - continue; + case RESILIENCE4J: { + break; } - // 获取熔断配置 - Degrade degrade; - if (method.isAnnotationPresent(Degrade.class)) { - degrade = method.getAnnotation(Degrade.class); - } else { - degrade = retrofitInterface.getAnnotation(Degrade.class); + default: { + throw new IllegalArgumentException("Not currently supported! degradeType=" + degradeType); } - 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); + } + + /** + * TODO 可以优化 和{@link com.github.lianjiatech.retrofit.spring.boot.degrade.SentinelDegradeRuleRegister#convert(com.github.lianjiatech.retrofit.spring.boot.degrade.RetrofitDegradeRule)}放到一起 + * Sentinel 规则转换器 + * @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) { + return null; + } + + 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); + return degradeRule; } /** @@ -284,10 +307,11 @@ private synchronized OkHttpClient getOkHttpClient(Class retrofitClientInterfa case SENTINEL: { try { Class.forName("com.alibaba.csp.sentinel.SphU"); - SentinelDegradeInterceptor sentinelDegradeInterceptor = new SentinelDegradeInterceptor(); - sentinelDegradeInterceptor.setEnvironment(environment); - sentinelDegradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser()); - okHttpClientBuilder.addInterceptor(sentinelDegradeInterceptor); + DegradeInterceptor degradeInterceptor = new DegradeInterceptor(); + degradeInterceptor.setEnvironment(environment); + degradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser()); + degradeInterceptor.setDegradeRuleRegister(retrofitConfigBean.getDegradeRuleRegister()); + okHttpClientBuilder.addInterceptor(degradeInterceptor); } catch (ClassNotFoundException e) { logger.warn("com.alibaba.csp.sentinel not found! No SentinelDegradeInterceptor is set."); } 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..5266514 --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java @@ -0,0 +1,21 @@ +package com.github.lianjiatech.retrofit.spring.boot.degrade; + +import java.io.IOException; +import java.util.List; + +/** + * @author yukdawn@gmail.com 2022/4/5 23:14 + */ +public interface DegradeRuleRegister { + + void register(RetrofitDegradeRule retrofitDegradeRule); + + void batchRegister(List retrofitDegradeRuleList); + + T 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/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/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..44beec2 --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java @@ -0,0 +1,88 @@ +package com.github.lianjiatech.retrofit.spring.boot.degrade; + +import java.io.IOException; +import java.util.List; +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.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.github.lianjiatech.retrofit.spring.boot.config.DegradeProperty; +import com.github.lianjiatech.retrofit.spring.boot.exception.RetrofitException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; + +/** + * @author yukdawn@gmail.com 2022/4/5 23:15 + */ +public class SentinelDegradeRuleRegister implements DegradeRuleRegister{ + + private final static Logger logger = LoggerFactory.getLogger(SentinelDegradeRuleRegister.class); + + private final DegradeProperty degradeProperty; + + public SentinelDegradeRuleRegister(DegradeProperty degradeProperty) { + this.degradeProperty = degradeProperty; + } + + @Override + public void register(RetrofitDegradeRule retrofitDegradeRule) { + throw new RetrofitException("sentinel not supported simple register"); + } + + @Override + public void batchRegister(List retrofitDegradeRuleList) { + if (CollectionUtils.isEmpty(retrofitDegradeRuleList)){ + return; + } + DegradeRuleManager.loadRules(retrofitDegradeRuleList.stream().map(this::convert).collect(Collectors.toList())); + } + + @Override + public T 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){ + DegradeStrategy degradeStrategy = retrofitDegradeRule.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 = retrofitDegradeRule.getResourceName(); + // add degrade rule + DegradeRule rule = new DegradeRule() + .setGrade(grade) + // Max allowed response time + .setCount(retrofitDegradeRule.getCount()) + // Retry timeout (in second) + .setTimeWindow(retrofitDegradeRule.getTimeWindow()); + rule.setResource(resourceName); + return rule; + } +} From 7f8e34d3e394ac72be9522b2acca63125466ec52 Mon Sep 17 00:00:00 2001 From: yukdawn Date: Wed, 6 Apr 2022 21:22:57 +0800 Subject: [PATCH 2/4] =?UTF-8?q?test:=20=E6=B7=BB=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +- .../config/RetrofitAutoConfiguration.java | 9 ++++ .../spring/boot/test/RetrofitStarterTest.java | 26 ++++++++++ .../spring/boot/test/http/HttpApi4.java | 52 +++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java diff --git a/pom.xml b/pom.xml index d8e8eda..56bd2f7 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 1.17.5 1.6.3 2.12.6.1 + 1.7.1 @@ -163,7 +164,7 @@ io.github.resilience4j resilience4j-spring - 1.7.1 + ${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 a03f300..f8e9389 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 @@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit; import com.github.lianjiatech.retrofit.spring.boot.degrade.DegradeRuleRegister; +import com.github.lianjiatech.retrofit.spring.boot.degrade.SentinelDegradeRuleRegister; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; @@ -13,6 +14,7 @@ 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.jackson.JacksonAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -128,6 +130,13 @@ public RetrofitConfigBean retrofitConfigBean(ObjectProvider return retrofitConfigBean; } + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(com.alibaba.csp.sentinel.SphU.class) + public DegradeRuleRegister sentinelDegradeRuleRegister(){ + return new SentinelDegradeRuleRegister(retrofitProperties.getDegrade()); + } + @Bean @ConditionalOnMissingBean 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..5411f6c 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,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import com.github.lianjiatech.retrofit.spring.boot.test.http.HttpApi4; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -56,6 +58,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 +422,25 @@ public void testBoolean() { System.out.println(apiBoolean); } + @Test + public void testDegrade() { + for (int i = 0; i < 10; i++) { + try { + MockResponse response = new MockResponse() + .setResponseCode(400) + .addHeader("Content-Type", "application/text; charset=utf-8") + .addHeader("Cache-Control", "no-cache") + .setBody("false") + .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..3c98b72 --- /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.5) +public interface HttpApi4 { + + /** + * . + * + * @param id . + * @return . + */ + @GET("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; + }; + } + } + +} From 2bde477f7fcb51750363a118ef6c2ee35d9fcbd1 Mon Sep 17 00:00:00 2001 From: yukdawn Date: Thu, 7 Apr 2022 23:19:44 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../retrofit/spring/boot/core/RetrofitFactoryBean.java | 1 + .../lianjiatech/retrofit/spring/boot/test/http/HttpApi4.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 5aa0140..3f995bf 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 @@ -300,6 +300,7 @@ private synchronized OkHttpClient getOkHttpClient(Class retrofitClientInterfa } // add DegradeInterceptor + // TODO 这里稍微有点问题,开启熔断则所有实例都会加熔断拦截器,但是拦截器升不生效则取决于是否配置了@Degrade注解,可不可以把这里做成有一个全局默认配置,有@Degrade则走单独配置? DegradeProperty degradeProperty = retrofitProperties.getDegrade(); if (degradeProperty.isEnable()) { DegradeType degradeType = degradeProperty.getDegradeType(); 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 index 3c98b72..b9f5d47 100644 --- 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 @@ -24,7 +24,7 @@ public interface HttpApi4 { * @param id . * @return . */ - @GET("person") + @GET("/degrade/person") Result getPerson(@Query("id") Long id); @Service From 14fd4d25b4c36bcedce63d6cf0cf2571a3980eaf Mon Sep 17 00:00:00 2001 From: yukdawn Date: Sun, 10 Apr 2022 10:15:35 +0800 Subject: [PATCH 4/4] =?UTF-8?q?add:=20=E6=B7=BB=E5=8A=A0resilience4j?= =?UTF-8?q?=E7=86=94=E6=96=AD=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../config/RetrofitAutoConfiguration.java | 18 +++- .../spring/boot/core/RetrofitFactoryBean.java | 64 +++---------- .../retrofit/spring/boot/degrade/Degrade.java | 14 ++- .../boot/degrade/DegradeRuleRegister.java | 17 +++- .../spring/boot/degrade/DegradeStrategy.java | 17 ---- .../Resilience4jDegradeRuleRegister.java | 89 +++++++++++++++++++ .../boot/degrade/RetrofitDegradeRule.java | 21 ++--- .../degrade/SentinelDegradeRuleRegister.java | 63 +++++-------- .../boot/retry/DefaultRetryInterceptor.java | 1 + .../spring/boot/test/RetrofitStarterTest.java | 18 ++-- .../spring/boot/test/http/HttpApi4.java | 4 +- src/test/resources/application.yml | 2 +- 13 files changed, 184 insertions(+), 146 deletions(-) delete mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeStrategy.java create mode 100644 src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Resilience4jDegradeRuleRegister.java diff --git a/pom.xml b/pom.xml index 56bd2f7..4295f4c 100644 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ io.github.resilience4j - resilience4j-spring + 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 f8e9389..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,8 +4,12 @@ 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; @@ -16,6 +20,7 @@ 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; @@ -132,9 +137,18 @@ public RetrofitConfigBean retrofitConfigBean(ObjectProvider @Bean @ConditionalOnMissingBean - @ConditionalOnClass(com.alibaba.csp.sentinel.SphU.class) + @ConditionalOnClass(SphU.class) + @ConditionalOnProperty(name = "retrofit.degrade.degrade-type", havingValue = "sentinel") public DegradeRuleRegister sentinelDegradeRuleRegister(){ - return new SentinelDegradeRuleRegister(retrofitProperties.getDegrade()); + 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); } 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 3f995bf..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 @@ -122,30 +122,16 @@ private void loadDegradeRules() { return; } Assert.notNull(degradeRuleRegister, "[DegradeRuleRegister] not found bean instance"); - DegradeType degradeType = degradeProperty.getDegradeType(); - switch (degradeType) { - case SENTINEL: { - Method[] methods = retrofitInterface.getMethods(); - List retrofitDegradeRuleList = - Arrays.stream(methods).map(this::convertSentinelRule).filter(Objects::nonNull).collect(Collectors.toList()); - degradeRuleRegister.batchRegister(retrofitDegradeRuleList); - break; - } - case RESILIENCE4J: { - break; - } - default: { - throw new IllegalArgumentException("Not currently supported! degradeType=" + degradeType); - } - - } - - + Method[] methods = retrofitInterface.getMethods(); + List retrofitDegradeRuleList = Arrays.stream(methods) + .map(this::convertSentinelRule) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + degradeRuleRegister.batchRegister(retrofitDegradeRuleList); } /** - * TODO 可以优化 和{@link com.github.lianjiatech.retrofit.spring.boot.degrade.SentinelDegradeRuleRegister#convert(com.github.lianjiatech.retrofit.spring.boot.degrade.RetrofitDegradeRule)}放到一起 - * Sentinel 规则转换器 + * 提取熔断规则,优先级为方法>类>默认 * @param method method * @return RetrofitDegradeRule */ @@ -164,19 +150,11 @@ private RetrofitDegradeRule convertSentinelRule(Method method) { } else { degrade = retrofitInterface.getAnnotation(Degrade.class); } - - if (degrade == null) { - return null; - } - - 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.setCount(Optional.ofNullable(degrade).map(Degrade::count).orElse(null)); + degradeRule.setTimeWindow(Optional.ofNullable(degrade).map(Degrade::timeWindow).orElse(null)); degradeRule.setResourceName(resourceName); return degradeRule; } @@ -303,25 +281,11 @@ private synchronized OkHttpClient getOkHttpClient(Class retrofitClientInterfa // 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"); - DegradeInterceptor degradeInterceptor = new DegradeInterceptor(); - degradeInterceptor.setEnvironment(environment); - degradeInterceptor.setResourceNameParser(retrofitConfigBean.getResourceNameParser()); - degradeInterceptor.setDegradeRuleRegister(retrofitConfigBean.getDegradeRuleRegister()); - okHttpClientBuilder.addInterceptor(degradeInterceptor); - } 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/DegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/DegradeRuleRegister.java index 5266514..742260c 100644 --- 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 @@ -3,16 +3,27 @@ import java.io.IOException; import java.util.List; +import okhttp3.Response; + /** * @author yukdawn@gmail.com 2022/4/5 23:14 */ public interface DegradeRuleRegister { - void register(RetrofitDegradeRule retrofitDegradeRule); - + /** + * 批量注册规则 + * @param retrofitDegradeRuleList 规则描述对象集合 + */ void batchRegister(List retrofitDegradeRuleList); - T exec(String resourceName, DegradeProxyMethod func) throws IOException; + /** + * 使用规则代理执行目标方法 + * @param resourceName 资源名称 + * @param func 目标方法 + * @return okhttp响应 + * @throws IOException IOException + */ + Response exec(String resourceName, DegradeProxyMethod func) throws IOException; @FunctionalInterface interface DegradeProxyMethod{ 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/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/SentinelDegradeRuleRegister.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegradeRuleRegister.java index 44beec2..c1935cb 100644 --- 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 @@ -2,6 +2,8 @@ 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; @@ -9,12 +11,10 @@ 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 com.github.lianjiatech.retrofit.spring.boot.config.DegradeProperty; -import com.github.lianjiatech.retrofit.spring.boot.exception.RetrofitException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import okhttp3.Response; import org.springframework.util.CollectionUtils; /** @@ -22,19 +22,6 @@ */ public class SentinelDegradeRuleRegister implements DegradeRuleRegister{ - private final static Logger logger = LoggerFactory.getLogger(SentinelDegradeRuleRegister.class); - - private final DegradeProperty degradeProperty; - - public SentinelDegradeRuleRegister(DegradeProperty degradeProperty) { - this.degradeProperty = degradeProperty; - } - - @Override - public void register(RetrofitDegradeRule retrofitDegradeRule) { - throw new RetrofitException("sentinel not supported simple register"); - } - @Override public void batchRegister(List retrofitDegradeRuleList) { if (CollectionUtils.isEmpty(retrofitDegradeRuleList)){ @@ -44,7 +31,7 @@ public void batchRegister(List retrofitDegradeRuleList) { } @Override - public T exec(String resourceName, DegradeProxyMethod func) throws IOException { + public Response exec(String resourceName, DegradeProxyMethod func) throws IOException { Entry entry = null; try { entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.OUT); @@ -59,30 +46,24 @@ public T exec(String resourceName, DegradeProxyMethod func) throws IOExce } private DegradeRule convert(RetrofitDegradeRule retrofitDegradeRule){ - DegradeStrategy degradeStrategy = retrofitDegradeRule.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 = retrofitDegradeRule.getResourceName(); // add degrade rule - DegradeRule rule = new DegradeRule() - .setGrade(grade) - // Max allowed response time - .setCount(retrofitDegradeRule.getCount()) - // Retry timeout (in second) - .setTimeWindow(retrofitDegradeRule.getTimeWindow()); - rule.setResource(resourceName); + 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 5411f6c..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 @@ -9,6 +9,7 @@ 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; @@ -424,22 +425,29 @@ public void testBoolean() { @Test public void testDegrade() { - for (int i = 0; i < 10; i++) { + 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(400) + .setResponseCode(200) .addHeader("Content-Type", "application/text; charset=utf-8") .addHeader("Cache-Control", "no-cache") - .setBody("false") + .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()); + 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 index b9f5d47..e6b12b8 100644 --- 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 @@ -15,7 +15,7 @@ * @author 陈添明 */ @RetrofitClient(baseUrl = "${test.baseUrl}", fallbackFactory = HttpApi4.HttpDegradeFallbackFactory.class) -@Degrade(count = 0.5) +@Degrade(count = 0.5F) public interface HttpApi4 { /** @@ -24,7 +24,7 @@ public interface HttpApi4 { * @param id . * @return . */ - @GET("/degrade/person") + @GET("degrade/person") Result getPerson(@Query("id") Long id); @Service 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 # 全局连接超时时间