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; + } +}