- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有什么区别: spring-cloud-starter-openfeign ( https://github.com/spring-cloud/spring-cloud-openfeign ) 和 s
我们正在使用netflix feign来调用 Restful Web服务。对于补丁请求,看来不支持PATCH请求。 Caused by: feign.RetryableException: Inval
据我了解,当请求响应状态码!= 2xx 时,将调用 feign ErrorDecoder 的 decode() 方法。通过调试我的测试,我发现我的 CustomErrorDecoder 的 decod
我有一个像这样构建的假客户端服务: Feign.Builder builder = Feign.builder() .contract(new SpringMvcContract())
使用spring cloud feign调用我的服务,当服务返回401异常时,respose.body()一片空白。 当我抛出异常时 throw new BadRequestException(400
我使用 ErrorDecoder 返回正确的异常而不是 500 状态代码。 有没有办法在解码器中检索原始消息。我可以看到它在 FeignException 中,但不在 decode 方法中。我所拥有的
通过学习曲线,遇到了这个场景: 鉴于90%的调用都是JSON,在构建客户端时添加了GSON解码器。不过接口(interface)中有些方法调用应该是支持raw return without decod
我正在尝试使用以下 Feign 客户端在 Spring Boot 应用程序中检索在线图像内容。 @FeignClient(name = "image") public interface ImageC
我正在使用需要设置几个字段的 REST api。我的应用程序应始终将某些字段设置为相同的值。是否可以在带有 feign 定义(或其他地方)的界面中使这些值“硬编码”? 我的假声明看起来像这个例子。假设
我是新来的 Spring 和假装和探索几天以来。我能够向我们的 protected 资源(用户名/密码)发出身份验证请求,并在后续请求 header 中使用身份验证服务返回的 JWT token 。但
我尝试在我的 Spring Boot 应用程序上配置 OpenFeign,我使用 pokeapi 进行测试。 我编写了这段代码: @FeignClient(value = "pokeapi", url
快速浏览了Feign的源码,发现Feign使用的是JDK的HttpUrlConnection在不使用连接池的情况下发出 HTTP 请求并在请求完成时关闭它。我怀疑这种方式的效率。然后我看了Spring
我为 feignClients 启用了我的 spring 云,如下所示: @Configuration @EnableAutoConfiguration @RestController @Enable
当我发帖时 Map使用 Feign Client,我收到错误消息: feign.FeignException: status 400 reading MAp . 代码 //Client side @C
目录 使用HttpClient和OkHttp 使用HttpClient 使用OkHttp OpenFeign替换为OkHtt
需求 最近小编的项目中出现了很多feign 调用出现 Read Time out 的异常,但因为没有集成链路追踪的第三方框架,查不到原因。 所以想到打印请求的ip地址,判断是指定的服务器出现的问
我正在使用 spring feign 压缩请求和响应 服务器端: server: servlet: context-path: /api/v1/ compression: en
我面临以下情况,令我惊讶的是,我找不到太多文档:有一项服务仅通过一一获取项目详细信息来提供休息调用。总共有 1000 多个项目。 出于响应能力的原因,我想将这些数据保留在我的一端,而不是懒惰地获取它。
我正在尝试实现一个涉及 FeignClient 调用的单元测试,该调用应返回 404 Not Found。 由于 Feign 抛出了 404 异常,那么实现此测试用例的正确方法是什么? 我正在尝试这样
当使用spring-cloud-feign连接到本地主机时,Feign找不到url,而是给我以下错误消息。 2019-11-13 12:01:55.553 ERROR 23798 --- [n
我是一名优秀的程序员,十分优秀!