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

网友投稿 266 2023-01-03

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

需求描述

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

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

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

我需要的是这样的效果

try{

feign.api();

}catch(){

return fallback();

}

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

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

源码学习

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

基于之前的分析关键代码

HystrixInvocationHandler (feign.hystrix)

@Override

public Object invoke(final Object proxy, final Method method, final Object[] args)

throws Throwable {

.............

// setterMethodMap 封装 hystrixCommand 配置信息(超时时间、是否重试.....)

HystrixCommand hystrixCommand = new HystrixCommand(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() 方法

Feign build(final FallbackFactory> nullableFallbackFactory) {

super.invocationHandlerFactory(new InvocationHandlerFactory() {

// 重新定义一个 InvocationHandler 实现 类似 aop效果

@Override public InvocationHandler create(Target target,

Map dispatch) {

return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);

}

});

super.contract(new HystrixDelegatingContract(contract));

return super.build();

}

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

java.lang.reflect.InvocationHandler#invoke

方案构想

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

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

feign.hystrix.HystrixInvocationHandler#invoke

这个方法中的

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

这个代码

方案具体代码实现

方案一

自己实现

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 dispatch;

private final Object fallbackObj;

private final Map fallbackMethodMap = new ConcurrentHashMap<>();

CusFeignInvocationHandler(Target target, Map 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();

}

方案二

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

加入依赖

com.alibaba.cloud

spring-cloud-starter-alibaba-sentinel

配置开启

feign.sentinel.enabled=true

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

@Component

public class DeviceApiFallBack implements DeviceApi{

@Override

public ServerResponse login(String appId) {

return ServerResponse.createByErrorMessage("请求失败");

}

}

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

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

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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java通过百度API实现图片车牌号识别
下一篇:太原快递物流查询单号(太原邮政快递单号查询)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~