spring cloud gateway 指定接口熔断时间
本文主要解决spring cloud gateway不可以针对不同的接口进行熔断时间的设置使用spring cloud gateway后,有了熔断,问题也就随之而来,服务间调用有了hystrix可以及时的排除坏接口、坏服务的问题,对系统很有帮助。但是!不是所有的接口都是极短时间内完成的,不是所有的接口都可以设置一样的超时时间的!我们实际使用时,总有一些接口(需要交互,需要通讯,需要。...
本文主要解决spring cloud gateway不可以针对不同的接口进行熔断时间的设置
使用spring cloud gateway后,有了熔断,问题也就随之而来,服务间调用有了hystrix可以及时的排除坏接口、坏服务的问题,对系统很有帮助。但是!不是所有的接口都是极短时间内完成的,不是所有的接口都可以设置一样的超时时间的!
我们实际使用时,总有一些接口(需要交互,需要通讯,需要。。。尤其是物联网行业)
那么我们面临一个问题,那就是百分之99的接口都可以在1s内完美完成,但是就是那几个特殊接口,需要十几秒,几十秒的等待时间,而默认熔断的时间又只有一个。
前几天,查阅种种资料,问遍身边大神,都没有找到一个很好的解决办法,最后决定看源码,自己写!!!
废话不多说
思路:
定义一个自己的熔断策略,根据配置,设置不同的接口的熔断时间
想起来简单,但是真正找源码的时候需要一点时间!
贴上改完的代码:
/**
* Copyright 2013-2017 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Apache(源码修改 HystrixGatewayFilterFactory)
* @ClassName MyHystrixGatewayFilterFactory
* @Description 自定义的HystrixGatewayFilterFactory主要用来调整特殊接口的容断时间
* @Modify TY
* @Date 17:20 2019-07-11
* @Version 1.0
**/
@Component
public class MyHystrixGatewayFilterFactory extends AbstractGatewayFilterFactory<MyHystrixGatewayFilterFactory.Config> {
private static final String NAME = "MyHystrix";
private final ObjectProvider<DispatcherHandler> dispatcherHandler;
public MyHystrixGatewayFilterFactory(ObjectProvider<DispatcherHandler> dispatcherHandler) {
super(Config.class);
this.dispatcherHandler = dispatcherHandler;
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList(NAME_KEY);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
Map<String, Integer> timeoutMap = config.getTimeout();
Integer timeout = null;
if (timeoutMap != null) {
timeout = timeoutMap.get(path);
}
MyRouteHystrixCommand command;
if (timeout == null) {
//没有定义时间的接口将使用配置的default时间
command = new MyRouteHystrixCommand(config.getFallbackUri(), exchange, chain, path);
} else {
//有配置时间的接口将使用配置的时间
command = new MyRouteHystrixCommand(config.getFallbackUri(), exchange, chain, timeout, path);
}
return Mono.create(s -> {
Subscription sub = command.toObservable().subscribe(s::success, s::error, s::success);
s.onCancel(sub::unsubscribe);
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
if (throwable instanceof HystrixRuntimeException) {
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
HystrixRuntimeException.FailureType failureType = e.getFailureType();
switch (failureType) {
case TIMEOUT:
return Mono.error(new TimeoutException());
case COMMAND_EXCEPTION: {
Throwable cause = e.getCause();
if (cause instanceof ResponseStatusException || AnnotatedElementUtils
.findMergedAnnotation(cause.getClass(), ResponseStatus.class) != null) {
return Mono.error(cause);
}
}
default:
break;
}
}
return Mono.error(throwable);
}).then();
};
}
@Override
public String name() {
return NAME;
}
private class MyRouteHystrixCommand extends HystrixObservableCommand<Void> {
private final URI fallbackUri;
private final ServerWebExchange exchange;
private final GatewayFilterChain chain;
public MyRouteHystrixCommand(URI fallbackUri, ServerWebExchange exchange, GatewayFilterChain chain,
String key) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(key))
.andCommandKey(HystrixCommandKey.Factory.asKey(key)));
this.fallbackUri = fallbackUri;
this.exchange = exchange;
this.chain = chain;
}
public MyRouteHystrixCommand(URI fallbackUri, ServerWebExchange exchange, GatewayFilterChain chain,
int timeout,
String key) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(key))
.andCommandKey(HystrixCommandKey.Factory.asKey(key))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeout)));
this.fallbackUri = fallbackUri;
this.exchange = exchange;
this.chain = chain;
}
@Override
protected Observable<Void> construct() {
return RxReactiveStreams.toObservable(this.chain.filter(exchange));
}
@Override
protected Observable<Void> resumeWithFallback() {
if (null == fallbackUri) {
return super.resumeWithFallback();
}
URI uri = exchange.getRequest().getURI();
boolean encoded = containsEncodedParts(uri);
URI requestUrl = UriComponentsBuilder.fromUri(uri)
.host(null)
.port(null)
.uri(this.fallbackUri)
.build(encoded)
.toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
ServerHttpRequest request = this.exchange.getRequest().mutate().uri(requestUrl).build();
ServerWebExchange mutated = exchange.mutate().request(request).build();
DispatcherHandler dispatcherHandler = MyHystrixGatewayFilterFactory.this.dispatcherHandler.getIfAvailable();
return RxReactiveStreams.toObservable(dispatcherHandler.handle(mutated));
}
}
public static class Config {
private String id;
private URI fallbackUri;
/**
* url -> timeout ms
*/
private Map<String, Integer> timeout;
public String getId() {
return id;
}
public Config setId(String id) {
this.id = id;
return this;
}
public URI getFallbackUri() {
return fallbackUri;
}
public Config 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;
return this;
}
public Map<String, Integer> getTimeout() {
return timeout;
}
public Config setTimeout(Map<String, Integer> timeout) {
//YAML解析的时候MAP的KEY不支持'/',这里只能用'-'替代
Map<String, Integer> tempTimeout = new HashMap<>(timeout.size());
for (String key : timeout.keySet()) {
Integer value = timeout.get(key);
key = key.replace("-", "/");
if (!key.startsWith("/")) {
key = "/" + key;
}
tempTimeout.put(key, value);
}
this.timeout = tempTimeout;
return this;
}
}
}
有个小插曲,配置不支持接口的“/”我试了几十种办法,最后妥协了,使用“-”代替了“/”
配置文件
spring:
cloud:
#网关
gateway:
default-filters:
routes:
- id: demo-service
#服务的application名称
uri: lb://demo-service
#路由级别
order: 0
predicates:
#前缀
- Path=/demo/**
filters:
- name: MyHystrix
args:
id: MyHystrix
fallbackUri: forward:/fallback
timeout:
# 这里暂时用-分隔URL,因为/不支持
demo-control-down_control: 11000
demo-control-down_up: 11000
#设置hystrix的熔断时间
hystrix:
command:
default:
execution:
isolation:
thread:
#设置API网关中路由转发请求的HystrixCommand执行超时时间
timeoutInMilliseconds: 5000
逻辑很简单,就是替换源码的处理方式,但是,需要花一定时间,去跟踪,希望拿去用的童鞋可以点个赞!哈哈
大部分是源码,可以自行对比,少部分有改动,不放心我的改动可以看代码。
更多推荐
所有评论(0)