美文网首页springcloud
Spring Cloud Hystrix断路器

Spring Cloud Hystrix断路器

作者: yunqing_71 | 来源:发表于2019-10-28 19:57 被阅读0次

    Hystrix介绍

    当微服务之间调用的时候,假设A服务调用B服务,B服务调用C服务,如果调用链路上的任何一环出现异常响应时间过长或者服务不可用,就会造成调用方长时间得不到响应或者错误异常响应而占用线程,占用系统资源过多甚至会引起服务雪崩。

    Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。

    话不多说,开干!!!

    1. 创建hystrix-service模块,模块的创建过程参考Spring Cloud Eureka服务注册中心

      添加依赖有:

      <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <!--这个依赖是我之后介绍使用WebClient代替RestTemplate需要引入的依赖-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-webflux</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
          </dependency>
      
    2. 添加application.yml配置

    
    server:
      port: 8401 #端口号
    spring:
      application:
        name: hystrix-service #服务名称
    
    eureka:
      client:
        register-with-eureka: true
        fetch-registry: true
        service-url:
          defaultZone: http://localhost:8001/eureka/ #注册中心地址
    
    #这是我声明的一个值,在springboot中通过@Value注解可调用
    service-url:
      emp-service: http://emp-service # emp-service的调用路径,cloud的模块之间的调用要用服务名称
    

    3.需要通过启动类加上@EnableCircuitBreaker启用对断路器的支持

    /**
     * @EnableCircuitBreaker 开启断路器功能
     */
    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @SpringBootApplication
    public class HystrixServiceApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(HystrixServiceApplication.class, args);
        }
    
    
    }
    

    4.参考上一篇文章给RestTemplate和WebClient负载均衡的能力,也就是加入RibbonConfig类

    @Configuration
    public class RibbonConfig {
    
        /**
         * @LoadBalanced 使 WebClient能够进行负载均衡
         * @return
         */
        @Bean
        @LoadBalanced
        public WebClient.Builder webClient() {
            return WebClient.builder();
        }
    
        /**
         * @LoadBalanced 使 RestTemplate能够进行负载均衡
         * @return
         */
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    

    5.通过本模块调用emp-service的服务来讲解断路器的功能,添加HystrixController来调用emp-service的服务。首先是通过RestTemplate完成调用。

    @RestController
    @RequestMapping("/emp")
    public class HystrixController {
    
        /**
         * 注入RestTemplate
         */
        @Autowired
        private RestTemplate restTemplate;
    
        /**
         * 获取application.yml中声明的emp-service的服务调用路径
         */
        @Value("${service-url.emp-service}")
        private String empServiceUrl;
    
        /**
         * @HystrixCommand 可以声明该方法需要使用断路器保护
         * fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
         * 注意这里的fallback必须和当前方法拥有相同的返回值,参数
         * @param id
         * @return
         */
        @HystrixCommand(fallbackMethod = "fallback")
        @GetMapping("/{id}")
        public String getEmpById(@PathVariable Long id){
    
            return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);
    
        }
    
    
    
        public String fallback(@PathVariable Long id) {
            return "服务降级处理";
        }
    }
    

    @HystrixCommand 可以声明该方法需要使用断路器保护,然后fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法

    6.启动这四个服务,然后访问http://localhost:8401/emp/11

    image

    查询员工返回200调用成功,刷新几次发现交替在8201和8202端口调用emp-service的根据id查询员工的服务

    image

    负载均衡的调用emp-service成功

    image image

    7.下面停掉8202端口的emp-service的服务,再次刷新

    image

    刷新发现会出现200服务降级处理两种结果,这是因为当负载均衡到8202端口的时候由于服务已经停掉,所以就开启了断路器,执行服务熔断降级,根据配置的降级处理调用fallback方法,所以返回服务降级处理这个结果。

    image

    8.刚才测试了直接关闭服务之后的降级处理,现在测试一下服务开启的状态,emp-service服务的代码中加一个运行时异常之后的处理结果。

    image

    重新启动emp-service服务,访问localhost:8401/emp/11

    返回结果,服务降级处理

    image

    查看一下emp-service服务的控制台,发现报异常了,所以是出现异常然后断路器做了降级处理了。

    image

    9.restTemplate调用时通过@HystrixCommand(fallbackMethod = "fallback")设置服务降级处理,那如果通过WebClient调用呢?直接上代码!!!

    @RestController
    @RequestMapping("/emp")
    public class HystrixController {
    
        /**
         * 注入webClient
         */
        @Autowired
        private WebClient.Builder webClient;
    
        /**
         * 注入RestTemplate
         */
        @Autowired
        private RestTemplate restTemplate;
    
        /**
         * 获取application.yml中声明的emp-service的服务调用路径
         */
        @Value("${service-url.emp-service}")
        private String empServiceUrl;
    
        /**
         * @HystrixCommand 可以声明该方法需要使用断路器保护
         * fallbackMethod = "fallback" 指定当服务出现问题,做降级处理,走fallback方法
         * 注意这里的fallback必须和当前方法拥有相同的返回值,参数
         * @param id
         * @return
         */
        //@HystrixCommand(fallbackMethod = "fallback")
        @GetMapping("/{id}")
        public Mono<String> getEmpById(@PathVariable Long id){
    
            Mono<String> call = webClient.build()
                    .get()
                    .uri(empServiceUrl + "/emp/{id}", id)
                    .retrieve()
                    .bodyToMono(String.class);
            Mono<String> mono = HystrixCommands
                    .from(call)
                    .fallback(Mono.just("服务降级处理"))
                    .commandName("getEmpById")
                    .toMono();
            return mono;
            //return restTemplate.getForObject(empServiceUrl + "/emp/{1}", String.class, id);
    
        }
    
    
    
        /*public String fallback(@PathVariable Long id) {
            return "服务降级处理";
        }*/
    }
    

    额。可以发现不需要注解@HystrixCommand,具体写法就是如上面代码,我也是找了好多资料才在https://stackoverflow.com上找到的。

    @HystrixCommand won't really work, because Hystrix doesn't threat Mono/Flux any different from Java primitives.

    Hystrix doesn't monitor content of Mono, but only the result of call public Mono<WeatherApiResponse> getWeatherByCityName(String cityName).

    This result is always OK, because reactive-call-chain creation will always succeed.

    What you need, is to make Hystrix threat Mono/Flux differently. In Spring Cloud, there is a builder, to wrap Mono/Flux with HystrixCommand.

    Mono<WeatherApiResponse> call = this.getWeatherByCityName(String cityName);
    
    Mono<WeatherApiResponse> callWrappedWithHystrix = HystrixCommands
                                    .from(call)
                                    .fallback(Mono.just(WeatherApiResponse.EMPTY))
                                    .commandName("getWeatherByCityName")
                                    .toMono();
    

    项目源码见 https://github.com/kangqing/cloud

    相关文章

      网友评论

        本文标题:Spring Cloud Hystrix断路器

        本文链接:https://www.haomeiwen.com/subject/hhqivctx.html