fegin实现方法级别注解超时配置

news2024/9/22 23:24:01

fegin实现方法级别注解超时配置

测试的3.18新版本已经支持方法中参数带有Options 也可以自定义配置, Options options = findOptions(argv);;

使用该注解方式需配合AOP使用!

原理是包装自己的client客户端, 替换框架的客户端!
应用到生产环境需自己充验证测试

1.0 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignTimeout {
    /**
     * 连接超时时间
     * @return
     */
    int connectTimeoutMillis() default 10000;

    /**
     * 读取超时时间
     * @return
     */
    int readTimeoutMillis() default 60000;
}

2.0 包装的客户端FeignTimeoutClient

import feign.Client;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Objects;

public class FeignTimeoutClient implements Client {

    private static final Logger log = LoggerFactory.getLogger(FeignTimeoutClient.class);

    private static final ThreadLocal<Request.Options> NOW_OPTIONS = new ThreadLocal<>();

    private final Client delegate;

    private FeignTimeoutClient(Client delegate){
        this.delegate = delegate;
    }

    public static Client wrap(Client client){
        return new FeignTimeoutClient(client);
    }

    public static void nowOptions(Request.Options options){
        NOW_OPTIONS.set(options);
    }

    public static void nowOptionsClear(){
        NOW_OPTIONS.remove();
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        // 如果未取到当前上下文的 自定义超时时间 则直接使用默认的,如果取到了则用特殊的
        Request.Options optionsNow = NOW_OPTIONS.get();
        if(Objects.nonNull(optionsNow)){
            log.info("feign命中自定义超时:url="+request.url()+";options.connectTimeoutMillis="+ optionsNow.connectTimeoutMillis()+",options.readTimeoutMillis="+optionsNow.readTimeoutMillis());
            return delegate.execute(request,optionsNow);
        }
        return delegate.execute(request,options);
    }
}

3.0 包装FeignContext

FeignTimeoutFeignContext上下文

import feign.Client;
import feign.Feign;
import lombok.SneakyThrows;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class FeignTimeoutFeignContext extends FeignContext {
    private static final Logger log = LoggerFactory.getLogger(FeignTimeoutFeignContext.class);
    private final FeignContext delegate;

    private FeignTimeoutFeignContext(FeignContext delegate) {
        this.delegate = delegate;
    }

    @Override
    public <T> T getInstance(String name, Class<T> type) {
        T instance = delegate.getInstance(name, type);
        return (T) wrapIfShould(instance);
    }

    @Override
    public <T> Map<String, T> getInstances(String name, Class<T> type) {
        Map<String, T> instances = delegate.getInstances(name, type);
        if (MapUtils.isNotEmpty(instances)) {
            Map<String, T> convertedInstances = new HashMap<>();
            for (Map.Entry<String, T> entry : instances.entrySet()) {
                convertedInstances.put(entry.getKey(), (T) wrapIfShould(entry.getValue()));
            }
            return convertedInstances;
        }

        return instances;
    }

    @SneakyThrows
    private Object wrapIfShould(Object instance) {
        if (instance instanceof Feign.Builder) {
            Field field = instance.getClass().getDeclaredField("client");
            field.setAccessible(true);
            Client client = (Client) ReflectionUtils.getField(field, instance);
            if (client instanceof FeignTimeoutClient) {
                return instance;
            }
            Client wrap = FeignTimeoutClient.wrap(client);
            ReflectionUtils.setField(field, instance, wrap);
        }
        if (instance instanceof Client && !(instance instanceof FeignTimeoutClient)) {
            Object client = instance;
            return FeignTimeoutClient.wrap((Client) client);
        }
        return instance;
    }

    public static FeignTimeoutFeignContext wrap(FeignContext context) {
        return new FeignTimeoutFeignContext(context);
    }
}

bean后置处理器进行包装

package com.zhihao.fegin;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignContext;
public class FeignTimeoutBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 对FeignContext进行包装
        if(bean instanceof FeignContext && !(bean instanceof FeignTimeoutFeignContext)){
            return FeignTimeoutFeignContext.wrap(((FeignContext) bean));
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

4.0 配置AOP注解拦截设置超时

package com.zhihao.fegin;
import feign.Request;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;
import java.util.Objects;
public class FeignTimeoutMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        try {
            // 先查找当前方法以及方法所在类上的注解
            FeignTimeout feignTimeout = AnnotationUtils.findAnnotation(invocation.getMethod(), FeignTimeout.class);
            if(Objects.nonNull(feignTimeout)){
                FeignTimeoutClient.nowOptions(new Request.Options(feignTimeout.connectTimeoutMillis(), feignTimeout.readTimeoutMillis()));
            }
            return invocation.proceed();
        } finally {
            FeignTimeoutClient.nowOptionsClear();
        }
    }
}

5.0 使用配置类加入IOC容器

@Configuration
// 如果后续有配置需要在FeignClientsConfiguration初始化前使用则需要该注解, 配合@condition使用
// @AutoConfigureBefore(FeignClientsConfiguration.class)
public class FeignConfiguration {

    @Bean
    public FeignTimeoutBeanPostProcessor feignTimeoutBeanPostProcessor(){
        return new FeignTimeoutBeanPostProcessor();
    }

    @Bean
    public DefaultPointcutAdvisor feignTimeoutPointcutAdvisor(){
        FeignTimeoutMethodInterceptor interceptor = new FeignTimeoutMethodInterceptor();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("@annotation(com.zhihao.spring.cloud.fegin.ext.timeout.FeignTimeout)");
        // 配置增强类advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(interceptor);
        return advisor;
    }
}

6.0 使用

/**
 * @Author: ZhiHao
 * @Date: 2023/8/26 14:33
 * @Description:
 * @Versions 1.0
 **/
@FeignClient(name = "feignTimeOutTest",url = "https://test-zhiha.plus")
public interface FeignTimeOutTest {

    @GetMapping("/masterPriceManager/detail")
    @FeignTimeout(connectTimeoutMillis = 500,readTimeoutMillis = 300)
    Map<String, Object> detail(@RequestParam("masterId") Long masterId);
}

7.0 结果:

pPNDPOK.png

ail")
@FeignTimeout(connectTimeoutMillis = 500,readTimeoutMillis = 300)
Map<String, Object> detail(@RequestParam(“masterId”) Long masterId);
}


7.0 结果:

[[外链图片转存中...(img-YhTVhjsr-1693034378942)]](https://imgse.com/i/pPNDPOK)



1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/931561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Banana Pi 开源社区在深圳国际电子展(2023)上展示全系列新产品

Banana Pi 开源社区在深圳国际电子展(2023)上展示全系列新产品 Banana Pi开源硬件社区是由广东比派科技主导、台湾鸿海科技&#xff08;富士康&#xff09;全面战略支持的开源硬件项目。Banana Pi开源硬件系列开发板&#xff0c;完成核心系统和架构设计。开发文档、软件、硬件…

远程调试环境

一、远程调试 1.安装vscode 2.打开vscode&#xff0c;下载插件Remote-SSH,用于远程连接 3.安装php debug 4.远程连接&#xff0c;连接到远端服务器 注&#xff1a;连接远程成功后&#xff0c;在远程依然要进行安装xdebug&#xff0c;刚才只是在vscode中进行的安装。 5.配置la…

Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required

项目场景&#xff1a; 最近因为公司业务需要在搭一个新架构&#xff0c;用的springboot3和jdk17,在整合mybatis多数据源的时候报错 &#xff08;引用的mybatisplus 和 mybatisplusjion的是最新的包-2023-08-26&#xff09; Error creating bean with name ‘XXXServiceImpl’:…

Python中的format()函数详细讲解

注&#xff1a;所以代码皆成功运行&#xff0c;可直接复制运行 一、基本使用 1、Python中的format()函数是一个格式字符串的函数&#xff0c;通过花括号{}识别替换字段&#xff0c;从而完成字符串的格式化。 #format后面放数字、字符串都可以 print("{}喜欢{}岁的{}&qu…

「Python」机器学习之线性判别分析(代码,不调包)

「Python」机器学习之线性判别分析&#xff08;代码&#xff0c;不调包&#xff09; 前言1 线性判别分析&#xff08;LDA&#xff09;2 实现2.1 LDA实现2.2 数据集示例 3 最后 前言 语言&#xff1a;python库&#xff1a;numpy, matplotlib教材参考&#xff1a;《机器学习》——…

MySql学习4:多表查询

教程来源 黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 多表关系 各个表结构之间存在各种关联关系&#xff0c;基本上分为三种&#xff1a;一对多&#xff08;多对一&#xff09;、多对多、一对一 一对多&#xff08;多对一&…

论文阅读 The Power of Tiling for Small Object Detection

The Power of Tiling for Small Object Detection Abstract 基于深度神经网络的技术在目标检测和分类方面表现出色。但这些网络在适应移动平台时可能会降低准确性&#xff0c;因为图像分辨率的增加使问题变得更加困难。在低功耗移动设备上实现实时小物体检测一直是监控应用的…

计网第四章(网络层)(四)

目录 一、IP数据报的发送和转发过程 发送&#xff1a; 1.直接交付和间接交付 如果判断源主机和目的主机是否在同一个网络中&#xff1f; 2.默认网关&#xff1a; 转发&#xff1a; 路由表&#xff1a; 一、IP数据报的发送和转发过程 发送&#xff1a; 由主机发送IP数据…

统信OS国产操作系统身份证读卡器社保卡读卡web网页开发使用操作流程

用于DONSEE系列身份证阅读器谷歌Chrome火狐Firefox插件&#xff0c;支持的型号有&#xff1a;EST-100、EST-100GS、EST-100G、EST-100U、EST-200G、EST-J13X等。 本方案无缝支持最新版本谷歌Chrome火狐Firefox等网页浏览器&#xff0c;支持H5、Vue、React、Node.js、Electron、…

Java“牵手”天猫商品销量API接口数据,天猫API接口申请指南

天猫平台商品销量接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品销量接口API是一种用于获取电商平台上商品销量数据的接口&#xff0c;通过…

mysql 命令行 执行sql文件

方法1 source source file.sql; file.sql : 绝对路径或 相对路径。 方法2 mysql -u xxx -p < file.sql 方法3 MySQLImport 工具 mysqlimport [options] database file_name 其中&#xff0c;database为要导入数据的数据库名&#xff0c;file_name为要导入的SQL文件名。还可以…

框架分析(5)-Django

框架分析&#xff08;5&#xff09;-Django 专栏介绍Django核心概念以及组件讲解模型&#xff08;Model&#xff09;视图&#xff08;View&#xff09;模板&#xff08;Template&#xff09;路由&#xff08;URLconf&#xff09;表单&#xff08;Form&#xff09;后台管理&…

【动态规划】1137. 第 N 个泰波那契数

Halo&#xff0c;这里是Ppeua。平时主要更新C&#xff0c;数据结构算法&#xff0c;Linux与ROS…感兴趣就关注我bua&#xff01; 文章目录 0. 题目解析1.算法原理1.1 状态表示1.2 状态转移方程1.3初始化1.4 填表顺序1.5 返回值 2.算法代码 &#x1f427; 本篇是整个动态规划的…

RT-Thread 时钟管理

时钟节拍 任何操作系统都需要提供一个时钟节拍&#xff0c;以供系统处理所有和时间有关的事件&#xff0c;如线程的延时、时间片的轮转调度以及定时器超时等。 RTT中&#xff0c;时钟节拍的长度可以根据RT_TICK_PER_SECOND的定义来调整。rtconfig.h配置文件中定义&#xff1a…

软件测试用例经典方法 |一文了解软件测试规范

软件测试规范是测试工作的依据和准则&#xff0c;在进行软件测试时&#xff0c;应在相关国标文件的要求和指导下完成测试工作&#xff0c;这样可以从根本上保证软件测试工作的质量&#xff0c;进而提升软件产品的质量。 一个完整的软件测试规范应该包括对规范本身的详细说明&a…

Python(.pyc)反编译:pycdc工具安装与使用

本文将介绍如何将python的.pyc文件反编译成源码&#xff0c;以便我们对源码的学习与改进。pycdc工具安装 下载地址&#xff1a; 1、Github地址&#xff1a;https://github.com/zrax/pycdc &#xff0c;下载后需要使用CMake进行编译。 2、已下载好及编译好的地址&#xff1a;ht…

Java多线程(十二)

目录 一、多线程环境使用哈希表 1.1 HashTable 1.2 ConcurrentHashTable 二、ConcurrentHashMap和Hashtable、HashMap 的区别 一、多线程环境使用哈希表 HashMap 本身就是线程不安全的&#xff0c;所以在多线程的环境下可以使用&#xff1a;HashTable、 ConcurrentHashMap 1.…

Mysql中explain执行计划信息中字段详解

Mysql中explain执行计划信息中字段详解 1. 获取执行计划2. 字段含义2.1 id2.2 select_type2.3 table2.4 partitions2.5 type2.6 possible_keys2.7 key2.8 ley_len2.9 ref2.10 rows2.11 extra 1. 获取执行计划 explain select * from t1; --或 desc select * from t1;2. 字段含…

Pandas数据分析教程-数据清洗-扩展数据类型

pandas-02-数据清洗&预处理 扩展数据类型1. 传统数据类型缺点2. 扩展的数据类型3. 如何转换类型文中用S代指Series,用Df代指DataFrame 数据清洗是处理大型复杂情况数据必不可少的步骤,这里总结一些数据清洗的常用方法:包括缺失值、重复值、异常值处理,数据类型统计,分…

23款奔驰GLE450轿跑升级原厂外观暗夜套件,战斗感满满的

升级的方案基本都是替换原来车身部位的镀铬件&#xff0c;可能会有人问&#xff1a;“难道直接用改色膜贴黑不好吗&#xff1f;”如果是贴膜的话&#xff0c;第一个是颜色没有那么纯正&#xff0c;这些镀铬件贴黑的技术难度先抛开不说&#xff0c;即使贴上去了&#xff0c;那过…