HystrixGatewayFilterFactory,定位到该类,看注释得知,它需要引入spring-cloud-starter-netflix-hystrix。
/**
* Depends on `spring-cloud-starter-netflix-hystrix`,
* {@see https://cloud.spring.io/spring-cloud-netflix/}.
*
* @author Spencer Gibb
* @author Michele Mancioppi
* @author Olga Maciaszek-Sharma
*/
public class HystrixGatewayFilterFactory
extends AbstractGatewayFilterFactory<HystrixGatewayFilterFactory.Config> {
private final ObjectProvider<DispatcherHandler> dispatcherHandlerProvider;
// do not use this dispatcherHandler directly, use getDispatcherHandler() instead.
private volatile DispatcherHandler dispatcherHandler;
public HystrixGatewayFilterFactory(
ObjectProvider<DispatcherHandler> dispatcherHandlerProvider) {
super(Config.class);
this.dispatcherHandlerProvider = dispatcherHandlerProvider;
}
...后续略
引入maven depend
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
applicaiton.yml配置:
name必须要有,它是用来创造hystrixCommand的,fallbackUri必须要有forward:/
,源码详看后面,因为里面有这要求:
Assert.notNull(config.name, "A name must be supplied for the Hystrix Command Key");
public void setFallbackUri(URI fallbackUri) {
if (fallbackUri != null && !"forward".equals(fallbackUri.getScheme())) {
throw new IllegalArgumentException(
"Hystrix Filter currently only supports 'forward' URIs, found "
+ fallbackUri);
}
this.fallbackUri = fallbackUri;
}
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
order: 10000
predicates:
- Path=/user/**
filters:
- RewritePath=/user/(?<path>.*), /$\{path}
# 指定使用HystrixGatewayFilterFactory生成filter
- name: Hystrix
args:
# 指定hystrix command的名称,一般不配置setter,因为涉及线程池,没有setter所以必须设置name,不然报错
name: hystrixCmd
# 注意fallbackUri要以forward开头
fallbackUri: forward:/fallback
熔断返回示例:
@RestController
@RequestMapping("/fallback")
public class FallbackController {
@RequestMapping("")
public String fallback(){
return "error";
}
}
这里引用官网,说首选方案是放在内部这么一个应用里,直接放gateway的controller,但是,你仍然可以用一个外部应用的链接来处理,如以下配置,http://localhost:9994 就是外部的应用
Example 22. application.yml
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
源代码:
public GatewayFilter apply(Config config) {
// 1.第一次创建 filter config时,是没有这个setter字段的,它是用于构造HystrixCommand
if (config.setter == null) {
// 这里的name实际是我们在yml配置的filter的args参数中的name,比如:hystrixCmd
Assert.notNull(config.name,
"A name must be supplied for the Hystrix Command Key");
// 这里是把HystrixGatewayFilterFactory这个类名作为HystrixCommandGroup的key
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory
.asKey(getClass().getSimpleName());
// 通过我们配置的hystrix命令名作为HystrixCommand的key
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(config.name);
// groupKey和commandKey为参数来构造一个setter
config.setter = Setter.withGroupKey(groupKey).andCommandKey(commandKey);
}
// 真正开始生产熔断功能的GatewayFilter
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
return Mono.deferWithContext(context -> {
// 1.用config里的参数构造一个RouteHystrixCommand
RouteHystrixCommand command = new RouteHystrixCommand(
// 这里的createCommandSetter实际直接返回了config.setter
// 估计是打算以后扩展这个方法,加些额外的操作
createCommandSetter(config, exchange), config.fallbackUri,
exchange, chain, context);
return Mono.create(s -> {
// 2.使用Observable<HystrixCommand>订阅
// 订阅的三项内容分别是onNext、onError、onComplete
Subscription sub = command.toObservable().subscribe(s::success,
s::error, s::success);
// 3.Mono取消时,取消Observable<HystrixCommand>的订阅,结束HystrixCommand的执行
s.onCancel(sub::unsubscribe);
// 4.对异常情况的处理
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
// a.Hystrix自己的运行时异常
if (throwable instanceof HystrixRuntimeException) {
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
HystrixRuntimeException.FailureType failureType = e
.getFailureType();
// 根据异常类型的不同,返回不同的错误内容
switch (failureType) {
// I.超时
case TIMEOUT:
return Mono.error(new TimeoutException());
// II.服务不可用
case SHORTCIRCUIT:
return Mono.error(new ServiceUnavailableException());
// III.执行HystrixCommand发生异常
case COMMAND_EXCEPTION: {
Throwable cause = e.getCause();
if (cause instanceof ResponseStatusException
|| AnnotatedElementUtils.findMergedAnnotation(
cause.getClass(), ResponseStatus.class) != null) {
return Mono.error(cause);
}
}
// 这三种异常情况以外,则break,走默认的异常返回逻辑
default:break;
}
}
// b.异常的默认返回
return Mono.error(throwable);
// Mono.then()参数为空,返回空Mono,不再向后发射数据
}).then();
});
}
}
实测 ,
分权重,low是小,用简写;high是大,用全写,
注意:
- low:- Hystrix=hystrixConsumer,forward:/fallback/port
- hign:
- name: Hystrix
args:
name: hystrixConsumer
fallbackUri: forward:/fallback/port #官网写了,以forward:开头
routes: #配置路由
- id: consumer_low
uri: http://192.168.1.106:8000
predicates:
- Path=/cs/** #路径
- Weight=group1,2 #权重
filters: # 网关过滤器
- StripPrefix=1
- name: RequestRateLimiter #不能简写,因为factory没有shortcut方法,key-resolver使用自己的bean
args:
key-resolver: '#{@uriKeyResolver}'
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 5 #令牌桶容量
redis-rate-limiter.requestedTokens: 1 #每次消耗令牌个数
- Hystrix=hystrixConsumer,forward:/fallback/port
- id: consumer_high
uri: http://192.168.1.106:8001
predicates:
- name: Path
args:
patterns: /cs/**
- name: Weight
args:
weight.group: group1
weight.weight: 8
filters:
- name: StripPrefix
args:
parts: 1
- name: RequestRateLimiter #不能简写,因为factory没有shortcut方法,key-resolver使用自己的bean
args:
key-resolver: '#{@uriKeyResolver}'
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 5 #令牌桶容量
redis-rate-limiter.requestedTokens: 1 #每次消耗令牌个数
- name: Hystrix
args:
name: hystrixConsumer
fallbackUri: forward:/fallback/port #官网写了,以forward:开头
结果:
low的配置后面只有一个参数,正如源码HystrixGatewayFilterFactory这方法:
@Override
public List<String> shortcutFieldOrder() {
return singletonList(NAME_KEY);
}
所以low这种配置Hystrix=hystrixConsumer,forward:/fallback/port
是不对的,如果要fallback,一定要全配。还有就是gateway归gateway,无论跳转方是多少处理时间都无关,一旦大于gateway的默认1s超时,必然降级。熔断也是如此,但是不能用局部hystrixCommadnKey配置熔断,不起效,大概是因为service接口不是在gateway这边。
想要自定义hystrix的超时时间,按照hystrix那样在application配置文件中配置就行了
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 # 设置hystrix的超时时间,默认1s
circuitBreaker:
#在当10秒的时间内,最近20次调用请求,请求错误率超过60%,则触发熔断5秒,期间快速失败,以下都是默认值
requestVolumeThreshold: 20
errorThresholdPercentage: 50
sleepWindowInMilliseconds: 5000
#设置统计的时间窗口值的毫秒值,circuit break 的打开会根据1个rolling window的统计来计算。
#若rolling window被设为10000毫秒,则rolling window会被分成n个buckets,
#每个bucket包含success,failure,timeout,rejection的次数的统计信息。默认10000。
metrics:
rollingStats:
timeInMilliseconds: 10000
网友评论