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的超时时间等。

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