• SpringCloud系列十六:Feign使用Hystrix


    1. 回顾

      上文讲解了使用注解@HystrixCommand的fallbackMethod属性实现回退。然而,Feign是以接口形式工作的,

    它没有方法体,前文讲解的方式显然不适用与Feign。

      事实上,Spring Cloud默认已为Feign整合了Hystrix,只要Hystrix在项目的classpath中,Feign默认就会用

    断路器包裹所有方法。

    2. 为Feign添加回退

      > 复制项目 microservice-consumer-movie-feign ,将 ArtifactId 修改为 microservice-consumer-movie-feign-hystrix-fallback

      > 将之前编写的Feign接口修改为如下内容:

    package com.itmuch.cloud.microserviceconsumermoviefeignhystrixfallback.feign;
    
    import com.itmuch.cloud.microserviceconsumermoviefeignhystrixfallback.pojo.User;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    /**
     * Feign的fallback测试类
     * 使用@FeignClient的fallback属性指定回退类
     */
    @FeignClient(name = "microservice-provider-user", fallback = FeignClientFallback.class)
    public interface UserFeignClient {
    
        @GetMapping(value = "/{id}")
        User findById(@PathVariable("id") Long id);
    
    }
    
    /**
     * 回退类FeignClientFallback需实现Feign Client接口
     * FeignClientFallback也可以是public class,没有区别
     */
    @Component
    class FeignClientFallback implements UserFeignClient {
    
        @Override
        public User findById(Long id) {
            User user = new User();
            user.setId(-1L);
            user.setUsername("默认用户");
            return user;
        }
    }

      > 修改 application.yml 文件,声明 Feign 的 Hystrix 支持

    feign:
      hystrix:
        enabled: true

      > 启动 microservice-discovery-eureka

      > 启动 microservice-provider-user

      > 启动 microservice-consumer-movie-feign-hystrix-fallback

      > 访问 http://localhost:8010/user/1 ,可正常获取结果

      > 停止 microservice-provider-user

      > 访问 http://localhost:8010/user/1 ,可获取回退方法里返回的结果

     

    3. 通过Fallback Factory检查回退原因

      > 复制项目 microservice-consumer-movie-feign,将 ArtifactId 修改为 microservice-consumer-movie-feign-hystrix-fallback-factory

      > 修改 UserFeignClient.java 为如下内容

    package com.itmuch.cloud.microserviceconsumermoviefeignhystrixfallbackfactory.feign;
    
    import com.itmuch.cloud.microserviceconsumermoviefeignhystrixfallbackfactory.pojo.User;
    import feign.hystrix.FallbackFactory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    @FeignClient(name = "microservice-provider-user", fallbackFactory = FeignClientFallbackFactory.class)
    public interface UserFeignClient {
    
        @GetMapping(value = "/{id}")
        User findById(@PathVariable("id") Long id);
    
    }
    
    /**
     * UserFeignClient的fallbackFactory类,该类需要实现FallbackFactory接口,并覆写create方法
     */
    @Component
    class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(FeignClientFallbackFactory.class);
    
        @Override
        public UserFeignClient create(Throwable throwable) {
            // 下方匿名方法的Lambda表达式。>= JDK8
            return (id) -> {
                // 日志最好放在各个fallback方法中,而不要直接放在create方法中。
                // 否则在引用启动时,就会打印该日志
                FeignClientFallbackFactory.LOGGER.info("fallback; reason was: ", throwable);
                User user = new User();
                user.setId(-1L);
                user.setUsername("默认用户");
                return user;
            };
    
           /* return new UserFeignClient() {
                @Override
                public User findById(Long id) {
                    // 日志最好放在各个fallback方法中,而不要直接放在create方法中。
                    // 否则在引用启动时,就会打印该日志
                    FeignClientFallbackFactory.LOGGER.info("fallback; reason was: ", throwable);
                    User user = new User();
                    user.setId(-1L);
                    user.setUsername("默认用户");
                    return user;
                }
            };*/
        }
    
    }

      > 修改 application.yml 文件,声明 Feign 的 Hystrix 支持

    feign:
      hystrix:
        enabled: true

      > 跟上方测试方法一样。此次调用回退方法时,控制台会打出错误日志

    2018-06-07 13:55:31.716  INFO 5188 --- [provider-user-1] c.i.c.m.f.FeignClientFallbackFactory     : fallback; reason was: 
    
    java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: microservice-provider-user
        at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:71) ~[spring-cloud-openfeign-core-2.0.0.M1.jar:2.0.0.M1]
        at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:97) ~[feign-core-9.5.1.jar:na]
        at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-9.5.1.jar:na]
        at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:108) ~[feign-hystrix-9.5.1.jar:na]
        at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302) ~[hystrix-core-1.5.12.jar:1.5.12]
        at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298) ~[hystrix-core-1.5.12.jar:1.5.12]
        at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.unsafeSubscribe(Observable.java:10256) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.unsafeSubscribe(Observable.java:10256) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.unsafeSubscribe(Observable.java:10256) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100) [rxjava-1.3.6.jar:1.3.6]
        at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56) [hystrix-core-1.5.12.jar:1.5.12]
        at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47) [hystrix-core-1.5.12.jar:1.5.12]
        at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69) [hystrix-core-1.5.12.jar:1.5.12]
        at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) [rxjava-1.3.6.jar:1.3.6]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_161]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_161]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]
    Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: microservice-provider-user
        at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
        at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
        at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
        at rx.Observable.unsafeSubscribe(Observable.java:10256) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.unsafeSubscribe(Observable.java:10256) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.Subscriber.setProducer(Subscriber.java:209) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) [rxjava-1.3.6.jar:1.3.6]
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) [rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.subscribe(Observable.java:10352) [rxjava-1.3.6.jar:1.3.6]
        at rx.Observable.subscribe(Observable.java:10319) [rxjava-1.3.6.jar:1.3.6]
        at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443) ~[rxjava-1.3.6.jar:1.3.6]
        at rx.observables.BlockingObservable.single(BlockingObservable.java:340) ~[rxjava-1.3.6.jar:1.3.6]
        at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112) ~[ribbon-loadbalancer-2.2.4.jar:2.2.4]
        at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:63) ~[spring-cloud-openfeign-core-2.0.0.M1.jar:2.0.0.M1]
        ... 32 common frames omitted
    
    2018-06-07 14:00:17.616  INFO 5188 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration

     4. 为Feign禁用Hystrix

      上面说过,在Spring Cloud中,只要Hystrix在项目的classpath中,Feign就会使用断路器包裹Feign客户端的所有方法。这样虽然方便,但是很多场景下并不需要该功能。

      全局禁用Hystrix,只须在 application.yml 中配置 feign.hystrix.enabled=false 即可

      借助Feign的自定义配置,可轻松为指定名称的Feign客户端禁用Hystrix。

      > 创建配置类

    package com.itmuch.cloud.microserviceconsumermoviefeignhystrixfallbackfactory.config;
    
    import feign.Feign;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    
    @Configuration
    public class FeignDisableHystrixConfiguration {
    
        @Bean
        @Scope("prototype")
        public Feign.Builder feignBuilder() {
            return Feign.builder();
        }
    
    }

      > 在想要禁用Hystrix的@FeignClient引用该配置类即可

    @FeignClient(name = "microservice-provider-user", configuration = FeignDisableHystrixConfiguration.class)
    public interface UserFeignClient {
    
    }

    5. 总结

      本文讲了Feign使用Hystrix的各种知识。包括使用、检查回退原因、禁用Hystrix等。

      下文将讲解Hystrix的监控。敬请期待~~~

    6. 参考

      周立 --- 《Spring Cloud与Docker微服务架构与实战》

  • 相关阅读:
    富文本
    管理员状态
    分页
    tp。3.2中的模板基础
    get和post之间的区别
    RegExp
    获取各种类型的节点
    节点的层次关系
    创建元素节点
    JavaScript 正则
  • 原文地址:https://www.cnblogs.com/jinjiyese153/p/9150269.html
Copyright © 2020-2023  润新知