sequence
This article focuses on how spring Cloud Gateway integrates with Hystrix
maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Copy the code
Add the spring-cloud-starter-Netflix-hystrix dependency to enable hystrix
Configure the instance
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000
spring:
cloud:
gateway:
discovery:
locator:
enabled: trueroutes: - id: employee-service uri: lb://employee-service predicates: - Path=/employee/** filters: - RewritePath=/employee/(? <path>.*), /$\{path} - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallbackCopy the code
- First filter configuration is in the name of Hystrix filter, actual it is the corresponding HystrixGatewayFilterFactory
- Then specify the hystrix command name and fallbackUri, which starts with forward
- Finally through hystrix.com mand. Fallbackcmd. Execution. The isolation. Thread. TimeoutInMilliseconds specify the command timeout
Fallback instance
@RestController
@RequestMapping("/fallback")
public class FallbackController {
@RequestMapping("")
public String fallback() {return "error"; }}Copy the code
The source code parsing
GatewayAutoConfiguration
Spring – the cloud – gateway – core – 2.0.0. RC2 – sources. The jar! /org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java
@Configuration
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)
@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
//......
@Configuration
@ConditionalOnClass({HystrixObservableCommand.class, RxReactiveStreams.class})
protected static class HystrixConfiguration {
@Bean
public HystrixGatewayFilterFactory hystrixGatewayFilterFactory(DispatcherHandler dispatcherHandler) {
returnnew HystrixGatewayFilterFactory(dispatcherHandler); }} / /... }Copy the code
Introducing spring — cloud – starter – netflix – hystrix class libraries, have HystrixObservableCommand. Class, RxReactiveStreams. Class, And opened HystrixConfiguration
HystrixGatewayFilterFactory
Spring – the cloud – gateway – core – 2.0.0. RC2 – sources. The jar! /org/springframework/cloud/gateway/filter/factory/HystrixGatewayFilterFactory.java
/**
* Depends on `spring-cloud-starter-netflix-hystrix`, {@see http://cloud.spring.io/spring-cloud-netflix/}
* @author Spencer Gibb
*/
public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory<HystrixGatewayFilterFactory.Config> {
public static final String FALLBACK_URI = "fallbackUri";
private final DispatcherHandler dispatcherHandler;
public HystrixGatewayFilterFactory(DispatcherHandler dispatcherHandler) {
super(Config.class);
this.dispatcherHandler = dispatcherHandler;
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList(NAME_KEY);
}
public GatewayFilter apply(String routeId, Consumer<Config> consumer) {
Config config = newConfig();
consumer.accept(config);
if(StringUtils.isEmpty(config.getName()) && ! StringUtils.isEmpty(routeId)) { config.setName(routeId); }return apply(config);
}
@Override
public GatewayFilter apply(Config config) {
//TODO: if no name is supplied, generate one from command id (useful for default filter)
if (config.setter == null) {
Assert.notNull(config.name, "A name must be supplied for the Hystrix Command Key");
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName());
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(config.name);
config.setter = Setter.withGroupKey(groupKey)
.andCommandKey(commandKey);
}
return (exchange, chain) -> {
RouteHystrixCommand command = new RouteHystrixCommand(config.setter, config.fallbackUri, exchange, chain);
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;
if (e.getFailureType() == TIMEOUT) { //TODO: optionally set status
setResponseStatus(exchange, HttpStatus.GATEWAY_TIMEOUT);
returnexchange.getResponse().setComplete(); }}returnMono.error(throwable); }).then(); }; } / /... }Copy the code
RouteHystrixCommand is created, converted to Mono, and then on onErrorResume determines if HystrixRuntimeException failureType is failureType. TIMEOUT. The GATEWAY_TIMEOUT(504, “Gateway Timeout”) status code is returned.
RouteHystrixCommand
//TODO: replace with HystrixMonoCommand that we write private class RouteHystrixCommand extends HystrixObservableCommand<Void> { private final URI fallbackUri; private final ServerWebExchange exchange; private final GatewayFilterChain chain; RouteHystrixCommand(Setter setter, URI fallbackUri, ServerWebExchange exchange, GatewayFilterChain chain) { super(setter); 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 (this.fallbackUri == null) {
return super.resumeWithFallback();
}
//TODO: copied from RouteToRequestUrlFilter
URI uri = exchange.getRequest().getURI();
//TODO: assume always?
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();
returnRxReactiveStreams.toObservable(HystrixGatewayFilterFactory.this.dispatcherHandler.handle(mutated)); }}Copy the code
- Here to rewrite the construct method, RxReactiveStreams toObservable (this) chain) filter (exchange), converts the reactor Mono rxjava observables
- Here we override the resumeWithFallback method to reroute to the address of the fallbackUri in case of a fallbackUri
Config
public static class Config {
private String name;
private Setter setter;
private URI fallbackUri;
public String getName() {
return name;
}
public Config setName(String name) {
this.name = name;
return this;
}
public Config setFallbackUri(String fallbackUri) {
if(fallbackUri ! = null) {setFallbackUri(URI.create(fallbackUri));
}
return this;
}
public URI getFallbackUri() {
return fallbackUri;
}
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;
}
public Config setSetter(Setter setter) {
this.setter = setter;
returnthis; }}Copy the code
As you can see, Config validates fallbackUri. If it is not null, it must start with forward
summary
Spring Cloud Gateway integrates hystrix into the following steps:
- Add spring-cloud-starter-Netflix-Hystrix dependencies
- Add the filter whose name is Hystrix to the filter of the route, specify the Hystrix command name, and the fallbackUri(
optional
) - Specify hystrix command timeout, etc.
doc
- 112.4 Hystrix GatewayFilter Factory