Spring Cloud 断路器 Hystrix

在微服务架构中,一般存在多个服务调用层。微服务之间经过网络进行通讯,从而支撑起整个应用,为了保证高可用,单个服务一般也会集群部署。但因为网络缘由或者自身缘由,服务并不能保证100% 可用。而服务间的依赖关系,会致使故障传播,即服务提供者的不可用会致使消费者不可用,并把不可用逐渐放大,这就是雪崩效应。java

这里就引入了请求超时机制和断路器模式git

Hystrix 简介

Hystrix 是 Netflix 的开源组件,是一个用于实现超时机制和断路器模式的工具类库。在微服务架构中,一般存在多个服务调用层。较低级别的服务中的服务故障可能致使级联故障,一直到达用户。当对特定服务的调用超过一个阈值(默认是 10秒/20个请求/故障百分比 > 50%),断路器将打开而且不会进行调用。在出现错误和开路的状况下,开发人员能够提供一个回退github

断路打开后能够防止级联故障。回退能够是另外一个受 Hystrix 保护的调用、静态数据或合理的空值。web

Ribbon 中整合 Hystrix

咱们在 Spring Cloud 服务消费(Ribbon) 中的例子基础上添加 Hystrixspring

准备工做express

  • eureka-server 做为服务注册中心
  • product-service 做为服务提供者
  • 复制工程 order-service-ribbon 为 order-service-ribbon-hystrix 来整合 Hystrix

代码参考:https://github.com/morgan412/spring-cloud-demo/tree/master/netflix网络

添加 Hystrix 依赖架构

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

主类上添加注解 @EnableCircuitBreaker ,开启断路器功能app

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class OrderServiceRibbonHystrixApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceRibbonHystrixApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在须要容错的方法上添加注解 @HystrixCommand,并指定回调(回退)方法ide

@Service
public class ProductService {
    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "getProductFallBack")
    public Product getProduct(Long id) {
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    public Product getProductFallBack(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setName("默认商品");
        return product;
    }
}

@HystrixCommand 的配置能够比较灵活,可使用 commandProperties 属性列出 @HystrixProperty注释,例如

@HystrixCommand(fallbackMethod = "getProductFallBack",
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000")},
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "1"),
                    @HystrixProperty(name = "maxQueueSize", value = "10")
            })
@RestController
@Log4j2
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.getProduct(id);
    }
}

验证结果

  • 启动项目 eureka-server、product-service、order-service-ribbon-hystrix
  • 访问 http://localhost:8081/product/1 ,获得正常结果
  • 关闭 product-service 服务,再访问 http://localhost:8081/product/1 ,获得结果 {"id":-1,"name":"默认商品","description":null,"price":null,"count":null}。说明当调用的服务不可用时,执行了回退方法

Feign 使用 Hystrix

Feign 使用声明式的接口,没有方法体,因此经过注解的方式显然不适合 Feign。Spring Cloud 已经默认为 Feign 整合了 Hystrix。咱们在 Spring Cloud 服务消费(Feign) 中的例子基础上使用 Hystrix

准备工做

  • eureka-server 做为服务注册中心
  • product-service 做为服务提供者
  • 复制工程 order-service-feign 为 order-service-feign-hystrix 来使用 Hystrix

为 Feign 添加回退

Spring Cloud 已经默认为 Feign 整合了 Hystrix,因此咱们不须要添加依赖。配置文件中须要把 feign.hystrix.enabled 属性设置为 true

feign:
  hystrix:
    enabled: true

改写 Feign 客户端接口,在 @FeignClient 注解中经过 fallback 属性指定回调类

@FeignClient(value = "product-service", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProduct(@PathVariable Long id);
}

建立回调类,这个类须要实现 @FeignClient 的接口,其中实现的方法就是对应接口的回退方法

@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProduct(Long id) {
        Product product = new Product();
        product.setId(-1L);
        product.setName("默认商品");
        return product;
    }
}
@RestController
@Log4j2
public class ProductController {

    @Autowired
    private ProductFeignClient productFeignClient;

    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productFeignClient.getProduct(id);
    }
}

验证结果

  • 启动项目 eureka-server、product-service、order-service-feign-hystrix
  • 访问 http://localhost:8081/product/1 ,获得正常结果
  • 关闭 product-service 服务,再访问 http://localhost:8081/product/1 ,获得结果 {"id":-1,"name":"默认商品","description":null,"price":null,"count":null}。说明当调用的服务不可用时,执行了回退方法

经过 fallbackFactory 检查回退缘由

若是须要查看回退缘由,可使用注解 @FeignClient 中的 fallbackFactory 属性

@FeignClient(value = "product-service", fallbackFactory = ProductClientFallbackFactoty.class)
public interface ProductFeignClient {

    @GetMapping("/product/{id}")
    Product getProduct(@PathVariable Long id);
}

同时建立 ProductClientFallbackFactoty 类,实现 FallbackFactory 接口,create 方法有个 Throwable 参数记录回退缘由

@Component
@Log4j2
public class ProductClientFallbackFactoty implements FallbackFactory<ProductFeignClient> {
    @Override
    public ProductFeignClient create(Throwable throwable) {
        return new ProductFeignClient() {
            @Override
            public Product getProduct(Long id) {
                // 日志最好放在各个 fallback 方法中,而不要直接放在 create 方法中
                // 不然在引用启动时会打印该日志
                log.info("fallback, caused by:", throwable);
                Product product = new Product();
                product.setId(-1L);
                product.setName("默认商品");
                return product;
            }
        };
    }
}

测试一下结果,当调用的服务不可用时,会进入回调方法中,并打印以下日志

2019-05-06 14:21:20.384  INFO 8056 --- [ HystrixTimer-1] c.t.o.c.ProductClientFallbackFactoty     : fallback, caused by:
com.netflix.hystrix.exception.HystrixTimeoutException: null

Hystrix 监控

Hystrix 还提供了一些监控,当 HystrixCommand 和 HystrixObservableCommand 在执行时会产生会产生执行的结果和运行指标。能够配合 spring-boot-starter-actuator 模块同时设置 management.endpoints.web.exposure.include: hystrix.stream,经过 /actuator/hystrix.stream 端点来获取指标信息

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

当有 HystrixCommand 方法被执行,访问 /actuator/hystrix.stream 能够重复看到一些信息

Feign 项目须要添加依赖 spring-cloud-starter-netflix-hystrix,并在主类上加上注解 @EnableCircuitBreaker,才能使用 /actuator/hystrix.stream 端点

Hystrix Dashboard 监控面板

使用 Hystrix Dashboard 可让上面的监控数据可视化,新建一个 Spring Boot 工程 hystrix-dashboard,添加依赖 spring-cloud-starter-netflix-hystrix-dashboard

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

而后在主类上添加注解 @EnableHystrixDashboard

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }
}

设置服务端口 server.port=8000,启动 hystrix-dashboard,访问 http://localhost:8000/hystrix 能够看到如下界面

从上面的界面咱们能够看到,Hystrix Dashboard 面板监控支持三种方式:默认的集群监控、自定义的集群监控、单个应用的监控。其中前两个集群的方式是须要结合 Turbine 实现,后面去用到。另外 Delay 是轮询监控信息的延迟时间,Title 是监控界面标题,默认是监控实例的 URL

  • 默认的集群监控:http://turbine-hostname:port/turbine.stream
  • 自定义的集群监控: http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
  • 单个应用监控: http://hystrix-app:port/actuator/hystrix.stream

那咱们把上面例子的 order-service-ribbon-hystrix 服务添加 hystrix 监控相关的端点,pom.xml 添加依赖 spring-boot-starter-actuator,设置配置属性 management.endpoints.web.exposure.include=hystrix.stream,同时确保在主类上已经使用注解@EnableCircuitBreaker@EnableHystrix

完成配置后,启动项目,把地址 http://localhost:8081/actuator/hystrix.stream 添加到面板页面,能够看到如下界面

上面展现了 getProduct 这个 HystrixCommand 的各项指标,各指标含义能够看下图

Turbine 聚合监控数据

Turbine 是聚合 Hystrix 监控数据的工具,上面的 Dashboard 面板能够可视化一个 /actuator/hystrix.stream 端点的数据,可是对于多个服务和而且每一个服务有多个实例,来回的切换想要监控实例的地址就会很不方便。咱们须要一个把全部服务的 /actuator/hystrix.stream 端点数据整合起来的工具,这个工具就是 Turbine

建立一个 Spring Boot 项目命名为 hystrix-turbine,部分的依赖入以下,该依赖默认包含了 Eureka Client

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

主类上添加注解 @EnableTurbine,该注解的配置中已经配置了服务注册的功能,即至关于包含了 @EnableDiscoveryClient 的功能

@SpringBootApplication
@EnableTurbine
public class HystrixTurbineApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixTurbineApplication.class, args);
    }
}

配置信息以下

server:
  port: 8001
spring:
  application:
    name: hystrix-turbine
eureka:
  client:
    service-url:
      defaultZone:  http://localhost:8761/eureka/
      
turbine:
  app-config: order-service-ribbon-hystrix,order-service-feign-hystrix
  cluster-name-expression: "'default'"
  combine-host-port: true

turbine.app-config 配置用于查找实例的服务名
turbine.cluster-name-expression 指定集群名称,服务数量很的多时候,能够启动多个 Turbine 服务聚合不一样的集群数据,而该参数能够用来区分不一样的聚合
turbine.combine-host-port 经过主机名与端口号的组合来进行区分服务

咱们分别启动项目 eureka-server:876一、product-service:807一、order-service-ribbon-hystrix:808九、order-service-feign-hystrix:8081

咱们能够先测试下 http://localhost:8081/actuator/hystrix.streamhttp://localhost:8089/actuator/hystrix.stream 的端口有无问题,没有问题咱们启动 hystrix-dashboard,在页面输入 http://localhost:8001/turbine.stream 获得的界面以下

相关文章
相关标签/搜索