Feign和restTemplate 单个接口超时拦截处理
文章目录
- 1. Feign超时
- 2. restTemplate超时(全局)
- 3. restTemplate超时(指定接口,用注解实现切面拦截)
以5s超时为例。超时的接口数据,就不要了,默认为空
1. Feign超时
feign:
client:
config:
default: #default默认为所有feign的超时配置如下,可以改为具体的feign服务名
connectTimeout: 1000 #单位毫秒
readTimeout: 1000 #单位毫秒
2. restTemplate超时(全局)
重写以下内容
@Configuration
public class HttpConfig {
@Bean
public RestTemplate restTemplate() {
//设置超时时间
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectionRequestTimeout(10000);
/** 连接超时参数 ConnectTimeout,让用户配置建连阶段的最长等待时间 **/
httpRequestFactory.setConnectTimeout(10000);
/** 读取超时参数 ReadTimeout,用来控制从 Socket 上读取数据的最长等待时间 5s**/
httpRequestFactory.setReadTimeout(5000);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
//设置UTF-8 编码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
restTemplate.getInterceptors().add(new LoggingClientHttpRequestInterceptor());
return restTemplate;
}
}
3. restTemplate超时(指定接口,用注解实现切面拦截)
note:
当全局和指定接口的restTemplate都有时,有以下几种情况(全局超时时间:T1,指定接口超时时间:T2)
1)T1 ≥ T2:接口到达T2就会抛出
2)T1 < T2:接口到达T1就会抛出
1.定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TargetHttpTimeout {
/**
* 读取超时时间,默认:-1
*/
int timeout() default -1;
}
2.切面拦截
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import static org.springframework.http.HttpStatus.OK;
/**
* @author huangying03
* @date 2022/12/21 19:14
*/
@Slf4j
@Aspect
@Component
public class TargetHttpTimeoutAspect {
@Around("@annotation(TargetHttpTimeout)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
TargetHttpTimeout targetHttpTimeout = methodSignature.getMethod().getAnnotation(TargetHttpTimeout.class);
// 成员内部类
final Object[] result = {null};
class CallableThread implements Callable<String> {
@Override
public String call() {
try {
long start = System.currentTimeMillis();
result[0] = joinPoint.proceed();
long end = System.currentTimeMillis();
log.info("[TargetHttpTimeout joinPoint.proceed]耗时:{}", end - start);
} catch (Throwable e) {
log.info("[TargetHttpTimeout joinPoint.proceed]异常:", e);
}
return OK.name();
}
}
//region 开始事件
Callable<String> callableThread = new CallableThread();
FutureTask<String> task = new FutureTask<>(callableThread);
// 开启线程
new Thread(task).start();
try {
// 如果 targetHttpTimeout.readTimeout() ms没有返回值就 抛出异常
task.get(targetHttpTimeout.readTimeout(), TimeUnit.MILLISECONDS);
} catch (Exception e) {
log.info("[TargetHttpTimeout joinPoint.proceed]超时:", e);
}
//endregion 结束事件
return result[0];
}
}
3.注解使用 java实现
@TargetHttpTimeout(timeout = 200) //通过修改此处timeout时间来实现接口获取数据超时问题
@TargetHttpTimeout(timeout = 200) //通过修改此处时间来实现读取超时问题
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(value = 500))
@Override
public ResponseEntity<String> getData(List<Long> projectIdList) {
String projectIds= StringUtil.listToString(projectIdList, ",");
String url = valueConfig.getOpenapiData() + "?projectIds={projectIds}";
Map<String, Object> params = new HashMap<>();
params.put("projectIds", projectIds);
/** GET 请求参数不需要带着content-type **/
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> httpEntity = new HttpEntity<>(null, headers);
ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, params);
return exchange;
}