Spring Cloud学习笔记【四】断路器Hystrix

雪崩效应

在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因 “服务提供者” 的不可用导致 “服务消费者” 的不可用, 并将不可用逐渐放大的过程。
如果下图所示:A 作为服务提供者,B 为 A 的服务消费者,C 和 D 是 B 的服务消费者。A 不可用引起了 B 的不可用,并将不可用像滚雪球一样放大到 C 和 D 时,雪崩效应就形成了。

断路器

Netflix 创建了一个名为 Hystrix 的库, 实现了断路器的模式。“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

当然,在请求失败频率较低的情况下,Hystrix 还是会直接把故障返回给客户端。只有当失败次数达到阈值时,断路器打开并且不进行后续通信,而是直接返回备选响应。当然,Hystrix 的备选响应也是可以由开发者定制的。

 

Ribbon 整合 Hystrix

新建spring start project,导入依赖

 1 <dependencies>
 2    <dependency>
 3        <groupId>org.springframework.boot</groupId>
 4        <artifactId>spring-boot-starter-web</artifactId>
 5    </dependency>
 6    <dependency>
 7        <groupId>org.springframework.cloud</groupId>
 8        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 9    </dependency>
10    <dependency>
11        <groupId>org.springframework.cloud</groupId>
12        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
13    </dependency>
14    <dependency>
15        <groupId>org.springframework.cloud</groupId>
16        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
17    </dependency>
18</dependencies>

属性配置(application.yml)

server:
  port: 9001
spring:
  application:
    name: service-hystrix-ribbon
eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:123456@localhost:8761/eureka/

开启hystrix功能,在启动类上加上@EnableHystrix注解

 1 package com.carry.springcloud;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.client.loadbalancer.LoadBalanced;
 6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 7 import org.springframework.cloud.netflix.hystrix.EnableHystrix;
 8 import org.springframework.context.annotation.Bean;
 9 import org.springframework.web.client.RestTemplate;
10 
11 @EnableHystrix
12 @EnableEurekaClient
13 @SpringBootApplication
14 public class ServiceHystrixRibbonApplication {
15 
16     public static void main(String[] args) {
17         SpringApplication.run(ServiceHystrixRibbonApplication.class, args);
18     }
19 
20     @Bean
21     @LoadBalanced
22     RestTemplate restTemplate() {
23         return new RestTemplate();
24     }
25 }

添加fallback方法

package com.carry.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
public class RibbonController {

    @Autowired
    RestTemplate restTemplate;

    @Value("${server.port}")
    String port;

    @GetMapping("/getPoducerInfo")
    @HystrixCommand(fallbackMethod = "getPoducerInfoFallback")
    public String getPoducerInfo() {
        String result = this.restTemplate.getForObject("http://service-producer/getPortInfo", String.class);
        return result;
    }
    
    public String getPoducerInfoFallback(){
        return "getPoducerInfo异常,端口:" + port;
    }
}

测试

依次启动eureka-server、service-producer、service-hystrix-ribbon,如下图成功注册到eureka

访问localhost:9001/getPoducerInfo,结果轮询显示8080与8081,停掉8081

 

接着访问localhost:9001/getPoducerInfo,当访问到8081时出现以下结果,说明服务降级调用了fallback方法

Feign 整合 Hystrix

新建项目并导入依赖

 1 <dependencies>
 2    <dependency>
 3        <groupId>org.springframework.boot</groupId>
 4        <artifactId>spring-boot-starter-web</artifactId>
 5    </dependency>
 6    <dependency>
 7        <groupId>org.springframework.cloud</groupId>
 8        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 9    </dependency>
10    <dependency>
11        <groupId>org.springframework.cloud</groupId>
12        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
13    </dependency>
14    <dependency>
15        <groupId>org.springframework.cloud</groupId>
16        <artifactId>spring-cloud-starter-openfeign</artifactId>
17    </dependency>
18</dependencies>

配置属性

server:
  port: 9002
spring:
  application:
    name: service-hystrix-feign
eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:123456@localhost:8761/eureka/
feign: 
  hystrix: 
    enabled: true

启动类开启Feign支持

 1 package com.carry.springcloud;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.openfeign.EnableFeignClients;
 6 
 7 @EnableFeignClients
 8 @SpringBootApplication
 9 public class ServiceHystrixFeignApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(ServiceHystrixFeignApplication.class, args);
13     }
14 }

添加Feign接口

 1 package com.carry.springcloud.api;
 2 
 3 import org.springframework.cloud.openfeign.FeignClient;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 
 6 import com.carry.springcloud.fallback.ConsumerFeignFallback;
 7 
 8 @FeignClient(name = "service-producer", fallback = ConsumerFeignFallback.class)
 9 public interface ConsumerFeignClient {
10 
11     @GetMapping("/getPortInfo")
12     public String produce();
13 }

添加fallback类

 1 package com.carry.springcloud.fallback;
 2 
 3 import org.springframework.stereotype.Component;
 4 
 5 import com.carry.springcloud.api.ConsumerFeignClient;
 6 
 7 @Component
 8 public class ConsumerFeignFallback implements ConsumerFeignClient {
 9 
10     @Override
11     public String produce() {
12         return "服务调用失败!";
13     }
14 
15 }

控制层注入Feign接口

 1 package com.carry.springcloud.controller;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 import com.carry.springcloud.api.ConsumerFeignClient;
 8 
 9 @RestController
10 public class ConsumerController {
11     
12     @Autowired
13     private ConsumerFeignClient feignClient;
14     
15     @GetMapping("/getPoducerInfoByFeign")
16     public String getPoducerInfoByFeign() {
17         return feignClient.produce();
18     }
19 
20 }

测试

 测试方法同上不再赘述

posted @ 2018-08-15 17:15  CarryChan  阅读(599)  评论(0编辑  收藏  举报