close #I6B5EU common-feign 支持接口级别重试

This commit is contained in:
lbw 2023-01-21 14:06:47 +08:00
parent 917ee2a636
commit 161d3a57f1
7 changed files with 201 additions and 50 deletions

View File

@ -16,52 +16,57 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.6.5</version>
</parent>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.6.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>pig-common-feign</artifactId>
<description>feign-sentinel服务降级熔断、限流组件</description>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>pig-common-feign</artifactId>
<description>feign-sentinel服务降级熔断、限流组件</description>
<dependencies>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--feign 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- okhttp 扩展 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<!-- LB 扩展 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--caffeine 替换LB 默认缓存实现-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--oauth server 依赖-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--feign 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- okhttp 扩展 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<!-- LB 扩展 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--caffeine 替换LB 默认缓存实现-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--oauth server 依赖-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<!--重试-->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -20,6 +20,7 @@ import com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pig4cloud.pig.common.feign.retry.FeignRetryAspect;
import com.pig4cloud.pig.common.feign.sentinel.ext.PigSentinelFeign;
import com.pig4cloud.pig.common.feign.sentinel.handle.PigUrlBlockHandler;
import com.pig4cloud.pig.common.feign.sentinel.parser.PigHeaderRequestOriginParser;
@ -61,4 +62,9 @@ public class PigFeignAutoConfiguration {
return new PigHeaderRequestOriginParser();
}
@Bean
public FeignRetryAspect feignRetryAspect() {
return new FeignRetryAspect();
}
}

View File

@ -0,0 +1,21 @@
package com.pig4cloud.pig.common.feign.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 重试具体的策略
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Backoff {
long delay() default 1000L;;
long maxDelay() default 0L;
double multiplier() default 0.0D;;
}

View File

@ -0,0 +1,21 @@
package com.pig4cloud.pig.common.feign.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 重试注解作用在 @FeignClient 注解之上
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignRetry {
Backoff backoff() default @Backoff();
int maxAttempt() default 3;
Class<? extends Throwable>[] include() default {};
}

View File

@ -0,0 +1,94 @@
package com.pig4cloud.pig.common.feign.retry;
import com.pig4cloud.pig.common.feign.annotation.FeignRetry;
import feign.RetryableException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* FeignRetry 注解切面注入 retryTemplate
*
* @author lengleng
* @date 2023/1/21
* {@link org.springframework.cloud.loadbalancer.blocking.retry.BlockingLoadBalancedRetryPolicy}.
*/
@Slf4j
@Aspect
@Component
public class FeignRetryAspect {
@Around("@annotation(feignRetry)")
public Object retry(ProceedingJoinPoint joinPoint, FeignRetry feignRetry) throws Throwable {
Method method = getCurrentMethod(joinPoint);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(prepareBackOffPolicy(feignRetry));
retryTemplate.setRetryPolicy(prepareSimpleRetryPolicy(feignRetry));
// 重试
return retryTemplate.execute(arg0 -> {
int retryCount = arg0.getRetryCount();
log.info("Sending request method: {}, max attempt: {}, delay: {}, retryCount: {}", method.getName(),
feignRetry.maxAttempt(), feignRetry.backoff().delay(), retryCount);
return joinPoint.proceed(joinPoint.getArgs());
});
}
/**
* 构造重试策略
* @param feignRetry 重试注解
* @return BackOffPolicy
*/
private BackOffPolicy prepareBackOffPolicy(FeignRetry feignRetry) {
if (feignRetry.backoff().multiplier() != 0) {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(feignRetry.backoff().delay());
backOffPolicy.setMaxInterval(feignRetry.backoff().maxDelay());
backOffPolicy.setMultiplier(feignRetry.backoff().multiplier());
return backOffPolicy;
}
else {
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(feignRetry.backoff().delay());
return fixedBackOffPolicy;
}
}
/**
* 构造重试策略
* @param feignRetry 重试注解
* @return SimpleRetryPolicy
*/
private SimpleRetryPolicy prepareSimpleRetryPolicy(FeignRetry feignRetry) {
Map<Class<? extends Throwable>, Boolean> policyMap = new HashMap<>();
policyMap.put(RetryableException.class, true); // Connection refused or time out
if (feignRetry.include().length != 0) {
for (Class<? extends Throwable> t : feignRetry.include()) {
policyMap.put(t, true);
}
}
return new SimpleRetryPolicy(feignRetry.maxAttempt(), policyMap, true);
}
private Method getCurrentMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getMethod();
}
}

View File

@ -25,12 +25,14 @@ import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.feign.annotation.FeignRetry;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
@ -38,6 +40,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import static feign.Util.checkNotNull;
@ -134,8 +137,9 @@ public class PigSentinelInvocationHandler implements InvocationHandler {
}
}
else {
// 若是R类型 执行自动降级返回R
if (R.class == method.getReturnType()) {
// 若是R类型 并且不包含@FeignRetry 执行自动降级返回R
FeignRetry feignRetry = AnnotationUtils.findAnnotation(method, FeignRetry.class);
if (R.class == method.getReturnType() && Objects.isNull(feignRetry)) {
log.error("feign 服务间调用异常", ex);
return R.failed(ex.getLocalizedMessage());
}

View File

@ -43,8 +43,8 @@
</dependency>
<!--feign 注解依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-feign</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis 依赖-->