02 Spring Cloud Hystrix

Wu Jun 2020-02-21 10:57:50
10 微服务 > 1 服务调用

Hystrix 提供了熔断、隔离、Fallback、cache、监控等功能,能够在一个、或多个依赖同时出现问题时保证系统依然可用。

一 熔断

断路器

失败次数达到阈值后, 断路器会切换到开路状态。 所有请求快速失败返回备选响应。 熔断时间窗后, 自动切换到半开路状态。判断下次请求,如果请求成功, 断路器关闭,否则重新开路。
熔断器开关转换逻辑

容错计数

  1. Hystrix的Metrics中保存了当前服务的健康状况, 包括服务调用总次数和服务调用失败次数等
  2. 根据Metrics的计数, 熔断器从而能计算出当前服务的调用失败率, 用来和设定的阈值比较从而决定熔断器的状态切换逻辑

Fallback

Fallback相当于是降级操作。服务不可用时,可返回一个缺省值或缓存的值。

设置熔断

spring-cloud.version:Finchley

RestTemplate调用

  1. pom
<!--hystrix-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--配合dashboard-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 启动类注解
@EnableCircuitBreaker //或@EnableHystrix
@SpringBootApplication
  1. 调用Fallback
    在方法上加上@HystrixCommand,并指定fallbackMethod方法。
    @HystrixCommand注解的ignoreException参数可以设置忽略的异常
@Autowired
RestTemplate restTemplate;

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

@HystrixCommand(fallbackMethod = "fallback")
public String consumer() {
    return restTemplate.getForObject("http://localhost:8888/consumer", String.class);
}

public String fallback() {
    return "fallback";
}
  1. 配置url
    如果是springboot 2.x则需要配置ServletRegistrationBean, 因为springboot的默认路径不是 “/hystrix.stream”
@Configuration
public class ServletRegistrationConfig {
    @Bean
    public ServletRegistrationBean registerTurbineBean(){
        return  new ServletRegistrationBean(new HystrixMetricsStreamServlet(), "/hystrix.stream");
    }
}   

Feign调用

基于Feign:注解中加上fallback的指定类

  1. pom
<!--feign包含hystrix-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--支持之后的dashboard-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

  1. 启动类注解
@EnableFeignClients
@SpringBootApplication
  1. 配置项 开启hystrix,默认关闭
feign.hystrix.enabled=true
  1. Feign接口fallback
@Component
@FeignClient(name= "spring-cloud-provider",fallback = ConsumerFallback.class,url = "http://localhost:8761/")
public interface ConsumerClient {
    @RequestMapping(value = "/consumer", method = RequestMethod.GET)
    String consumer();
}
  1. fallback类
@Component
public class ConsumerFallback implements ConsumerClient{
    @Override
    public String consumer() {
        return "Feign客户端访问失败";
    }
}
  1. 使用Feign服务
@RestController
public class DcController {
    @Autowired
    ConsumerClient consumerClient;

    @GetMapping("/consumer")
    public String index() {
        return consumerClient.consumer();
    }
}

如是springboot 2.x也需配置"/hystrix.stream"

二 监控

spring-cloud.version:Finchley
a.调用hystrix:http://localhost:8002/consumer
b.查看hystrix详情:http://localhost:8002/hystrix.stream
c.查看turbine详情:http://localhost:8001/turbine.stream

单点监控-Dashboard

使用Hystrix Dashboard, 监控单个应用内的服务信息

  1. pom
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 启动类 启动类添加启用Hystrix Dashboard
@SpringBootApplication
@EnableHystrixDashboard

访问http://localhost:8080/hystrix监控首页
通过URLhttp://hystrix-app:port/hystrix.stream开启,实现对具体某个服务实例的监控。

多点监控-Turbine

把多个hystrix.stream的内容聚合为一个数据源供Dashboard展示

Spring Cloud Turbine

  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 启动类
@EnableTurbine
@SpringBootApplication
  1. application.properties
spring.application.name=hystrix-dashboard-turbine
server.port=8001
//配置Eureka中的serviceId列表,表明监控哪些服务
turbine.appConfig=node01,node02
//指定聚合哪些集群,多个使用”,”分割,默认为default。
turbine.aggregator.clusterConfig= default
//指定集群名称
turbine.clusterNameExpression= new String("default")

eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/

在dashboard中查看turbine详情:http://localhost:8001/turbine.stream

Netflix Turbine

使用原生turbine-core可不用eureka

  1. pom
<dependency>
	<groupId>com.netflix.turbine</groupId>
	<artifactId>turbine-core</artifactId>
	<version>1.0.0</version>
</dependency>
  1. config.properties 详细见官方文档
#集群名
turbine.aggregator.clusterConfig=${clusterName}
#集群名匹配
turbine.clusterNameExpression= new String("${clusterName}")
#集群url后缀
turbine.instanceUrlSuffix.${clusterName}= /hystrix.stream
#集群实例
turbine.ConfigPropertyBasedDiscovery.${clusterName}.instances= 10.0.64.166:9001,10.0.64.166:9002
  1. 配置url
@Configuration
public class ServletRegistrationConfig {
    @Bean
    public ServletRegistrationBean registerTurbineBean(){
        return  new ServletRegistrationBean(new TurbineStreamServlet(), "/turbine.stream");
    }
}
  1. 启动类
TurbineInit.init();

在Dashboard中查看,Dashboard和Turbine可以建在同一项目

三 其他

资源隔离

采用线程/信号的方式,通过隔离限制依赖的并发量和阻塞扩散。

请求缓存

通过注解开启缓存,和缓存相关的注解一共有三个

请求合并

在test10方法上添加@HystrixCollapser注解实现请求合并,用batchMethod属性指明请求合并后的处理方法,collapserProperties属性指定其他属性。

@HystrixCollapser(batchMethod = "test11",collapserProperties = {@HystrixProperty(name ="timerDelayInMilliseconds",value = "100")})
public Future<Book> test10(Long id) {
    return null;
}

@HystrixCommand
public List<Book> test11(List<Long> ids) {
    System.out.println("test9---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
    Book[] books = restTemplate.getForObject("http://HELLO-SERVICE/getbook6?ids={1}", Book[].class, StringUtils.join(ids, ","));
    return Arrays.asList(books);
}