diff --git a/README.md b/README.md index d6c1b2c..0799978 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter] com.github.lianjiatech retrofit-spring-boot-starter - 2.2.22 + 2.3.0 ``` @@ -67,7 +67,7 @@ gitee项目地址:[https://gitee.com/lianjiatech/retrofit-spring-boot-starter] com.github.lianjiatech retrofit-spring-boot-starter - 2.2.22 + 2.3.0 com.squareup.okhttp3 diff --git a/README_EN.md b/README_EN.md index bfa0233..a455dbd 100644 --- a/README_EN.md +++ b/README_EN.md @@ -43,7 +43,7 @@ com.github.lianjiatech retrofit-spring-boot-starter - 2.2.22 + 2.3.0 ``` @@ -53,7 +53,7 @@ This project depends on Retrofit-2.9.0, okhttp-3.14.9, and okio-1.17.5 versions. com.github.lianjiatech retrofit-spring-boot-starter - 2.2.22 + 2.3.0 com.squareup.okhttp3 diff --git a/pom.xml b/pom.xml index 84224ba..398728b 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.lianjiatech retrofit-spring-boot-starter - 2.2.22 + 2.3.0 retrofit-spring-boot-starter retrofit-spring-boot-starter 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..652e5e6 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 @@ -29,7 +29,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; @@ -145,11 +144,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/core/RetrofitFactoryBean.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/core/RetrofitFactoryBean.java index f97c8de..f422304 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 @@ -27,6 +27,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +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.annotation.Intercept; import com.github.lianjiatech.retrofit.spring.boot.annotation.InterceptMark; import com.github.lianjiatech.retrofit.spring.boot.annotation.Intercepts; @@ -36,13 +38,9 @@ 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.SentinelDegrade; 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; @@ -123,8 +121,19 @@ public T getObject() throws Exception { } private void loadDegradeRules() { + DegradeProperty degrade = retrofitProperties.getDegrade(); + if (!degrade.isEnable()) { + return; + } + if (degrade.getDegradeType() == DegradeType.SENTINEL) { + loadSentinelDegradeRules(); + } + } + + private void loadSentinelDegradeRules() { // 读取熔断配置 Method[] methods = retrofitInterface.getMethods(); + List rules = new ArrayList<>(); for (Method method : methods) { if (method.isDefault()) { continue; @@ -134,28 +143,29 @@ private void loadDegradeRules() { continue; } // 获取熔断配置 - Degrade degrade; - if (method.isAnnotationPresent(Degrade.class)) { - degrade = method.getAnnotation(Degrade.class); - } else { - degrade = retrofitInterface.getAnnotation(Degrade.class); - } - - if (degrade == null) { + SentinelDegrade sentinelDegrade = getSentinelDegrade(method); + if (sentinelDegrade == null) { continue; } + String resourceName = retrofitConfigBean.getResourceNameParser().parseResourceName(method, environment); + DegradeRule degradeRule = new DegradeRule() + .setCount(sentinelDegrade.count()) + .setTimeWindow(sentinelDegrade.timeWindow()) + .setGrade(sentinelDegrade.grade()); + degradeRule.setResource(resourceName); + rules.add(degradeRule); + } + DegradeRuleManager.loadRules(rules); + } - 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); + private SentinelDegrade getSentinelDegrade(Method method) { + SentinelDegrade sentinelDegrade; + if (method.isAnnotationPresent(SentinelDegrade.class)) { + sentinelDegrade = method.getAnnotation(SentinelDegrade.class); + } else { + sentinelDegrade = retrofitInterface.getAnnotation(SentinelDegrade.class); } + return sentinelDegrade; } /** 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 deleted file mode 100644 index febad15..0000000 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/Degrade.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.lianjiatech.retrofit.spring.boot.degrade; - -import java.lang.annotation.*; - -/** - * @author 陈添明 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -@Documented -public @interface Degrade { - - /** - * RT threshold or exception ratio threshold count. - */ - double count(); - - /** - * 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/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/RetrofitDegradeRule.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java deleted file mode 100644 index 757a4d4..0000000 --- a/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/RetrofitDegradeRule.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.github.lianjiatech.retrofit.spring.boot.degrade; - -/** - * @author 陈添明 - */ -public class RetrofitDegradeRule { - - private String resourceName; - - private double count; - - private int timeWindow; - - private DegradeStrategy degradeStrategy; - - public String getResourceName() { - return resourceName; - } - - public void setResourceName(String resourceName) { - this.resourceName = resourceName; - } - - public double getCount() { - return count; - } - - public void setCount(double count) { - this.count = count; - } - - public int getTimeWindow() { - return 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 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/SentinelDegrade.java b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegrade.java new file mode 100644 index 0000000..7e87f15 --- /dev/null +++ b/src/main/java/com/github/lianjiatech/retrofit/spring/boot/degrade/SentinelDegrade.java @@ -0,0 +1,31 @@ +package com.github.lianjiatech.retrofit.spring.boot.degrade; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author 陈添明 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +@Documented +public @interface SentinelDegrade { + + /** + * RT模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 + */ + double count(); + + /** + * 熔断时长,单位为 s + */ + int timeWindow() default 5; + + /** + * 降级策略(0:平均响应时间;1:异常比例;2:异常数量) + */ + int grade() default 0; +} diff --git a/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeTest.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeTest.java new file mode 100644 index 0000000..cdb1b0b --- /dev/null +++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/DegradeTest.java @@ -0,0 +1,85 @@ +package com.github.lianjiatech.retrofit.spring.boot.test; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result; +import com.github.lianjiatech.retrofit.spring.boot.test.http.DegradeApi; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; + +/** + * @author 陈添明 + * @summary + * @since 2022/1/21 4:20 下午 + */ +@SpringBootTest(classes = RetrofitTestApplication.class) +@RunWith(SpringRunner.class) +public class DegradeTest { + + @Autowired + private DegradeApi degradeApi; + + private static final ObjectMapper objectMapper = + new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + + private MockWebServer server; + + @Before + public void before() throws IOException { + System.out.println("=========开启MockWebServer==========="); + server = new MockWebServer(); + server.start(8080); + + } + + @After + public void after() throws IOException { + System.out.println("=========关闭MockWebServer==========="); + server.close(); + } + + @Test + public void test() throws IOException { + Random random = new Random(System.currentTimeMillis()); + IntStream.range(0, 1000).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(random.nextInt(1000), TimeUnit.MILLISECONDS); + server.enqueue(response); + System.out.println(degradeApi.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/DegradeApi.java b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeApi.java new file mode 100644 index 0000000..5f8a6f7 --- /dev/null +++ b/src/test/java/com/github/lianjiatech/retrofit/spring/boot/test/http/DegradeApi.java @@ -0,0 +1,30 @@ +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.SentinelDegrade; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Person; +import com.github.lianjiatech.retrofit.spring.boot.test.entity.Result; + +import retrofit2.http.GET; +import retrofit2.http.Query; + +/** + * @author 陈添明 + * @summary + * @since 2022/1/21 4:19 下午 + */ +@RetrofitClient(baseUrl = "${test.baseUrl}") +@SentinelDegrade(count = 100) +public interface DegradeApi { + + /** + * 其他任意Java类型
+ * 将响应体内容适配成一个对应的Java类型对象返回,如果http状态码不是2xx,直接抛错!
+ * + * @param id id + * @return 其他任意Java类型 + */ + @GET("person") + Result getPerson(@Query("id") Long id); + +}