天天看点

SpringCloud(Hystrix)

Hystrix 熔断器

    • 1. Hystrix 介绍
      • 1.1 是什么:
      • 1.2 Hystrix设计原则:
      • 1.3 Hystrix特性
    • 2. 图解
    • 3. SpringCloud 集成Hystrix
      • 3.1 maven依赖
      • 3.2 添加注解
      • 3.Hystrix的服务熔断
    • 4. Hystrix的服务降级
    • 5. Hystrix的异常处理
      • 5.1 未调用远程服务前发生异常
      • 5.2 调用远程服务发生异常
      • 5.3 忽略异常
      • 5.4 自定义Hystrix的服务异常熔断
        • 5.4.1 同步调用
        • 5.4.1 异步调用
    • 6. Hystrix的仪表盘监控
      • 6.1 添加maven依赖
      • 6.2 启动类添加注解
      • 6.3 配置文件中配置服务端口号
      • 6.4 配置要监控的项目
        • 6.4.1 首先需要引入Hystrix以及Dashboard的依赖
        • 6.4.1 修改需要监控的项目配置

1. Hystrix 介绍

1.1 是什么:

在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。简单点说:就是当被调用方没有响应,向调用方直接返回一个错误响应即可,而不是长时间的等待,这样避免调用时因为等待而线程一直得不到释放。

1.2 Hystrix设计原则:

  • 1.防止单个服务的故障,耗尽整个系统服务的容器(比如tomcat)的线程资源,避免分布式环境里大量级联失败。通过第三方客户端访问(通常是通过网络)依赖服务出现失败、拒绝、超时或短路时执行回退逻辑
  • 2.用快速失败代替排队(每个依赖服务维护一个小的线程池或信号量,当线程池满或信号量满,会立即拒绝服务而不会排队等待)和优雅的服务降级;当依赖服务失效后又恢复正常,快速恢复
  • 3.提供接近实时的监控和警报,从而能够快速发现故障和修复。监控信息包括请求成功,失败(客户端抛出的异常),超时和线程拒绝。如果访问依赖服务的错误百分比超过阈值,断路器会跳闸,此时服务会在一段时间内停止对特定服务的所有请求
  • 4.将所有请求外部系统(或请求依赖服务)封装到HystrixCommand或HystrixObservableCommand对象中,然后这些请求在一个独立的线程中执行。使用隔离技术来限制任何一个依赖的失败对系统的影响。每个依赖服务维护一个小的线程池(或信号量),当线程池满或信号量满,会立即拒绝服务而不会排队等待

1.3 Hystrix特性

  • 1.请求熔断: 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
  • 2.服务降级:Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.告知后面的请求服务不可用了,不要再来了。
  • 3.依赖隔离(采用舱壁模式,Docker就是舱壁模式的一种):在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池.比如说,一个服务调用两外两个服务,你如果调用两个服务都用一个线程池,那么如果一个服务卡在哪里,资源没被释放后面的请求又来了,导致后面的请求都卡在哪里等待,导致你依赖的A服务把你卡在哪里,耗尽了资源,也导致了你另外一个B服务也不可用了。这时如果依赖隔离,某一个服务调用A B两个服务,如果这时我有100个线程可用,我给A服务分配50个,给B服务分配50个,这样就算A服务挂了,我的B服务依然可以用。
  • 4.请求缓存:比如一个请求过来请求我userId=1的数据,你后面的请求也过来请求同样的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求。
  • 5.请求合并:我依赖于某一个服务,我要调用N次,比如说查数据库的时候,我发了N条请求发了N条SQL然后拿到一堆结果,这时候我们可以把多个请求合并成一个请求,发送一个查询多条数据的SQL的请求,这样我们只需查询一次数据库,提升了效率。

2. 图解

以下数据、图来自官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients
           

Netfilix创建了一个名为Hystrix的库,实现了熔断器模式。在微服务架构中,它通常有多个服务调用层。

微服务图
SpringCloud(Hystrix)
Hystrix回退以防止连锁故障
SpringCloud(Hystrix)

一个底层服务的故障会引发直至用户交互层的连锁故障。在一个设定时长为“metrics.rollingStats.timeInMilliseconds”(默认为十秒)的滚动窗口内,对一个特定服务的请求数大于“circuitBreaker.requestVolumeThreshold”(默认为20个),并且故障率大于“circuitBreaker.errorThresholdPercentage”(默认大于百分之五十)的时候,启用熔断机制以使请求失效。在熔断和报错的情况下,开发者可以启用回退机制。

启用熔断机制能防止连锁故障的情况,给故障服务提供时间以恢复正常。回退操作可以是另一个Hystrix受保护的调用、静态数据或是一个恰当的空值。回退操作可能是成串的,所以第一个回退操作会做一些其他的业务请求,让故障回退到预设的值。

3. SpringCloud 集成Hystrix

3.1 maven依赖

注意:添加maven依赖时,注意查看自己的SpringBoot 项目是什么版本的,因为 ServerPropertiesAutoConfiguration 在SpringBoot 2.x.x 移除了。如果你的SpringBoot 版本是2.x.x版本就不要引入这个依赖
<!--旧版本使用的-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
           

注意:应该引入以下这个版本。(别问我为什么知道?因为我就引错了。。。启动报错:org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.kinglong.springcloud.ComsumerApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfiguration.class] cannot be opened because it does not exist

at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:596) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]。

这个问题看官方文档的话,应该可以避免,见下图:)

SpringCloud(Hystrix)
<!--正确版本,应该是netflix的-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>
           

3.2 添加注解

Boot app 样例:

启动类

/**
 * @SpringBootApplication
 * @EnableEurekaClient//eureka客户端支持
 * @EnableCircuitBreaker//开启熔断器
 * 这三个注解在SpringCloud里可以合并为一个注解:@SpringCloudApplication,效果等同于这三个注解
 */
@SpringCloudApplication
public class ComsumerApplication {

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

}
           

解释:为什么可以这样呢?进入这个注解@SpringCloudApplication内部看可以看到:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient //此注解作用和@EnableEurekaClient一样,但是还将可以主持其他服务注册器
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}
           

3.Hystrix的服务熔断

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	
   /**
	 * @HystrixCommand由Netflix contrib library提供,被称作“javanica”
	 * Spring Cloud会自动将包含该注释的Spring bean封装在连接到Hystrix熔断器的代理中
	 * 熔断器会计算何时启用或关闭熔断机制,并决定在故障时该做什么。
	 * 在需要考虑熔断的方法上加上该注解即可,fallbackMethod 为回退时的指定操作方法;
	 * commandProperties:指定超时熔断时间;
	 * 
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error",commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超时
	public String hello() {
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔断的回调方法,也就是降级方法
	 * @return
	 */
	public String error(){
	//访问远程服务失败,处理逻辑写在这里即可
		return "error";
	}
}
           

4. Hystrix的服务降级

前文提到过:Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.告知后面的请求服务不可用了,不要再来了。这种做法,虽然不能得到正确的返回结果,但至少保证了服务的可用性,比直接抛出错误或服务不可用要好很多,可以根据业务场景自行选择。

5. Hystrix的异常处理

我们在调用服务提供者时,我们自己也有可能会抛出异常,默认情况下方法抛出了异常会自动进行服务降级,交给服务降级中的方法去处理。当我们自己发生异常后,只需要在服务降级方法中添加一个Throwable类型的参数就能够获取抛出的异常的类型。

5.1 未调用远程服务前发生异常

我们在上文的代码中模拟一个异常

/**
	 * @HystrixCommand由Netflix contrib library提供,被称作“javanica”
	 * Spring Cloud会自动将包含该注释的Spring bean封装在连接到Hystrix熔断器的代理中
	 * 熔断器会计算何时启用或关闭熔断机制,并决定在故障时该做什么。
	 *
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error", commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超时
	public String hello() {
		int i= 10/0;
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔断的回调方法,也就是降级方法
	 * @return
	 */
	public String error(){
	//访问远程服务失败,处理逻辑写在这里即可
		return "error";
	}
           

测试显示,未调用前发生异常也会触发熔断,调用服务是不会触发的,直接抛出运行时异常,并且可以在降级方法里获取到这个异常。

5.2 调用远程服务发生异常

将某个远程服务加入异常代码。测试显示,效果如上,可以在降级方法里获取到这个异常。

5.3 忽略异常

ignoreExceptions = RuntimeException.class 忽略异常,这样可以避免调用降级方法,根据业务场景选择

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	
   /**
	 * @HystrixCommand由Netflix contrib library提供,被称作“javanica”
	 * Spring Cloud会自动将包含该注释的Spring bean封装在连接到Hystrix熔断器的代理中
	 * 熔断器会计算何时启用或关闭熔断机制,并决定在故障时该做什么。
	 * 在需要考虑熔断的方法上加上该注解即可,fallbackMethod 为回退时的指定操作方法;
	 * commandProperties:指定超时熔断时间;
	 * ignoreExceptions = RuntimeException.class 忽略异常,这样可以避免调用降级方法,根据业务场景选择
	 * 
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error",ignoreExceptions = RuntimeException.class, commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超时
	public String hello() {
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔断的回调方法,也就是降级方法
	 * @return
	 */
	public String error(){
	//访问远程服务失败,处理逻辑写在这里即可
		return "error";
	}
}
           

5.4 自定义Hystrix的服务异常熔断

自定义类继承HystrixCommand类来实现自定义的Hystrix的请求,在getFallback方法中调用getExecutionException()方法获取服务抛出异常。

5.4.1 同步调用

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	/**
	 *  com.netflix.hystrix.HystrixCommand用全路径引用类是防止和@HystrixCommand这个注解混淆
	 *  MyHystrixCommand自定义熔断器
	 *  execute()调用是同步调用,会一直等待返回结果
	 *
	 * @return
	 */
	@RequestMapping("/hystrix")
	public String hystrix() {
		return new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate, "http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello").execute();
	}

}
           

5.4.1 异步调用

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	/**
	 *  com.netflix.hystrix.HystrixCommand用全路径引用类是防止和@HystrixCommand这个注解混淆
	 *  MyHystrixCommand自定义熔断器
	 *  execute()调用是同步调用,会一直等待返回结果
	 *
	 * @return
	 */
	@RequestMapping("/hystrix")
	public String hystrix() {
		Future<String> queue = new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate, "http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello").queue();
		String data = queue.get(20000, TimeUnit.MILLISECONDS);
		return data;
	}

}
           

自定义熔断器

package com.kinglong.springcloud.hystrix;

import com.netflix.hystrix.HystrixCommand;
import org.springframework.web.client.RestTemplate;

/**
 * 自定义的Hystrix
 *
 * @author haojinlong
 */
public class MyHystrixCommand extends HystrixCommand<String> {

	private RestTemplate restTemplate;

	private String url;

	public MyHystrixCommand (Setter setter, RestTemplate restTemplate){
		super(setter);
		this.restTemplate = restTemplate;
	}

	public MyHystrixCommand (Setter setter, RestTemplate restTemplate, String url){
		super(setter);
		this.restTemplate = restTemplate;
		this.url = url;
	}

	@Override
	protected String run() throws Exception {
		//调用远程的服务
		return restTemplate.getForEntity(url, String.class).getBody();
	}

	/**
	 * 当远程服务超时、异常、不可用时,调用该方法
	 *
	 * @return
	 */
	@Override
	protected String getFallback() {
		//实现服务熔断、降级处理
		return "error";
	}
}

           

6. Hystrix的仪表盘监控

在微服务架构中为例保证程序的可用性,防止程序出错导致网络阻塞,出现了断路器模型。断路器的状况反应了一个程序的可用性和健壮性,它是一个重要指标。Hystrix Dashboard是作为断路器状态的一个组件,提供了数据监控和友好的图形化界面。通过监控该面板,可以很直观的看到每一个服务请求在短时间内(10s)的请求量,以及成功率,失败率,耗时等信息,可以给后期的系统优化提供依据。

这个服务搭建十分快速,使用SpringBoot搭建三步走即可(我是新建的一个项目)

6.1 添加maven依赖

maven依赖最新版本即可

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

6.2 启动类添加注解

@SpringBootApplication
@EnableHystrixDashboard//打开仪表盘功能
public class DashBoardApplication {

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

}
           

6.3 配置文件中配置服务端口号

server.port=3721
           

大功告成,然后起服即可(访问路径为:http://localhost:3721/hystrix ,记得加上 /hystrix 路径),页面如下图所示:

SpringCloud(Hystrix)

6.4 配置要监控的项目

6.4.1 首先需要引入Hystrix以及Dashboard的依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
     <version>2.1.2.RELEASE</version>
 </dependency>
 <!--springboot提供的一个健康检查监控的依赖-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
           

6.4.1 修改需要监控的项目配置

#用来暴露endpoints的,由于endpoints包含很多敏感信息,除了health、info两个支持直接访问外,其他的默认不支持直接访问,可以指定某个端点访问,如下
#management.endpoints.web.exposure.include=hystrix.stream
#现在做测试,所以用通配符,可以访问所有端点
management.endpoints.web.exposure.include=*
           

至此,就配置好了

剩下的就是在先前的web页面里添加这个路径即可:http://localhost:8081/actuator/hystrix.stream

SpringCloud(Hystrix)

配置后,点击监控即可。

SpringCloud(Hystrix)

继续阅读