一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 如何自定義feign調用實現hystrix超時、異常熔斷

如何自定義feign調用實現hystrix超時、異常熔斷

2021-09-13 15:55帆影匆匆 Java教程

這篇文章主要介紹了自定義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

延伸 · 閱讀

精彩推薦
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
主站蜘蛛池模板: 色老板在线| 免费又爽又黄禁片视频在线播放 | 色老板免费在线观看 | 欧美大美bbb和大白屁股 | 亚洲国产精品成人午夜在线观看 | 久久综合老色鬼网站 | 末发育xxxxx仙踪林 | 99r在线播放 | 精品久久一区 | 免费网站直接进入 | 午夜精品一区 | 日韩日韩日韩手机看片自拍 | 欧美a一片xxxx片与善交 | 国产精品亚洲精品日韩已方 | 91国内精品久久久久影院优播 | 婷婷综合亚洲 | a级亚洲片精品久久久久久久 | 美女隐私部位视频网站 | 美女被灌浣肠失禁视频 | 亚洲高清国产品国语在线观看 | 国产大片51精品免费观看 | 日本三级成人中文字幕乱码 | 惊弦45集免费看 | 三年片韩国在线观看 | 久久精品热在线观看85 | 国色天香社区视频免费观看3 | 操姓| 国产免费成人在线视频 | 包臀裙女教师波多野结衣 | 欧美日韩国产一区二区三区在线观看 | 日本人泡妞xxxxxx69 | 女攻双性 | 日韩免费高清专区 | 国产高清在线观看 | 99欧美视频 | 国产免费又粗又猛又爽视频国产 | 国产一区二区视频免费 | 236z最新伦理| 紧致肉肉高h | 亚洲图片一区二区三区 | 天选之王漫画顾长歌免费阅读 |