聊聊spring cloud netflix的HystrixCommands

本文主要研究一下spring cloud netflix的HystrixCommands。

maven

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.0.0.RELEASE</version>
        </dependency>

这个组件对hystrix进行了封装了,2.0.0.RELEASE全面支持了Reactor的Reactive Streams。

spring-cloud-starter-netflix-hystrix/pom.xml

spring-cloud-starter-netflix-hystrix-2.0.0.RELEASE.jar!/META-INF/maven/org.springframework.cloud/spring-cloud-starter-netflix-hystrix/pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <name>Spring Cloud Starter Netflix Hystrix</name>
    <description>Spring Cloud Starter Netflix Hystrix</description>
    <url>https://projects.spring.io/spring-cloud</url>
    <organization>
        <name>Pivotal Software, Inc.</name>
        <url>https://www.spring.io</url>
    </organization>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-archaius</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-serialization</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-metrics-event-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
        </dependency>
        <dependency>
            <groupId>io.reactivex</groupId>
            <artifactId>rxjava-reactive-streams</artifactId>
        </dependency>
    </dependencies>
</project>

这里要讲的HystrixCommands在spring-cloud-netflix-core这个组件里

HystrixCommands

spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/hystrix/HystrixCommands.java

/**
 * Utility class to wrap a {@see Publisher} in a {@see HystrixObservableCommand}. Good for
 * use in a Spring WebFlux application. Allows more flexibility than the @HystrixCommand
 * annotation.
 * @author Spencer Gibb
 */
public class HystrixCommands {

    public static <T> PublisherBuilder<T> from(Publisher<T> publisher) {
        return new PublisherBuilder<>(publisher);
    }

    public static class PublisherBuilder<T> {
        private final Publisher<T> publisher;
        private String commandName;
        private String groupName;
        private Publisher<T> fallback;
        private Setter setter;
        private HystrixCommandProperties.Setter commandProperties;
        private boolean eager = false;
        private Function<HystrixObservableCommand<T>, Observable<T>> toObservable;

        public PublisherBuilder(Publisher<T> publisher) {
            this.publisher = publisher;
        }

        public PublisherBuilder<T> commandName(String commandName) {
            this.commandName = commandName;
            return this;
        }

        public PublisherBuilder<T> groupName(String groupName) {
            this.groupName = groupName;
            return this;
        }

        public PublisherBuilder<T> fallback(Publisher<T> fallback) {
            this.fallback = fallback;
            return this;
        }

        public PublisherBuilder<T> setter(Setter setter) {
            this.setter = setter;
            return this;
        }

        public PublisherBuilder<T> commandProperties(
                HystrixCommandProperties.Setter commandProperties) {
            this.commandProperties = commandProperties;
            return this;
        }

        public PublisherBuilder<T> commandProperties(
                Function<HystrixCommandProperties.Setter, HystrixCommandProperties.Setter> commandProperties) {
            if (commandProperties == null) {
                throw new IllegalArgumentException(
                        "commandProperties must not both be null");
            }
            return this.commandProperties(
                    commandProperties.apply(HystrixCommandProperties.Setter()));
        }

        public PublisherBuilder<T> eager() {
            this.eager = true;
            return this;
        }

        public PublisherBuilder<T> toObservable(Function<HystrixObservableCommand<T>, Observable<T>> toObservable) {
            this.toObservable = toObservable;
            return this;
        }

        public Publisher<T> build() {
            if (!StringUtils.hasText(commandName) && setter == null) {
                throw new IllegalStateException("commandName and setter can not both be empty");
            }
            Setter setterToUse = getSetter();

            PublisherHystrixCommand<T> command = new PublisherHystrixCommand<>(setterToUse, this.publisher, this.fallback);

            Observable<T> observable = getObservableFunction().apply(command);

            return RxReactiveStreams.toPublisher(observable);
        }

        public Function<HystrixObservableCommand<T>, Observable<T>> getObservableFunction() {
            Function<HystrixObservableCommand<T>, Observable<T>> observableFunc;

            if (this.toObservable != null) {
                observableFunc = this.toObservable;
            } else if (this.eager) {
                observableFunc = cmd -> cmd.observe();
            } else { // apply a default onBackpressureBuffer if not eager
                observableFunc = cmd -> cmd.toObservable().onBackpressureBuffer();
            }
            return observableFunc;
        }

        public Setter getSetter() {
            Setter setterToUse;
            if (this.setter != null) {
                setterToUse = this.setter;
            } else {
                String groupNameToUse;
                if (StringUtils.hasText(this.groupName)) {
                    groupNameToUse = this.groupName;
                } else {
                    groupNameToUse = commandName + "group";
                }

                HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(groupNameToUse);
                HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(this.commandName);
                HystrixCommandProperties.Setter commandProperties = this.commandProperties != null
                        ? this.commandProperties
                        : HystrixCommandProperties.Setter();
                setterToUse = Setter.withGroupKey(groupKey).andCommandKey(commandKey)
                        .andCommandPropertiesDefaults(commandProperties);
            }
            return setterToUse;
        }

        public Flux<T> toFlux() {
            return Flux.from(build());
        }

        public Mono<T> toMono() {
            return Mono.from(build());
        }

    }

    private static class PublisherHystrixCommand<T> extends HystrixObservableCommand<T> {

        private Publisher<T> publisher;
        private Publisher<T> fallback;

        protected PublisherHystrixCommand(Setter setter, Publisher<T> publisher,
                Publisher<T> fallback) {
            super(setter);
            this.publisher = publisher;
            this.fallback = fallback;
        }

        @Override
        protected Observable<T> construct() {
            return RxReactiveStreams.toObservable(publisher);
        }

        @Override
        protected Observable<T> resumeWithFallback() {
            if (this.fallback != null) {
                return RxReactiveStreams.toObservable(this.fallback);
            }
            return super.resumeWithFallback();
        }
    }
}

从类注释可以看到这个类就是为了方便webflux应用使用hystrix而设计的。

实例

    @Test
    public void testHystrixFallback() throws InterruptedException {
        Mono<String> delayMono = Mono.just("hello")
                .delayElement(Duration.ofMillis(500));
        Mono<String> result = HystrixCommands.from(delayMono)
                .commandName("demoCmd")
                .groupName("demoGroup")
                .eager()
                .commandProperties(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                        .withExecutionTimeoutInMilliseconds(1000)
                )
                .fallback(Mono.just("from fallback"))
                .toMono();

        System.out.println(result.block());

    }
  • HystrixCommands.from方法可以对Publisher进行hystrix包装
  • commandName用于指定hystrix的command名称
  • groupName用于指定hystrix的group名称
  • eager是默认方式,表示使用的是observe()方法,相当于hot Observable,只能消费从订阅时刻之后的数据,lazy使用的是toObservable()方法,相当于cold Observable,可以消费订阅之前的数据。
  • commandProperties用于指定command的属性,比如executionIsolationStrategy、executionTimeoutInMilliseconds
  • fallback用于指定fallback的操作

另外配置文件也可以指定默认的参数,比如

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds : 6000
      circuitBreaker:
        sleepWindowInMilliseconds: 10000
      metrics:
        rollingStats:
          timeInMilliseconds : 18000

小结

HystrixCommands就是spring cloud对netflix hystrix的包装,以方便webflux里头使用hystrix,就省得再去使用AOP技术了。

doc

  • Reactive Execution
  • hystrix-javanica
  • visualizing-reactive-streams-hot-and-cold

原文发布于微信公众号 - 码匠的流水账(geek_luandun)

原文发表时间:2018-06-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

扫码关注云+社区

领取腾讯云代金券