SpringCloud基础知识【Hystrix熔断器】
1. Hystrix概述
Hystix,是Netflix开源的一个延迟和容错库
,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩
)。
- 雪崩:一个服务失败。导致整条链路的服务都失败的情形。
那么如何预防雪崩的出现呢?
- Hystix为我们提供了
隔离
、降级
、熔断
、限流
等方法防止雪崩的发生。
2. Hystix-隔离
资源隔离
要解决的核心的问题:就是将多个依赖服务的调用
分别隔离到各自的资源池
内。
- 避免对某一个依赖服务的调用,因为依赖服务的接口调用的延迟或者失败,
导致服务所有的线程资源全部耗费在这个服务的接口调用上
。 - 一旦某个服务的线程资源全部耗尽的话,可能就导致服务就会崩溃,甚至说这种故障会不断蔓延。如下图所示:
2.1 线程池隔离
线程池隔离:适合绝大多数的场景,Hystix默认采用线程池隔离
- 如下图所示,
A
服务将Threadpool
中拆分成Threadpool-B
、Threadpool-C
、Threadpool-D
3个单独对B\C\D
进行服务。 - 各线程池相互独立。一个服务
[C]
挂掉并不会引起整条链路都失败的情况发生。
线程池隔离优缺点
优点∶
- 使用线程池隔离可以
完全隔离
依赖的服务(例如图中的 B、C、D服务)
,请求线程可以快速放回 - 当线程池出现问题时,
线程池隔离是独立的
,不会影响其他服务和接口。 - 当失败的服务再次变得可用时,
线程池将清理并可立即恢复
,而不需要一个长时间的恢复。 - 独立的线程池提高了并发性。
缺点∶
- 线程池隔离的主要缺点是它们增加计算开销(CPU)。
每个命令
的执行涉及到排队、调度和上下文切换都是在一个单独的线程
上运行的。
2.2 信号量隔离
信号量隔离:适合与对内部的一些比较复杂的业务逻辑的访问,而不是对外部依赖的访问。
- 一个线程池、但对不同的服务上加上一个
阀门(访问的次数)
。超过该数量,就拒绝后续的服务。
2.3 Hystix隔离小结
是否有线程切换 | 是否支持异步 | 是否支持超时 | 是否支持熔断 | 是否支持限流 | 开销 | |
---|---|---|---|---|---|---|
线程池 | √ | √ | √ | √ | √ | 大 |
信号量 | × | × | × | √ | √ | 小 |
什么情况下,用线程池隔离?
- 请求并发量大,并且耗时长(计算量大,或读数据库)
- 原因:可以保证大量的容器(tomcat)线程可用,不会由于服务原因,一直处于阻塞或等待状态,快速失败返回。
什么情况下,用信号量隔离?
- 请求并发量大,并且耗时短(计算量小,或读缓存)
- 原因:这类服务的返回通常会非常的快,不会占用容器线程太长时间,而且也减少了线程切换的一些开销,提高了缓存服务的效率。
3. Hystix-降级
Hystix降级:当服务发生异常或调用超时,返回默认处理方案(默认数据)
。
3.1 服务提供方降级
Hystrix初始代码
和Fiegn初始代码
一样,这里就不再赘述。
- 在服务提供方
(hystrix-provider)
,引入 hystrix依赖 - 定义降级方法
- 使用
@HystrixCommand
注解配置降级方法 - 在启动类上开启Hystrix功能:
@EnableCircuitBreaker
实例操作
- 引入 hystrix依赖
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 在服务提供方启动类开启开启Hystrix功能
package com.itheima.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient //该注解 在新版本中可以省略
@SpringBootApplication
@EnableCircuitBreaker //开启Hystrix功能
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
}
- 因为后续会有
消费方
调用提供方中controller中提供的方法接口,那么如果该接口出异常,我们就需要为其提供一个降级方法。
package com.itheima.provider.controller;
import com.itheima.provider.domain.Goods;
import com.itheima.provider.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* Goods Controller 服务提供方
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
/**
* 降级:
* 1. 出现异常
* 2. 服务调用超时
* * 默认1s超时
*
* @HystrixCommand(fallbackMethod = "findOne_fallback")
* fallbackMethod:指定降级后调用的方法名称
*/
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Goods findOne(@PathVariable("id") int id){
//降级情况1.异常
int i = 3/0;
//降级情况2.服务调用超时
try {
//2. 休眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public Goods findOne_fallback(int id){//findOne的降级方法。
Goods goods = new Goods();
goods.setTitle("降级了~~~");
return goods;
}
}
3.2 消费方降级
- feign 组件已经集成了hystrix组件。
- 定义feign调用接口实现类,
复写方法
,即降级方法
- 在
@FeignClient
注解中使用fallback 属性
设置降级处理类。 - 配置开启
feign.hystrix.enabled = true
实例操作
- 服务消费方的application.yml中添加下面代码:开启feign对hystrix的支持
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true
- 在GoodsFeignClient 接口上的
@FeignClient
注解中使用fallback 属性
设置降级处理类。
package com.itheima.consumer.feign;
import com.itheima.consumer.domain.Goods;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "HYSTRIX-PROVIDER",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
- 实现降级处理类
package com.itheima.consumer.feign;
import com.itheima.consumer.domain.Goods;
import org.springframework.stereotype.Component;
/**
* Feign 客户端的降级处理类
* 1. 定义类 实现 Feign 客户端接口
* 2. 使用@Component注解将该类的Bean加入SpringIOC容器
*/
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {
@Override
public Goods findGoodsById(int id) {
Goods goods = new Goods();
goods.setTitle("又被降级了~~~");
return goods;
}
}
3.3 Hystix降级小结
注意:
-
当服务提供方和服务的消费方都配置了降级时,当失败后
(出现异常、服务调用超时)
、只有服务提供方的降级会生效
。 -
原因:当出现异常,服务提供方已经降过级,降过级后,返回的数据是正常的数据。那么这时服务消费方就不用再次被降级。
4. Hystix-熔断
Hystrix 熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,不管请求降级与否,直到服务恢复正常为止。
- circuitBreaker.
sleep
WindowlnMilliseconds:监控时间 - circuitBreaker.
requestVolume
Threshold:失败次数 - circuitBreaker.
error
ThresholdPercentage:失败率
4.1 代码演示
在服务提供方(hystrix-provider)
controlloer包下的GoodsControlloer中写(默认该机制已开启)
。
package com.itheima.provider.controller;
import com.itheima.provider.domain.Goods;
import com.itheima.provider.service.GoodsService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* Goods Controller 服务提供方
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
//监控时间 默认5000 毫秒
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//失败次数。默认20次
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),
//失败率 默认50%
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")
})
public Goods findOne(@PathVariable("id") int id){
//如果id == 1 ,则出现异常,id != 1 则正常访问
if(id == 1){
//1.造个异常
int i = 3/0;
}
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public Goods findOne_fallback(int id){
Goods goods = new Goods();
goods.setTitle("降级了~~~");
return goods;
}
}
4.1 熔断监控
- Hystrix提供了
Hystrix-dashboard
功能,用于实时监控
微服务运行状态。 - 但是 Hystrix-dashboard 只能
监控一个微服务
。 - Netflix还提供了
Turbine
,进行聚合监控
。
聚合监控
- 使用Turbine ,进行聚合监控:SpringCloud基础知识【Hystrix熔断器–Turbine搭建步骤】
5. Hystix-限流
在高并发访问下,由于系统资源有限,必须对访问量进行控制。
- Hystrix提供了限流功能,在springcloud架构的系统中,可以在
网关启用Hystrix
,进行限流处理,每个微服务也可以各自启用
Hystrix进行限流。 - Hystrix默认使用线程隔离模式,可以通过线程数+队列大小进行限流
hystrix:
threadpool:
default:
coreSize: 200 #并发执行的最大线程数,默认10
maxQueueSize: 1000 #BlockingQueue的最大队列数,默认值-1
queueSizeRejectionThreshold: 800 #即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝,默认值5