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