어떻게 feign 호출 을 사용자 정의 하여 Hstrix 시간 초과,이상 용 단 을 실현 합 니까?

필요 설명
spring cloud 프로젝트 에서 feign 통합 hystrix 는 자주 사용 되 지만 최근 에 hystrix 기능 이 강하 다 는 것 을 발 견 했 지만 우리 에 게 는 큰 인재 가 적 게 사용 되 고 있다.
우선 저 는 그의 용 단 작용 만 필요 합 니 다.즉,요청 시간 이 초과 되 고 FeignClient 주석 에 설 치 된 fallback 으로 돌아 가 는 것 입 니 다.차단 작업 이 필요 하지 않 고 다시 시도 할 필요 도 없습니다.Hstrix 가 feign 을 호출 할 때 스 레 드 탱크 격 리 처 리 를 했 습 니 다.이렇게 하면 프로젝트 의 복잡 도 를 증가 시 켰 습 니 다.(스 레 드 탱크 파라미터 설정,스 레 드 가 적 으 면 요청 서 비 스 를 직접 거절 할 수 있 습 니 다.스 레 드 가 많아 지면 관리 해 야 합 니 다...)
현재 feign 이 시간 을 초과 한 후에 직접 이상 을 던 집 니 다.그러면 제때에 녹 았 지만 정상 적 인 프로그램 논 리 는 설정 한 fallback 도 소 용이 없습니다.이 설정 항목 은 Hstrix 에 맞 춰 야 합 니 다.
제 가 필요 한 건 이런 효과 예요.

  try{
     feign.api();
  }catch(){
  return fallback();
 }
하지만 모든 feign 호출 에 try.catch 를 수 동 으로 추가 하 는 것 은 너무 낮 습 니 다.절단면 과 같은 것 을 쓰 는 것 이 좋 습 니 다.
이때 하 이 스 트 릭 스 가 생각 났 어 요.남 의 프레임 워 크 가 이미 만 들 어 졌 으 니 제 가 코드 를 직접 볼 게 요.copy 가 끝 이 잖 아 요.
소스 코드 학습
이틀 전에 발표 한 글 도 관련 이 있 습 니 다feign,hystrix 호출 통합.
이전 분석 키 코드 기반

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();
  }
이전 분석 소스 코드 방식 에 따라 어디 가 호출 되 었 는 지 직접 보면 알 수 있 습 니 다.하 이 스 트 릭 스 는 실제 적 으로 자신 이 feign.Builer 라 는 이름 을 feign.hystrix.Hystrix Feign.Builder 는 건축 자 모델 을 사용 하고 생 성 된 유형 은 서 비 스 를 호출 할 때 사 용 됩 니 다.
관건 적 인 build()방법 보기

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 동적 대리 여기 서 더 이상 말 하지 않 겠 습 니 다.핵심 은 Invocation Handler(jdk 동적 대리 라면)입 니 다.그러면 feign 여기 서도 feign 호출 성명 은 인터페이스 입 니 다.사실은 spring 동적 대리 가 대리 류 를 만 들 었 습 니 다.호출 방법 은 실제 호출 된 것 입 니 다.

java.lang.reflect.InvocationHandler#invoke
방안 구상
그러면 우 리 는 Hstrix 의 방식 을 참고 하여 스스로 feign.build 를 실현 하고 Invocation Handler 를 자신의 것 으로 바 꿔 야 한다.
그리고 우리 자신의 Invocation Handler 에서 feign 공식 Invocation Handler 를 호출 하면 됩 니 다.즉,

feign.hystrix.HystrixInvocationHandler#invoke
이 방법 중의

this.dispatch.get(method).invoke(args);
이 코드
프로젝트 구체 코드 구현
방안 1
자기 실현

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);
        });
    }
}

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 을 등록 하면 됩 니 다.

@Bean
    CusFeignBuilder cusFeignBuilder(){
        return new CusFeignBuilder();
    }
방안 2
sentinel 통합,오늘 블 로 그 를 쓰 고 소스 코드 를 뒤 돌아 보 니 발견 되 었 습 니 다.
가입 의존

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
설정 오픈

feign.sentinel.enabled=true
feign 인 터 페 이 스 를 수 동 으로 구현 하여,실체 클래스 를 spring 에 등록 합 니 다.

@Component
public class DeviceApiFallBack implements DeviceApi{
  @Override
        public ServerResponse<String> login(String appId) {
            return ServerResponse.createByErrorMessage("    ");
        }
}
사실 코드 를 보면 원리 가 같 고 실현 방식 이 다르다 는 것 을 안다.
두 가지 방안 은 모두 괜 찮 습 니 다.방안 은 스스로 코드 양 이 많 고 방안 2 sentinel 이 공식 적 으로 실현 되 지만 의존 도 를 도입 하고 복잡 도 를 증가 해 야 합 니 다.또한 인터페이스 실현 은 spring 에 등록 해 야 합 니 다.
현재 내 가 선택 한 것 은 역시 방안 하나,간단 하 다.
이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기