前阵子在我的知识星球中,有位朋友对我提了个问题,问我如何让Hystrix支持对接口级别的超时配置,今天给大家写篇文章,普及下Hystrix配置超时的几种方式。

至于以后你是用阿里的Sentinel还是Netflix Hystrix我就不管了,但今天的主题还是Netflix Hystrix,至少目前还是有很多在使用的,所以今天这篇文章还是看看吧。

@HystrixCommand

如果我们使用的是@HystrixCommand注解,那么可以在注解中直接指定超时时间,如下:

@HystrixCommand
(
fallbackMethod
=
"fallback"
,
    commandProperties 
=
 
{
         
@HystrixProperty
(
name 
=
 
"execution.isolation.thread.timeoutInMilliseconds"
,
 value 
=
 
"1000"
 
)
    
}
)

当然也可以指定commandKey,然后在配置文件中配置超时时间,如下:

@HystrixCommand
(
fallbackMethod
=
"fallback"
,
commandKey
=
"userGetKey"
)

配置文件给commandKey配置超时时间:

hystrix
.
command
.
userGetKey
.
execution
.
isolation
.
thread
.
timeoutInMilliseconds 
=
 
13000

全局配置

如果只是想全局的配置,可以配置默认的超时时间:

hystrix
.
command
.
default
.
execution
.
isolation
.
thread
.
timeoutInMilliseconds
=
3000
接口级别配置

假如我们的Feign Client定义如下:

@FeignClient
(
value 
=
 
"user-service"
,
 fallbackFactory 
=
 
UserRemoteClientFallbackFactory
.
class
)
public
 
interface
 
UserRemoteClient
 
{

    
@GetMapping
(
"/user/get"
)
    
public
 
ResponseData
<
UserDto
>
 getUser
(
@RequestParam
(
"id"
)
 
Long
 id
);

}

那么配置如下:

hystrix
.
command
.
UserRemoteClient
#getUser(Long).execution.isolation.thread.timeoutInMilliseconds = 300

为什么要配置成上面的方式呢?

其实就是对commandKey进行配置,只要我们知道commandKey的生成规则就可以对接口级别进行配置,接口级别的规则是 Client名称#方法名(参数类型)

源码在feign.hystrix.SetterFactory.Default中:

String
 commandKey 
=
 
Feign
.
configKey
(
target
.
type
(),
 method
);

服务级别配置

1.在Zuul中针对服务级别的话,直接配置service-id,如下:

hystrix
.
command
.
service
-
id
.
execution
.
isolation
.
thread
.
timeoutInMilliseconds
=
3000

Zuul中之所以要配置service-id原因是commandKey就是用的service-id, 通过源码分析可以得到结论。

首先进入的是RibbonRoutingFilter中的run方法,然后我们看核心的forward方法:

ClientHttpResponse
 response 
=
 forward
(
commandContext
);
在forward中有下面的代码:

RibbonCommand
 command 
=
 
this
.
ribbonCommandFactory
.
create
(
context
);

通过create可以定位到具体的实现,这边就看你用的什么Http客户端,默认有三种实现,默认定位到org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory.create(RibbonCommandContext)方法。

重点在new HttpClientRibbonCommand这行代码,第一个参数就是serviceId,我们看下HttpClientRibbonCommand构造函数的完整参数:

所以service-id就是commandKey。

2.在Feign中针对服务级别的话,需要对commandKey进行定制,可以用service-id, 也可以用Feign Client Name,如下:

@Bean
@Scope
(
"prototype"
)
@ConditionalOnMissingBean
@ConditionalOnProperty
(
name 
=
 
"feign.hystrix.enabled"
)
public
 
Feign
.
Builder
 feignHystrixBuilder
()
 
{
    
return
 
HystrixFeign
.
builder
().
setterFactory
(
new
 
SetterFactory
()
 
{

        
@Override
        
public
 
Setter
 create
(
Target
<?>
 target
,
 
Method
 method
)
 
{
            
String
 groupKey 
=
 target
.
name
();
            
String
 commandKey 
=
 
Feign
.
configKey
(
target
.
type
(),
 method
);
            
return
 
HystrixCommand
.
Setter
.
withGroupKey
(
HystrixCommandGroupKey
.
Factory
.
asKey
(
groupKey
))
                        
//.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
                        
//.andCommandKey(HystrixCommandKey.Factory.asKey(groupKey))
                        
.
andCommandKey
(
HystrixCommandKey
.
Factory
.
asKey
(
target
.
type
().
getSimpleName
()));
            
}
    
});
}
  • .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)) 默认的接口方式

  • .andCommandKey(HystrixCommandKey.Factory.asKey(groupKey)) service-id方式

  • .andCommandKey(HystrixCommandKey.Factory.asKey(target.type().getSimpleName())); Feign Client Name方式

配置的话根据不同的配置填写不通的commandKey就可以了:

hystrix
.
command
.
Feign
 
Client
 
Name
.
execution
.
isolation
.
thread
.
timeoutInMilliseconds
=
3000

留一个问题大家思考下,欢迎留言讨论:

如果我们定制commandKey,也就意味着在使用Feign调用的时候,只能支持一种超时配置,要么默认的接口级别,要么自定义的服务级别。那么有没有什么方式能够让两种同时支持呢?

PS:技术交流加我微信 jihuan900,拉你进群!

尹吉欢 我不差钱啊 钟意作者