自定义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*rowcol*。您可以使用每个断点的装订线宽度制作自定义地图,然后迭代地图以在每个断点处重新构建网格:

@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熔断处理的主要内容,如果未能解决你的问题,请参考以下文章

Feign-灵活的使用Hystrix熔断(自定义CommandKey)

hystrix熔断器之自定义插架

SpringCloud-Gateway

服务网关zuul之五:熔断

SpringCloud-Gateway 网关路由断言过滤

springcloud-gateway集成knife4j