Spring Cloud Gateway 使用 HystrixGatewayFilterFactory 熔断降级

Posted 抓手

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cloud Gateway 使用 HystrixGatewayFilterFactory 熔断降级相关的知识,希望对你有一定的参考价值。

引入maven

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

在配置文件,路由上添加HystrixGatewayFilterFactory过滤器,name为Hystrix。

spring:
  cloud:
    gateway:
      routes:
        - id: 用户服务
          uri: lb://user-server
          predicates:
            - Path=/user-server/**
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd

另外在配置文件上设置请求超时时间

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
          	# 请求超时时间5000毫秒
            timeoutInMilliseconds: 5000

请求接口,并出发降级,返回如下:


    "timestamp": "2022-08-16T01:23:11.939+0000",
    "path": "/user-server/open/a",
    "status": 504,
    "error": "Gateway Timeout",
    "message": "Response took longer than configured timeout"

使用自定义降级策略返回,配置文件修改,添加fallbackUri

spring:
  cloud:
    gateway:
      routes:
        - id: 用户服务
          uri: lb://user-server
          predicates:
            - Path=/user-server/**
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                # 使用自定义fallback
                fallbackUri: forward:/fallback

然后添加Fallback类

@RestController
public class FallbackController 

    @RequestMapping("/fallback")
    public Object fallback() 
    	// 这里可以自定义返回json内容
        return "errorjson";
    

此时降级返回:

errorjson

org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory 源码

public class HystrixGatewayFilterFactory extends AbstractGatewayFilterFactory<HystrixGatewayFilterFactory.Config> 

	public static final String FALLBACK_URI = "fallbackUri";

	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;
	

	private DispatcherHandler getDispatcherHandler() 
		if (dispatcherHandler == null) 
			dispatcherHandler = dispatcherHandlerProvider.getIfAvailable();
		

		return 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;
					HystrixRuntimeException.FailureType failureType = e.getFailureType();

					switch (failureType) 
						case TIMEOUT:
							return Mono.error(new TimeoutException());
						case COMMAND_EXCEPTION: 
							Throwable cause = e.getCause();

							/*
							 * We forsake here the null check for cause as HystrixRuntimeException will
							 * always have a cause if the failure type is COMMAND_EXCEPTION.
							 */
							if (cause instanceof ResponseStatusException || AnnotatedElementUtils
									.findMergedAnnotation(cause.getClass(), ResponseStatus.class) != null) 
								return Mono.error(cause);
							
						
						default: break;
					
				
				return Mono.error(throwable);
			).then();
		;
	

	//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)
					.scheme(null)
					.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();
			return RxReactiveStreams.toObservable(getDispatcherHandler().handle(mutated));
		
	

	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;
			return this;
		
	


以上是关于Spring Cloud Gateway 使用 HystrixGatewayFilterFactory 熔断降级的主要内容,如果未能解决你的问题,请参考以下文章

聊聊spring cloud gateway的NettyConfiguration

Spring Cloud Gateway 使用谓词检查头部授权

浅谈Spring Cloud Gateway技术

如何配置 spring-cloud-gateway 以使用 sleuth 记录请求/响应正文

Spring Cloud Gateway 远程代码执行漏洞(CVE-2022-22947)

Spring Cloud Gateway 使用 HystrixGatewayFilterFactory 熔断降级