前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >openfeign方法级别自定义超时时间

openfeign方法级别自定义超时时间

原创
作者头像
半月无霜
发布2024-07-26 09:54:39
1590
发布2024-07-26 09:54:39
举报
文章被收录于专栏:半月无霜

openfeign方法级别自定义超时时间

一、介绍

最近,因为工作原因,一直在看openfeign相关的内容,其中就包括调研了如何支持到方法级别自定义超时时间。

全局设置的很简单

代码语言:yaml
复制
feign:
  client:
    config:
      default:
        connect-timeout: 5000
        read-timeout: 5000

而如果不设置,将会走默认的设置

二、代码

单条方法的话,代码其实很简单,我以前就会

代码语言:java
复制
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就行了。

在调用的时候,实例化创建出来传递进去

代码语言:java
复制
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技术来进行实现

1)代码

首先来一个注解吧,AOP会对标注上注解的方法进行增强

代码语言:java
复制
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

代码语言: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

代码语言: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());
    }

}

最后,最重要的,不要忘记添加配置啊

代码语言:java
复制
feign:
  connect-timeout:
    common: 10000
  read-timeout:
    common: 60000
    # 这里填写类名
    FeignTestClient:
      # 这里填写方法名
      customTimeoutRetryTest: 5000

2)测试

代码已经可以了,我们来改造一下,其实也就是添加上注解而已

代码语言:java
复制
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进去

代码语言:java
复制
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)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • openfeign方法级别自定义超时时间
    • 一、介绍
      • 二、代码
        • 三、使用AOP进行简化
          • 1)代码
          • 2)测试
        • 四、最后
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档