欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring cloud gateway集成hystrix实战篇

程序员文章站 2022-03-07 12:26:36
spring cloud gateway集成hystrix本文主要研究一下spring cloud gateway如何集成hystrixmaven...

spring cloud gateway集成hystrix

本文主要研究一下spring cloud gateway如何集成hystrix

maven

<dependency>
            <groupid>org.springframework.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-hystrix</artifactid>
        </dependency>

添加spring-cloud-starter-netflix-hystrix依赖,开启hystrix

配置实例

hystrix.command.fallbackcmd.execution.isolation.thread.timeoutinmilliseconds: 5000
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
      - id: employee-service
        uri: lb://employee-service
        predicates:
        - path=/employee/**
        filters:
        - rewritepath=/employee/(?<path>.*), /$\{path}
        - name: hystrix
          args:
            name: fallbackcmd
            fallbackuri: forward:/fallback
  • 首先filter里头配置了name为hystrix的filter,实际是对应hystrixgatewayfilterfactory
  • 然后指定了hystrix command的名称,及fallbackuri,注意fallbackuri要以forward开头
  • 最后通过hystrix.command.fallbackcmd.execution.isolation.thread.timeoutinmilliseconds指定该command的超时时间

fallback实例

@restcontroller
@requestmapping("/fallback")
public class fallbackcontroller {
    @requestmapping("")
    public string fallback(){
        return "error";
    }
}

源码解析

gatewayautoconfiguration

spring-cloud-gateway-core-2.0.0.rc2-sources.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) {
            return new hystrixgatewayfilterfactory(dispatcherhandler);
        }
    }  
    //......
}

引入spring-cloud-starter-netflix-hystrix类库,就有hystrixobservablecommand.class, rxreactivestreams.class,便开启hystrixconfiguration

hystrixgatewayfilterfactory

spring-cloud-gateway-core-2.0.0.rc2-sources.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);
                        return exchange.getresponse().setcomplete();
                    }
                }
                return mono.error(throwable);
            }).then();
        };
    }
    //......
}

这里创建了routehystrixcommand,将其转换为mono,然后在onerrorresume的时候判断如果hystrixruntimeexception的failuretype是failuretype.timeout类型的话,则返回gateway_timeout(504, "gateway timeout")状态码。

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();
            return rxreactivestreams.toobservable(hystrixgatewayfilterfactory.this.dispatcherhandler.handle(mutated));
        }
    }
  • 这里重写了construct方法,rxreactivestreams.toobservable(this.chain.filter(exchange)),将reactor的mono转换为rxjava的observable
  • 这里重写了resumewithfallback方法,针对有fallbackuri的情况,重新路由到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;
            return this;
        }
    }

可以看到config校验了fallbackuri,如果不为null,则必须以forward开头

小结

spring cloud gateway集成hystrix,分为如下几步:

  • 添加spring-cloud-starter-netflix-hystrix依赖
  • 在对应route的filter添加name为hystrix的filter,同时指定hystrix command的名称,及其fallbackuri(可选)
  • 指定该hystrix command的超时时间等。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。