gpt4 book ai didi

如何自定义feign调用实现hystrix超时、异常熔断

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 24 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章如何自定义feign调用实现hystrix超时、异常熔断由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

需求描述

spring cloud 项目中feign 整合 hystrix经常使用,但是最近发现hystrix功能强大,但是对我们来说有些大材小用.

首先我只需要他的一个熔断作用,就是说请求超时、异常了返回 FeignClient注解中配置的fallback,不需要非阻塞操作、也不需要重试,hystrix 调用feign时候做了线程池隔离处理,这样增加了项目复杂度(线程池参数配置、线程少了请求服务直接拒绝,多了线程得管理。。。) 。

目前feign 超时之后是直接抛异常的,这样的话虽然是及时熔断了,但是正常的程序逻辑不走了配置的fallback也没有作用,这个配置项得配合 hystrix 才行.

我需要的是这样的效果 。

?
1
2
3
4
5
try {
     feign.api();
  } catch (){
  return fallback();
}

但是每个feign调用都手动加上try..catch 实在是太low了,最好能写个类似切面一样的玩意.

这时候就想到了 hystrix,既然人家框架已经做了,我直接看下代码,copy不完了么 。

源码学习

前两天发布了一篇文章也是关于feign、hystrix 调用集成的 。

基于之前的分析关键代码 。

?
1
HystrixInvocationHandler (feign.hystrix)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
   public Object invoke( final Object proxy, final Method method, final Object[] args)
       throws Throwable {
     .............
   // setterMethodMap 封装 hystrixCommand 配置信息(超时时间、是否重试.....)
     HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
       @Override
       protected Object run() throws Exception {
         ....
         HystrixInvocationHandler. this .dispatch.get(method).invoke(args);
        ....
       }
       @Override
       protected Object getFallback() {
         .........
       }
     };
    ......
     return hystrixCommand.execute();
   }

按照之前分析源码方式,直接看哪里被调用了就可以看到, hystrix 实际上自己封装了一个 feign.Builer 类名是 feign.hystrix.HystrixFeign.Builder 用的是建造者模式,生成的类是在调用服务时用到 。

看到 关键的 build() 方法 。

?
1
2
3
4
5
6
7
8
9
10
11
Feign build( final FallbackFactory<?> nullableFallbackFactory) {
       super .invocationHandlerFactory( new InvocationHandlerFactory() {
         // 重新定义一个 InvocationHandler 实现 类似 aop效果
         @Override public InvocationHandler create(Target target,
             Map<Method, MethodHandler> dispatch) {
           return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
         }
       });
       super .contract( new HystrixDelegatingContract(contract));
       return super .build();
     }

spring 动态代理我这里不多说了,核心就是 InvocationHandler (如果是jdk动态代理的话),那么 feign 这里也是,我们看看feign 调用声明是个接口,实际上是spring 动态代理生成了代理类,调用方法时实际调用的是 。

?
1
java.lang.reflect.InvocationHandler#invoke

方案构想

那么我们只需要借鉴下 hystrix 的方式,自己实现一个feign.build ,将 InvocationHandler 换成自己的, 。

然后在我们自己的 InvocationHandler 中调用feign 官方的 InvocationHandler 就行,也就是 。

?
1
feign.hystrix.HystrixInvocationHandler#invoke

这个方法中的 。

?
1
this .dispatch.get(method).invoke(args);

这个代码 。

方案具体代码实现

方案一 。

自己实现 。

?
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
import feign.Feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
/**
  * 自定义feign 构建
  * @author hgf
  */
public class CusFeignBuilder extends Feign.Builder{
     public CusFeignBuilder() {
         this .invocationHandlerFactory((target, dispatch) -> {
             Class<?> type = target.type();
             FeignClient annotation = type.getAnnotation(FeignClient. class );
             // 构造 fallback 实例
             Object fallBackObj = null ;
             if (annotation != null && !annotation.fallback().equals( void . class )) {
                 try {
                     fallBackObj = annotation.fallback().newInstance();
                 } catch (InstantiationException | IllegalAccessException e) {
                     e.printStackTrace();
                 }
             }
             return new CusFeignInvocationHandler(target, dispatch, fallBackObj);
         });
     }
}
?
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Target;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FeignClient;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.eco.common.utils.Md5Util.logger;
import static feign.Util.checkNotNull;
/**
  * 自定义的feign调用
  */
@Slf4j
public class CusFeignInvocationHandler implements InvocationHandler {
     private final Target target;
     private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
     private final Object fallbackObj;
     private final Map<String, Method> fallbackMethodMap = new ConcurrentHashMap<>();
     CusFeignInvocationHandler(Target target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch, Object  fallbackObj) {
         this .target = checkNotNull(target, "target" );
         this .dispatch = checkNotNull(dispatch, "dispatch for %s" , target);
         this .fallbackObj = fallbackObj;
     }
     public Object feignInvoke(Object proxy, Method method, Object[] args) throws Throwable {
         if ( "equals" .equals(method.getName())) {
             try {
                 Object
                         otherHandler =
                         args.length > 0 && args[ 0 ] != null ? Proxy.getInvocationHandler(args[ 0 ]) : null ;
                 return equals(otherHandler);
             } catch (IllegalArgumentException e) {
                 return false ;
             }
         } else if ( "hashCode" .equals(method.getName())) {
             return hashCode();
         } else if ( "toString" .equals(method.getName())) {
             return toString();
         }
         return dispatch.get(method).invoke(args);
     }
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof CusFeignInvocationHandler) {
             CusFeignInvocationHandler other = (CusFeignInvocationHandler) obj;
             return target.equals(other.target);
         }
         return false ;
     }
     @Override
     public int hashCode() {
         return target.hashCode();
     }
     @Override
     public String toString() {
         return target.toString();
     }
     @Override
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         try {
             return feignInvoke(proxy, method, args);
         } catch (Throwable throwable) {
             String configKey = Feign.configKey(target.type(), method);
             logger.error( "{} 请求 出现异常 ==> {}" , configKey, throwable.getMessage());
             try {
                 return getFallbackReturn(method, args, throwable);
             } catch (Throwable e) {
                 throw throwable;
             }
         }
     }
     /**
      * 反射调用 {@link FeignClient#fallback()}生成失败返回值
      * @param method            当前feign方法
      * @param args              参数
      * @param throwable         异常
      */
     public Object getFallbackReturn(Method method, Object[] args, Throwable throwable) throws Throwable {
         if (fallbackObj == null ) {
             throw new RuntimeException( "fallbackObj is null" );
         }
         String configKey = Feign.configKey(target.type(), method);
         Method fallbackMethod = fallbackMethodMap.get(configKey);
         if (fallbackMethod == null ) {
             Class<?> declaringClass = method.getDeclaringClass();
             FeignClient annotation = declaringClass.getAnnotation(FeignClient. class );
             if (annotation == null ) {
                 throw new RuntimeException( "FeignClient annotation not found" );
             }
             // 失败返回
             Class<?> fallback = annotation.fallback();
             fallbackMethod = fallback.getMethod(method.getName(), method.getParameterTypes());
             fallbackMethodMap.put(configKey, fallbackMethod);
         }
         if (fallbackMethod == null ) {
             throw new RuntimeException( "fallbackMethodMap not found" );
         }
         return fallbackMethod.invoke(fallbackObj, args);
     }
}

然后在 spring 容器中注册这个bean就行 。

?
1
2
3
4
@Bean
     CusFeignBuilder cusFeignBuilder(){
         return new CusFeignBuilder();
     }

方案二 。

集成 sentinel ,今天写博客再回头看源码时候才发现的 。

加入依赖 。

?
1
2
3
4
< dependency >
     < groupId >com.alibaba.cloud</ groupId >
     < artifactId >spring-cloud-starter-alibaba-sentinel</ artifactId >
</ dependency >

配置开启 。

?
1
feign.sentinel.enabled= true

手动实现feign 接口,将实体类注册到 spring 中 。

?
1
2
3
4
5
6
7
@Component
public class DeviceApiFallBack implements DeviceApi{
   @Override
         public ServerResponse<String> login(String appId) {
             return ServerResponse.createByErrorMessage( "请求失败" );
         }
}

其实看代码知道原理一样,无非实现方式不一样 。

两个方案其实都行,方案一自己实现代码量多,方案二sentinel 官方实现,但是需要引入依赖,增加复杂度,而且 接口实现需要注册到spring 中 。

目前我选的还是方案一,简单.

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我.

原文链接:https://blog.csdn.net/weixin_39660224/article/details/109497272 。

最后此篇关于如何自定义feign调用实现hystrix超时、异常熔断的文章就讲到这里了,如果你想了解更多关于如何自定义feign调用实现hystrix超时、异常熔断的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com