前言
哈喽,我是会写bug的要饭的。本次是在gateway中集成hystrix,就是针对不同的接口设定不同的超时时间。时间参数是从数据库抓取
一、hystrix是什么?
Hystrix 就是一个可以通过使用 延时策略 和 故障容错逻辑 帮助您管理控制这些分布式服务之间交互的一个库. Hystrix 通过 服务隔离,停止故障服务级联调用并提供应急计划来改善系统的弹性.。让整个系统拥有自我保护能力的组件。
二、基础使用
我本来想写基础使用的,但感觉有点懒,特别懒,还要调试一下,算了,网上一大把,自己搜吧
三、gateway集成hystrix
正文
3.1pom中引入hystrix的jar
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
对,就这其他的都是springboot、springcloud、gateway、redis、、、自己添加喽。
3.2 引入hystrix组件
先上代码
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.containsEncodedParts;
import java.math.BigDecimal;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import com.netflix.hystrix.HystrixObservableCommand;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import reactor.core.publisher.Mono;
import rx.Observable;
import rx.RxReactiveStreams;
import rx.Subscription;
@Component
public class SpecialHystrixGatewayFilterFactory extends AbstractGatewayFilterFactory<SpecialHystrixGatewayFilterFactory.Config> {
private static final String NAME = "SpecialHystrix";
private final ObjectProvider<DispatcherHandler> dispatcherHandler;
public SpecialHystrixGatewayFilterFactory(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();
HttpHeaders headers = request.getHeaders();
String time = headers.getFirst("TimeOut");
Integer timeout=Integer.valueOf(time).intValue();
if (timeoutMap != null) {
timeout = timeoutMap.get(path);
}
MyRouteHystrixCommand command;
if (timeout == null) {
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 = SpecialHystrixGatewayFilterFactory.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) {
String scheme = fallbackUri.getScheme();
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;
}
/** 末尾有动态传参 **/
if (key.endsWith("/")) {
key = key + "**";
}
tempTimeout.put(key, value);
}
this.timeout = tempTimeout;
return this;
}
public String wildCard(String path){
String replace = path;
String[] split = path.split("/");
if (split.length>0) {
String wildcard = split[split.length - 1];
boolean numeric = isNumeric(wildcard);
if (numeric) {
replace = path.replace(wildcard, "**");
}
}
return replace;
}
private boolean isNumeric(String str) {
String bigStr;
try {
bigStr = new BigDecimal(str).toString();
} catch (Exception e) {
return false;//异常 说明包含非数字。
}
return true;
}
}
}
3.3 yml中配置hystrix
cloud:
gateway:
discovery:
locator:
enable:true
routes:
- id: gateway-service
uri: http://jkjkjkkkk.com
predicates:
- After=2018-12-25T14:33:47.789+08:00
filters:
- name: SpecialHystrix # Hystrix Filter的名称、设置成默认的
args: # Hystrix 配置参数
name: fallbackcmd # HystrixCommand 的名字
fallbackUri: forward:/fallback # fallback对用的uri
#timeout:
#指定接口超时处理
#test-sleep: 20000
# Hystrix 配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 50000
1.“SpecialHystrix”自定义hystrix的名字,我看别人都起这个,我也叫这个,你随意。
2.yml文件中需要配置上自定义的hystrix,-name:SpecialHystrix
3.fallbackUri配置降级转发的接口。
4. #timeout:
#test-sleep: 20000
这块为指定接口的超时处理,test/sleep接口超时时间设置为20000.可以指定多个接口
5. hystrix.command.default.execution. isolation.thread.timeoutInMilliseconds: 50000
为默认配置,如果不是timeout中指定的特定接口,都将设定超时时间为50000.
6. 上述3.2中Config.class读取yml文件timeout中特定接口
7. MyRouteHystrixCommand为接口熔断设定的类,可以看
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)));*****进行了超时时间的设定。
9. SpecialHystrixGatewayFilterFactory为工厂类。
10. 工厂类中的apply()方法,可以看到MyRouteHystrixCommand的创建,如果需要从数据库中抓取该请求接口的超时时间,可以在此处传入该接口的time。
总结
白话版,如有问题请提出,我不一定改。多看源码多读书,下次见over