自定义springcloud-gateway熔断处理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义springcloud-gateway熔断处理相关的知识,希望对你有一定的参考价值。
参考技术A 一、场景使用spring cloud gateway后,有了熔断,问题也就随之而来,服务间调用有了hystrix可以及时的排除坏接口、坏服务的问题,对系统很有帮助。但是!不是所有的接口都是极短时间内完成的,不是所有的接口都可以设置一样的超时时间的!
那么我们面临一个问题,那就是百分之99的接口都可以在1s内完美完成,但是就是那几个特殊接口,需要十几秒,几十秒的等待时间,而默认熔断的时间又只有一个。
二、分析
在前面 springcloudgateway源码解析之请求篇 中我们知道请求会经过一些列的过滤器(GatewayFilter),而springcloudgateway的降级熔断处理就是由一个特殊的过滤器来处理的,通过源码分析我们关注到HystrixGatewayFilterFactory这个类,这个类的作用就是生产GatewayFilter用的,我们看下它的实现
可以看到红框处最后构建了一个匿名的GatewayFilter对象返回,这个对象在接口请求过程中会被加载到过滤器链条中,仔细看到 这里是创建了一个RouteHystrixCommand这个命令对象,最终调用command.toObservable()方法处理请求,如果超时熔断调用resumeWithFallback方法
通过源码分析 gateway在路由时可以指定HystrixCommandKey,并且对HystrixCommandKey设置超时时间
三、方案
知道网关熔断的原理就好办了,自定义熔断的过滤器配置到接口请求过程中,由过滤器来读取接口熔断配置并构建HystrixObservableCommand处理请求。
自定义一个类XXXGatewayFilterFactory继承AbstractGatewayFilterFactory,将api和对应的timeout配置化,来实现细化到具体接口的熔断配置,具体实现如下:
package org.unicorn.framework.gateway.filter;
import cn.hutool.core.collection.CollectionUtil;
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 org.springframework.beans.factory.ObjectProvider;
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.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
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 reactor.core.publisher.Mono;
import rx.Observable;
import rx.RxReactiveStreams;
import rx.Subscription;
import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
/**
* @Author: xiebin
* @Description:
* @Date:Create:in 2022-07-06 9:17
*/
@Component
public class UnicornHystrixGatewayFilterFactoryextends AbstractGatewayFilterFactory
private static final StringNAME ="unicornHystrix";
private ObjectProviderdispatcherHandlerProvider;
private AntPathMatcherantPathMatcher;
public UnicornHystrixGatewayFilterFactory(ObjectProvider dispatcherHandlerProvider)
super(Config.class);
this.dispatcherHandlerProvider = dispatcherHandlerProvider;
this.antPathMatcher =new AntPathMatcher();
@Override
public ListshortcutFieldOrder()
return Collections.singletonList(NAME_KEY);
/**
* 获取服务ID
*
* @param exchange
* @return
*/
public StringserviceId(ServerWebExchange exchange)
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
try
path = path.split("/")[1];
return path;
catch (Exception e)
return "default";
/**
* @param key
* @param timeout
* @return
*/
private HystrixObservableCommand.SetterinitSetter(String key, Integer timeout)
HystrixObservableCommand.Setter setter = HystrixObservableCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(key)).andCommandKey(HystrixCommandKey.Factory.asKey(key));
if (timeout !=null)
setter.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(timeout));
return setter;
/**
* @param exchange
* @param chain
* @param config
* @return
*/
private UnicornRouteHystrixCommandinitUnicornRouteHystrixCommand(ServerWebExchange exchange, GatewayFilterChain chain, Config config)
//路由配置的超时设置
List apiTimeoutList = config.getTimeouts();
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
UnicornRouteHystrixCommand command;
if (CollectionUtil.isNotEmpty(apiTimeoutList))
//request匹配属于那种模式
ApiHystrixTimeout apiHystrixTimeout = getApiHystrixTimeout(apiTimeoutList, path);
command =new UnicornRouteHystrixCommand(config.getFallbackUri(), exchange, chain, initSetter(apiHystrixTimeout.getApiPattern(), apiHystrixTimeout.getTimeout()));
else
command =new UnicornRouteHystrixCommand(config.getFallbackUri(), exchange, chain, initSetter(serviceId(exchange), null));
return command;
/**
* @param apiTimeoutList
* @param path
* @return
*/
private ApiHystrixTimeoutgetApiHystrixTimeout(List apiTimeoutList, String path)
for (ApiHystrixTimeout apiTimeoutPattern : apiTimeoutList)
if (this.antPathMatcher.match(apiTimeoutPattern.getApiPattern(), path))
return apiTimeoutPattern;
ApiHystrixTimeout apiHystrixTimeout =new ApiHystrixTimeout();
apiHystrixTimeout.setApiPattern("default");
apiHystrixTimeout.timeout =null;
return apiHystrixTimeout;
@Override
public GatewayFilterapply(Config config)
return (exchange, chain) ->
UnicornRouteHystrixCommand command = initUnicornRouteHystrixCommand(exchange, chain, config);
return Mono.create(s ->
Subscription sub =command.toObservable().subscribe(s::success, s::error, s::success);
s.onCancel(sub::unsubscribe);
).onErrorResume((Function>) throwable ->
if (throwableinstanceof 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 (causeinstanceof ResponseStatusException || AnnotatedElementUtils
.findMergedAnnotation(cause.getClass(), ResponseStatus.class) !=null)
return Mono.error(cause);
default:
break;
return Mono.error(throwable);
).then();
;
@Override
public Stringname()
return NAME;
private class UnicornRouteHystrixCommandextends HystrixObservableCommand
private final URIfallbackUri;
private final ServerWebExchangeexchange;
private final GatewayFilterChainchain;
/**
* @param fallbackUri
* @param exchange
* @param chain
*/
public UnicornRouteHystrixCommand(URI fallbackUri, ServerWebExchange exchange, GatewayFilterChain chain, HystrixObservableCommand.Setter setter)
super(setter);
this.fallbackUri = fallbackUri;
this.exchange = exchange;
this.chain = chain;
@Override
protected Observableconstruct()
return RxReactiveStreams.toObservable(this.chain.filter(exchange));
@Override
protected ObservableresumeWithFallback()
if (null ==fallbackUri)
return super.resumeWithFallback();
URI uri =exchange.getRequest().getURI();
boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);
URI requestUrl = UriComponentsBuilder.fromUri(uri)
.host(null)
.port(null)
.uri(this.fallbackUri)
.build(encoded)
.toUri();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
ServerHttpRequest request =this.exchange.getRequest().mutate().uri(requestUrl).build();
ServerWebExchange mutated =exchange.mutate().request(request).build();
DispatcherHandler dispatcherHandler = UnicornHystrixGatewayFilterFactory.this.dispatcherHandlerProvider.getIfAvailable();
return RxReactiveStreams.toObservable(dispatcherHandler.handle(mutated));
public static class ApiHystrixTimeout
public StringgetApiPattern()
return apiPattern;
public void setApiPattern(String apiPattern)
this.apiPattern = apiPattern;
public IntegergetTimeout()
return timeout;
public void setTimeout(Integer timeout)
this.timeout = timeout;
private StringapiPattern;
private Integertimeout;
public static class Config
private Stringid;
private URIfallbackUri;
/**
* url -> timeout ms
*/
private Listtimeouts;
public StringgetId()
return id;
public ConfigsetId(String id)
this.id = id;
return this;
public URIgetFallbackUri()
return fallbackUri;
public ConfigsetFallbackUri(URI fallbackUri)
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 ListgetTimeouts()
return timeouts;
public ConfigsetTimeouts(List timeouts)
this.timeouts = timeouts;
return this;
配置示例
spring.cloud.gateway.default-filters[0].name=unicornHystrix
spring.cloud.gateway.default-filters[0].args.fallbackUri=forward:/defaultFallback
spring.cloud.gateway.default-filters[0].args.timeouts[0].apiPattern=/gf-oss-service//oss/part/upload
spring.cloud.gateway.default-filters[0].args.timeouts[0].timeout=100000
Bootstrap 5网格,如何在不同的断点处自定义网格排水沟(不使用实用程序类)?
【中文标题】Bootstrap 5网格,如何在不同的断点处自定义网格排水沟(不使用实用程序类)?【英文标题】:Bootstrap 5 grid, how to customize the grid gutters at different breakpoints (not using utility classes)? 【发布时间】:2021-09-29 02:27:20 【问题描述】:如何自定义 Bootstrap 5 以在不同的断点处设置不同的网格间距?我不想使用网格间距实用程序类,因为它们会在标记中到处都是硬编码并且很难更改。
看起来 Bootstrap 5 有这个变量作为默认的装订线宽度:
$grid-gutter-width: 1.5rem;
我想要的是这样的(这些值只是为了说明这个想法):
$grid-gutter-width-sm: 1rem;
$grid-gutter-width-md: 1.5rem;
$grid-gutter-width-lg: 2rem;
$grid-gutter-width-xl: 2.5rem;
【问题讨论】:
根据官方文档,排水沟类确实有响应断点,所以你应该能够使用,比如说g-0 g-lg-2
,直到中等,然后 g-2 (0.75rem) 从大。
我认为这些装订线类是“网格间距实用程序类”的一部分,他们希望全局更改每个断点的装订线宽度。
【参考方案1】:
这很棘手,因为排水沟用于构建所有 container*
、row
和 col*
。您可以使用每个断点的装订线宽度制作自定义地图,然后迭代地图以在每个断点处重新构建网格:
@import "functions";
@import "variables";
@import "mixins";
$custom-gutter-widths: (
sm: 1rem,
md: 1.5rem,
lg: 2rem,
xl: 2.5rem,
xxl: 3rem,
);
@import "bootstrap";
@each $breakpoint, $gutterwidth in $custom-gutter-widths
$container-padding-x: $gutterwidth*.5;
$grid-gutter-width: $gutterwidth;
@include media-breakpoint-up($breakpoint, $grid-breakpoints)
.container,
.container-fluid
@include make-container();
.row
@include make-row();
> *
@include make-col-ready();
@include make-grid-columns();
Bootstrap SASS on Codeply
注意:这会产生大量额外的 CSS!
【讨论】:
以上是关于自定义springcloud-gateway熔断处理的主要内容,如果未能解决你的问题,请参考以下文章