欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何自定義feign調(diào)用實(shí)現(xiàn)hystrix超時、異常熔斷

 更新時間:2021年06月24日 11:48:46   作者:帆影匆匆  
這篇文章主要介紹了自定義feign調(diào)用實(shí)現(xiàn)hystrix超時、異常熔斷的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

需求描述

spring cloud 項(xiàng)目中feign 整合 hystrix經(jīng)常使用,但是最近發(fā)現(xiàn)hystrix功能強(qiáng)大,但是對我們來說有些大材小用。

首先我只需要他的一個熔斷作用,就是說請求超時、異常了返回 FeignClient注解中配置的fallback,不需要非阻塞操作、也不需要重試,hystrix 調(diào)用feign時候做了線程池隔離處理,這樣增加了項(xiàng)目復(fù)雜度(線程池參數(shù)配置、線程少了請求服務(wù)直接拒絕,多了線程得管理。。。)

目前feign 超時之后是直接拋異常的,這樣的話雖然是及時熔斷了,但是正常的程序邏輯不走了配置的fallback也沒有作用,這個配置項(xiàng)得配合 hystrix 才行。

我需要的是這樣的效果

  try{
     feign.api();
  }catch(){
  return fallback();
 }

但是每個feign調(diào)用都手動加上try..catch 實(shí)在是太low了,最好能寫個類似切面一樣的玩意。

這時候就想到了 hystrix,既然人家框架已經(jīng)做了,我直接看下代碼,copy不完了么

源碼學(xué)習(xí)

前兩天發(fā)布了一篇文章也是關(guān)于feign、hystrix 調(diào)用集成的

基于之前的分析關(guān)鍵代碼

HystrixInvocationHandler (feign.hystrix)
@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();
  }

按照之前分析源碼方式,直接看哪里被調(diào)用了就可以看到, hystrix 實(shí)際上自己封裝了一個 feign.Builer 類名是 feign.hystrix.HystrixFeign.Builder 用的是建造者模式,生成的類是在調(diào)用服務(wù)時用到

看到 關(guān)鍵的 build() 方法

Feign build(final FallbackFactory<?> nullableFallbackFactory) {
      super.invocationHandlerFactory(new InvocationHandlerFactory() {
        // 重新定義一個 InvocationHandler 實(shí)現(xiàn) 類似 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 動態(tài)代理我這里不多說了,核心就是 InvocationHandler (如果是jdk動態(tài)代理的話),那么 feign 這里也是,我們看看feign 調(diào)用聲明是個接口,實(shí)際上是spring 動態(tài)代理生成了代理類,調(diào)用方法時實(shí)際調(diào)用的是

java.lang.reflect.InvocationHandler#invoke

方案構(gòu)想

那么我們只需要借鑒下 hystrix 的方式,自己實(shí)現(xiàn)一個feign.build ,將 InvocationHandler 換成自己的,

然后在我們自己的 InvocationHandler 中調(diào)用feign 官方的 InvocationHandler 就行,也就是

feign.hystrix.HystrixInvocationHandler#invoke

這個方法中的

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

這個代碼

方案具體代碼實(shí)現(xiàn)

方案一

自己實(shí)現(xiàn)

import feign.Feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
/**
 * 自定義feign 構(gòu)建
 * @author hgf
 */
public class CusFeignBuilder extends Feign.Builder{
    public CusFeignBuilder() {
        this.invocationHandlerFactory((target, dispatch) -> {
            Class<?> type = target.type();
            FeignClient annotation = type.getAnnotation(FeignClient.class);
            // 構(gòu)造 fallback 實(shí)例
            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);
        });
    }
}
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調(diào)用
 */
@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("{} 請求 出現(xiàn)異常 ==> {}", configKey, throwable.getMessage());
            try {
                return getFallbackReturn(method, args, throwable);
            } catch (Throwable e) {
                throw throwable;
            }
        }
    }
    /**
     * 反射調(diào)用 {@link FeignClient#fallback()}生成失敗返回值
     * @param method            當(dāng)前feign方法
     * @param args              參數(shù)
     * @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就行

@Bean
    CusFeignBuilder cusFeignBuilder(){
        return new CusFeignBuilder();
    }

方案二

集成 sentinel ,今天寫博客再回頭看源碼時候才發(fā)現(xiàn)的

加入依賴

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置開啟

feign.sentinel.enabled=true

手動實(shí)現(xiàn)feign 接口,將實(shí)體類注冊到 spring 中

@Component
public class DeviceApiFallBack implements DeviceApi{
  @Override
        public ServerResponse<String> login(String appId) {
            return ServerResponse.createByErrorMessage("請求失敗");
        }
}

其實(shí)看代碼知道原理一樣,無非實(shí)現(xiàn)方式不一樣

兩個方案其實(shí)都行,方案一自己實(shí)現(xiàn)代碼量多,方案二sentinel 官方實(shí)現(xiàn),但是需要引入依賴,增加復(fù)雜度,而且 接口實(shí)現(xiàn)需要注冊到spring 中

目前我選的還是方案一,簡單。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一文帶你認(rèn)識Java中的Object類和深淺拷貝

    一文帶你認(rèn)識Java中的Object類和深淺拷貝

    任何變成語言中,其實(shí)都有淺拷貝和深拷貝的概念,Java 中也不例外,下面這篇文章主要給大家介紹了關(guān)于Java中Object類和深淺拷貝的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • Java泛型 <T> T、 T、<T>的用法小結(jié)

    Java泛型 <T> T、 T、<T>的用法小結(jié)

    T在Java泛型中,被稱作類型變量, 有的方法返回值是<T> T,有的是T,區(qū)別在哪里,本文主要介紹了Java泛型 <T> T、 T、<T>的用法小結(jié),具有一定的參考價值,感興趣的可以了解下
    2023-12-12
  • SpringBoot自定義轉(zhuǎn)換器用法詳解

    SpringBoot自定義轉(zhuǎn)換器用法詳解

    在SpringBoot中,轉(zhuǎn)換器用于將一個類型的值轉(zhuǎn)換為另一個類型,這在處理HTTP請求參數(shù)、響應(yīng)結(jié)果、表單數(shù)據(jù)等方面非常有用,SpringBoot提供了多種方式來定義和使用轉(zhuǎn)換器,本文給大家介紹了
    如何使用SpringBoot自定義轉(zhuǎn)換器,需要的朋友可以參考下
    2023-08-08
  • 如何通過idea給web項(xiàng)目打war包

    如何通過idea給web項(xiàng)目打war包

    這篇文章主要介紹了如何通過idea給web項(xiàng)目打war包問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • spring整合Quartz框架過程詳解

    spring整合Quartz框架過程詳解

    這篇文章主要介紹了spring整合Quartz框架過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • 被遺忘的Java關(guān)鍵字transient的使用詳解

    被遺忘的Java關(guān)鍵字transient的使用詳解

    在 Java 中,transient 是一個關(guān)鍵字,用于指定一個類的字段(成員變量)在序列化時應(yīng)該被忽略。本文將通過示例為大家簡單講講transient的使用,需要的可以參考一下
    2023-04-04
  • Java實(shí)現(xiàn)打飛機(jī)小游戲(附完整源碼)

    Java實(shí)現(xiàn)打飛機(jī)小游戲(附完整源碼)

    這篇文章主要介紹了Java實(shí)現(xiàn)打飛機(jī)小游戲(附完整源碼),這里整理了詳細(xì)的代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • java構(gòu)造函數(shù)的三種類型總結(jié)

    java構(gòu)造函數(shù)的三種類型總結(jié)

    在本篇文章里小編給大家整理了一篇關(guān)于java構(gòu)造函數(shù)的三種類型總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)參考下。
    2021-01-01
  • Java在PDF中添加表格過程詳解

    Java在PDF中添加表格過程詳解

    這篇文章主要介紹了Java在PDF中添加表格過程詳解,本文將介紹通過Java編程在PDF文檔中添加表格的方法。添加表格時,可設(shè)置表格邊框、單元格對齊方式、單元格背景色、單元格合并、插入圖片、設(shè)置行高、列寬、字體、字號等,需要的朋友可以參考下
    2019-07-07
  • Java中如何將String轉(zhuǎn)JSONObject

    Java中如何將String轉(zhuǎn)JSONObject

    這篇文章主要介紹了Java中如何將String轉(zhuǎn)JSONObject,String類型轉(zhuǎn)JSONObject,下面有兩種方式可以進(jìn)行轉(zhuǎn)換,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05

最新評論