openfeign方法级别自定义超时时间
一、介绍
最近,因为工作原因,一直在看openfeign
相关的内容,其中就包括调研了如何支持到方法级别自定义超时时间。
全局设置的很简单
1 2 3 4 5 6
| feign: client: config: default: connect-timeout: 5000 read-timeout: 5000
|
而如果不设置,将会走默认的设置
二、代码
单条方法的话,代码其实很简单,我以前就会
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
就行了。
在调用的时候,实例化创建出来传递进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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事一大堆)
好吧,目标如下
1)代码
首先来一个注解吧,AOP
会对标注上注解的方法进行增强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface FeignOption { }
|
再来一个切面,FeignOptionAspect.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| 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;
@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(); if (ArrayUtil.isNotEmpty(types)) { int lastIndex = types.length - 1; Class<?> type = types[lastIndex]; if (Objects.equals(type, Request.Options.class)) { Object[] args = joinPoint.getArgs(); 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| package com.banmoon.utils;
import org.springframework.core.env.Environment; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@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()); }
}
|
最后,最重要的,不要忘记添加配置啊
1 2 3 4 5 6 7 8 9
| feign: connect-timeout: common: 10000 read-timeout: common: 60000 FeignTestClient: customTimeoutRetryTest: 5000
|
2)测试
代码已经可以了,我们来改造一下,其实也就是添加上注解而已
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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
进去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 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
能做的很多,得看如何进行使用。
SpringBoot使用AOP详解 | 半月无霜 (banmoon.top)
我是半月,你我一同共勉!!!