最近,因为工作原因,一直在看openfeign
相关的内容,其中就包括调研了如何支持到方法级别自定义超时时间。
全局设置的很简单
feign:
client:
config:
default:
connect-timeout: 5000
read-timeout: 5000
而如果不设置,将会走默认的设置
单条方法的话,代码其实很简单,我以前就会
package com.banmoon.feign;
import com.banmoon.constant.ServerNameConstant;
import com.banmoon.entity.UserEntity;
import com.banmoon.feign.fallback.FeignTestClientFallbackFactory;
import feign.Request;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ServerNameConstant.WEB_MQ, contextId = "FeignTestClient", fallbackFactory = FeignTestClientFallbackFactory.class)
public interface FeignTestClient {
@GetMapping("/feign/customTimeoutRetryTest")
UserEntity customTimeoutRetryTest(@RequestParam Integer id, Request.Options options);
}
在方法后面加个Request.Options options
就行了。
在调用的时候,实例化创建出来传递进去
package com.banmoon.controller;
import com.banmoon.business.obj.dto.ResultData;
import com.banmoon.entity.UserEntity;
import com.banmoon.feign.FeignTestClient;
import feign.Request;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Slf4j
@Api(tags = "feign测试模块")
@RestController
@RequestMapping("/feign")
public class FeignTestController {
@Resource
private FeignTestClient feignTestClient;
@ApiOperation("自定义超时重试测试")
@GetMapping("/customTimeoutRetryTest")
public ResultData<UserEntity> customTimeoutRetryTest(@RequestParam Integer id) {
Request.Options options = new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
UserEntity userEntity = feignTestClient.customTimeoutRetryTest(id, options);
return ResultData.success(userEntity);
}
}
AOP
进行简化是这样的,如果是一个两个还行,那万一是有一堆方法需要自定义超时时间呢?这样对代码的侵入就真的太大了。
而且,领导说了要支持可动态配置的(代码呢不写,B事一大堆)
好吧,目标如下
nacos
AOP
技术来进行实现首先来一个注解吧,AOP
会对标注上注解的方法进行增强
package com.banmoon.feign.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author banmoon
* @date 2024/04/10 18:26:28
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignOption {
}
再来一个切面,FeignOptionAspect.java
package com.banmoon.feign.aspect;
import cn.hutool.core.util.ArrayUtil;
import com.banmoon.utils.FeignOptionsUtil;
import feign.Request;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author banmoon
* @date 2024/04/10 18:24:44
*/
@Slf4j
@Aspect
@Component
public class FeignOptionAspect {
@Resource
public FeignOptionsUtil feignOptionsUtil;
@Pointcut("@annotation(com.banmoon.feign.annotations.FeignOption)")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Class<?> clazz = method.getDeclaringClass();
// 获取类名
String clazzName = clazz.getSimpleName();
// 获取方法名
String methodName = method.getName();
Class<?>[] types = method.getParameterTypes();
// 判断方法的最后一个入参是否是Options,并且实际传参是不是null
if (ArrayUtil.isNotEmpty(types)) {
int lastIndex = types.length - 1;
Class<?> type = types[lastIndex];
if (Objects.equals(type, Request.Options.class)) {
Object[] args = joinPoint.getArgs();
// 如果实际传参为null,则读取配置文件中的信息并设置进去
if (Objects.isNull(args[lastIndex])) {
Integer connectTimeout = feignOptionsUtil.getConnectTimeout(clazzName, methodName);
Integer readTimeout = feignOptionsUtil.getReadTimeout(clazzName, methodName);
args[lastIndex] = new Request.Options(connectTimeout, TimeUnit.MILLISECONDS, readTimeout, TimeUnit.MILLISECONDS, true);
return joinPoint.proceed(args);
}
}
}
return joinPoint.proceed();
}
}
还有一个工具类,FeignOptionsUtil.java
package com.banmoon.utils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author banmoon
* @date 2024/04/10 17:01:49
*/
@Component
public class FeignOptionsUtil {
@Resource
private Environment environment;
/**
* 点
*/
private static final String POINT = ".";
/**
* 连接超时前缀
*/
private static final String FEIGN_CONNECT_PREFIX = "feign.connect-timeout.";
/**
* 读取超时前缀
*/
private static final String FEIGN_READ_PREFIX = "feign.read-timeout.";
/**
* 公共连接超时
*/
private static final String COMMON_CONNECT_TIMEOUT_SUFFIX = FEIGN_CONNECT_PREFIX + "common";
/**
* 公共读取超时
*/
private static final String COMMON_READ_TIMEOUT_SUFFIX = FEIGN_READ_PREFIX + "common";
/**
* 默认连接超时时间
*/
private static final Integer DEFAULT_CONNECT_TIMEOUT = 5000;
/**
* 默认读取超时时间
*/
private static final Integer DEFAULT_READ_TIMEOUT = 60000;
public Integer getCommonConnectTimeout() {
return environment.getProperty(COMMON_CONNECT_TIMEOUT_SUFFIX, Integer.class, DEFAULT_CONNECT_TIMEOUT);
}
public Integer getCommonReadTimeout() {
return environment.getProperty(COMMON_READ_TIMEOUT_SUFFIX, Integer.class, DEFAULT_READ_TIMEOUT);
}
public Integer getConnectTimeout(String clazzName, String methods) {
return getConnectTimeout(clazzName + POINT + methods);
}
public Integer getReadTimeout(String clazzName, String methods) {
return getReadTimeout(clazzName + POINT + methods);
}
private Integer getConnectTimeout(String feignSuffix) {
return environment.getProperty(FEIGN_CONNECT_PREFIX + feignSuffix, Integer.class, getCommonConnectTimeout());
}
private Integer getReadTimeout(String feignSuffix) {
return environment.getProperty(FEIGN_READ_PREFIX + feignSuffix, Integer.class, getCommonReadTimeout());
}
}
最后,最重要的,不要忘记添加配置啊
feign:
connect-timeout:
common: 10000
read-timeout:
common: 60000
# 这里填写类名
FeignTestClient:
# 这里填写方法名
customTimeoutRetryTest: 5000
代码已经可以了,我们来改造一下,其实也就是添加上注解而已
package com.banmoon.feign;
import com.banmoon.constant.ServerNameConstant;
import com.banmoon.entity.UserEntity;
import com.banmoon.feign.annotations.FeignOption;
import com.banmoon.feign.fallback.FeignTestClientFallbackFactory;
import feign.Request;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ServerNameConstant.WEB_MQ, contextId = "FeignTestClient", fallbackFactory = FeignTestClientFallbackFactory.class)
public interface FeignTestClient {
@FeignOption
@GetMapping("/feign/customTimeoutRetryTest")
UserEntity customTimeoutRetryTest(@RequestParam Integer id, Request.Options options);
}
在调用的地方,就不需要自己手动创建一个Options
实例了,如下直接传递个null
进去
package com.banmoon.controller;
import com.banmoon.business.obj.dto.ResultData;
import com.banmoon.entity.UserEntity;
import com.banmoon.feign.FeignTestClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Slf4j
@Api(tags = "feign测试模块")
@RestController
@RequestMapping("/feign")
public class FeignTestController {
@Resource
private FeignTestClient feignTestClient;
@ApiOperation("自定义超时重试测试")
@GetMapping("/customTimeoutRetryTest")
public ResultData<UserEntity> customTimeoutRetryTest(@RequestParam Integer id) {
UserEntity userEntity = feignTestClient.customTimeoutRetryTest(id, null);
return ResultData.success(userEntity);
}
}
来请求一下,当前配置的是5000
,可以看到超时后,立刻降级返回
那么我们现在修改一下配置,配置改为2000
,来看一下
真的,AOP
能做的很多,得看如何进行使用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。