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可以通过集成熔断器来实现熔断功能。在Spring Cloud Gateway中,可以使用Hystrix或者Resilience4j作为熔断器。 使用Hystrix实现熔断的步骤如下: 1. 引入`spring-cloud-starter-netflix-hystrix`依赖。 2. 在启动类上添加`@EnableCircuitBreaker`注解开启熔断功能。 3. 在路由配置中,通过添加`hystrix`参数来开启熔断器。例如: ``` spring: cloud: gateway: routes: - id: my_route uri: http://example.org predicates: - Path=/foo/** filters: - RewritePath=/foo/(?<segment>.*), /$\{segment} - Hystrix=myHystrixCommand ``` 4. 在配置文件中配置Hystrix的相关属性,例如: ``` hystrix: command: myHystrixCommand: execution: isolation: thread: timeoutInMilliseconds: 5000 ``` 5. 自定义熔断处理逻辑,实现`fallback`方法,处理服务降级逻辑。 使用Resilience4j实现熔断的步骤如下: 1. 引入`resilience4j-spring-boot2`依赖。 2. 在配置文件中配置Resilience4j的相关属性,例如: ``` resilience4j: circuitbreaker: configs: myCircuitBreakerConfig: failureRateThreshold: 50 waitDurationInOpenState: 5000 ringBufferSizeInClosedState: 5 instances: myCircuitBreaker: baseConfig: myCircuitBreakerConfig ``` 3. 在路由配置中,通过添加`resilience4j`参数来开启熔断器。例如: ``` spring: cloud: gateway: routes: - id: my_route uri: http://example.org predicates: - Path=/foo/** filters: - RewritePath=/foo/(?<segment>.*), /$\{segment} - Resilience4j=myCircuitBreaker ``` 4. 自定义熔断处理逻辑,实现`fallback`方法,处理服务降级逻辑。 以上是Spring Cloud Gateway实现熔断的基本步骤,具体的配置和处理逻辑可以根据实际需求进行调整和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值