LianjiaTech / retrofit-spring-boot-starter

A spring-boot starter for retrofit, supports rapid integration and feature enhancements.(适用于retrofit的spring-boot-starter,支持快速集成和功能增强)
Apache License 2.0
1.78k stars 340 forks source link

@SentinelDegrade注解不生效 #135

Closed shelltea closed 2 years ago

shelltea commented 2 years ago
  1. 增加Sentinel依赖和配置,启动熔断降级功能;
        <dependency>
            <groupId>com.github.lianjiatech</groupId>
            <artifactId>retrofit-spring-boot-starter</artifactId>
            <version>2.3.9</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>1.8.3</version>
        </dependency>
retrofit.degrade.degrade-type = sentinel
  1. 在接口上配置@SentinelDegrade注解,异常数超过1触发降级,如下:
    
    @SentinelDegrade(grade = 2, count = 1)
    @RetrofitClient(baseUrl = "https://api.com", connectTimeoutMs = 3000,
        fallback = ApiFallback.class, fallbackFactory = ApiFallbackFactory.class)
    public interface Api {

}


3. 在单元测试中循环调用此接口的方法,通过抛出`java.net.ConnectException`异常模拟调不通的情况,未触发ApiFallback中的代码,在Sentinel的`${user_home}/logs/csp`日志目录中发现异常未计数:
<img width="618" alt="image" src="https://user-images.githubusercontent.com/864375/202384973-636bcd42-da4f-4849-b1fc-60aab9243702.png">

日志的第7个数值为异常数
<img width="794" alt="image" src="https://user-images.githubusercontent.com/864375/202385225-bc65aece-e726-4f2e-a0f1-c25817ec50c8.png">

4. 通过分析代码发现`SentinelRetrofitDegrade`类中未通过`Tracer`对异常进行统计:
```java
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
        SentinelDegrade sentinelDegrade = AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(),
                SentinelDegrade.class);
        if (!needDegrade(sentinelDegrade)) {
            return chain.proceed(request);
        }
        String resourceName = parseResourceName(method);
        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();
            }
        }
    }
image
  1. 在Sentinel提供的sentinel-okhttp-adapter中的代码,包含了Tracer记录的代码: https://github.com/alibaba/Sentinel/blob/master/sentinel-adapter/sentinel-okhttp-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/okhttp/SentinelOkHttpInterceptor.java image
shelltea commented 2 years ago

在对SentinelRetrofitDegrade类进行了以下修改后,可以正常记录异常数,并触发熔断:

@Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Method method = Objects.requireNonNull(request.tag(Invocation.class)).method();
        SentinelDegrade sentinelDegrade = AnnotationExtendUtils.findMergedAnnotation(method, method.getDeclaringClass(),
                SentinelDegrade.class);
        if (!needDegrade(sentinelDegrade)) {
            return chain.proceed(request);
        }
        String resourceName = parseResourceName(method);
        Entry entry = null;
        try {
            entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
            return chain.proceed(request);
        } catch (BlockException e) {
            throw new RetrofitBlockException(e);
        } catch (IOException | RetrofitException ex) {
            Tracer.traceEntry(ex, entry);
            throw ex;
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }
image

5次异常,995次拦截。

通过上述的改动,可以使@Retry@SentinelDegrade组合使用,例如:

@SentinelDegrade(grade = 2, count = 1)
@RetrofitClient(baseUrl = "https://api.com", connectTimeoutMs = 3000,
        fallback = Fallback.class, fallbackFactory = FallbackFactory.class)
public interface TexSmartApi {
    @Retry(intervalMs = 20)
    @POST("api")
    Response api(@Body Request request);
}

需要在方法调用地方对Retry Failed的RetrofitException进行捕获。

可以先进行2次重试,然后抛出Retry Failed的RetrofitException,一共进行15次请求(minRequestAmount = 5)尝试后触发降级。

chentianming11 commented 2 years ago

厉害了,分析的很深入。我在SentinelRetrofitDegrade加上Tracer,再发一个版本。

chentianming11 commented 2 years ago

感觉对于非BlockException,都需要trace

image

2.3.10版本已修复

shelltea commented 2 years ago

@chentianming11 升级到了2.3.10,测试可以熔断。👍