熔断、限流、降级 —— SpringCloud Hystrix
概述
Hystrix 为 微服务架构提供了一整套服务隔离、服务熔断和服务降级的解决方案。它是熔断器的一种实现,主要用于解决微服务架构的高可用及服务雪崩等问题
Hystrix 的特性如下:
- 服务熔断:Hystrix 熔断器就像家中的安全阀一样,一旦某个服务不可用,熔断器就会直接切断该链路上的请求,避免大量的无效请求影响系统稳定,并且熔断器有自我检测和恢复的功能,在服务状态恢复正常后会自动关闭
- 服务降级:Hystrix 通过 falback 实现服务降级,在需要进行服务降级的类中定义一个 falback 方法,当请求的远程服务出现异常时,可以直接使用 fallback 方法返回异常信息,而不调用远程服务
- 依赖隔离:Hyslrix 通过线程池和信号量两种方式实现服务之间的依赖隔离,这样即使其中一个服务出现异常,资源迟迟不能释放,也不会影响其他业务线程的正常运行
- 线程池的隔离策略:Hystrix 线程池的资源隔离为每个依赖的服务都分配一个线程池,每个线程池都处理特定的服务,多个服务之间的线程资源互不影响,以达到资源隔离的目标。当某个依赖服务异常时,只会阻塞这个依赖服务的线程资源,不影响其他依赖服务
- 信号量的隔离策略:Hystrix 信号量的隔离策略是为每个依赣的服务都分配一个信号量(原子计数器),当请求某个依赖服务时,先判断该服务的信号量值是否超过最大值。如果超过,则直接丢弃并返回错误提示,如果不超过,则在处理请求前执行“信号量+1”的操作,在请求返回后执行“信号量-1”的操作
- 请求缓存:Hystrix 按照请求参数把请求结果缓存起来,当后面有相同的请求时不会再走完整的调用链流程,而是把上次缓存的结果直接返回,以达到服务快速响应和性能优化的目的。同时,缓存可作为服务降级的数据源,当远程服务不可用时,直接返回缓存数据,对于消费者来说,只是可能获取了过期的数据,这样就优雅地处理了系统异常
- 请求合井:当微服务需要调用多个远程服务做结果的汇总时,需要使用请求合并。Hystrix 采用异步消息订阅的方式进行请求合并,当应用程序需要请求多个接口时,采用异步调用的方式提交请求,然后订阅返回值,应用程序的业务可以接着执行其他任务而不用阻塞等待,当所有请求都返回时,应用程序会得到一个通知,取出返回值合并即可
Hystrix 服务降级流程
- 当有服务请求时,首先会根据注解创建一个 HystrixCommand 指令对象,该对象设置了服务调用失败的场景和调用失败后服务降级的业务逻辑方法
- 熔斯器判断状态,当熔断器处于开路状态时,直接调用服务降级的业务逻辑方法返回调用失败的反馈信息
- 当熔断器处于半开路或者闭路状态时,服务会进行线程池和信号量等检查如果有可用资源,有则调用正常业务逻辑。如果调用正常业务逻辑成功,则返回成功后的息,如果失败,则调用降级的业务逻辑,进行服务降级
- 当熔断器处于半开路或者闭路状态时,如果在当前服务线程池和信号量中无可用资源,则执行服务降级的业务逻辑,返回调用失败的信息
- 当熔断器处于半开路状态并且本次服务执行失败时,熔断器会进入开路状态
- 当正常业务逻辑处理超时或者出现错误时,HystrixCommand 会执行服务降缓的业务逐辑,返回调用失败的信息
- 线程池和信号量的资源检查及正常业务逻辑会将自己的状态和调用结果反馈给监控,监控将服务状态反馈给熔断器,以便熔断器判断熔断状态
Hystrix 应用
Hystrix 的使用主要分为服务熔断、服务降级和服务监控三个方面
在 pom.xml 文件中引入 Hystrix 依赖,其中,spring-cloud-slarter-netflix-hystrix 和 hystrix-javanica 为 Hystrix 服务熔断所需的依赖,spring-cloud-netflix-hystrix-dashboard 为 Hystrix 服务监控所需的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
通过 @EnableHystrix 注解开启对服务熔断的支持,通过 @EnableHystrixDashboard 注解开启对服务监控的支持。注意,Hystrix 一般和服务发现配合使用,这里使 @EnableEurekaClient 开启了对服务发现客户端的支持
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
@EnableHystrixDashboard
public class HystrixServiceApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixServiceApplication.class, args);
}
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
配置 application.properties 文件
#服务名
spring.application.name=hystrix
#服务的端口
server.port=9005
#注册中心的地址
eureka.client.serviceUrl.defaultZone=http://localhost:9001/eureka/
eureka.client.registry-fetch-interval-seconds=30
服务熔断和降级,定义了一个远程调用的方法 hystrixHandler(),并通过 @HystrixCommand(fallbackMethod="exceptionHandler") 在方法上定义了一个服务降级的命令,当远程方法调用失败时,Hystrix 会自动调用 fallbackMethod 来完成服务熔断和降级,这里会调用 exceptionHandler 方法
@Autowired
private RestTemplate restTemplate;
//定义服务降级命令
@HystrixCommand(fallbackMethod = "exceptionHandler")
@RequestMapping(value = "/service/hystrix", method = RequestMethod.GET)
public String hystrixHandler() {
return restTemplate.getForEntity("http://EUREKA-CLIENT/serviceProducer", String.class).getBody();
}
public String exceptionHandler() {
return "提供者服务挂了";
}
异步处理
上节中的远程调用请求必须等到网络请求返回结果后,才会执行后面的代码,即阻塞运行。而在实际使用过程中,应用程序常常希望使用非阻塞 IO 来更优雅地实现功能.Hyslrix 为非阻塞 IO 提供了两种实现方式,分别是表示将来式的 Future 和表示回调式的 Callable
1. Future
定义 HystrixCommand
public class CommandFuture extends HystrixCommand<String> {
private String name;
private RestTemplate restTemplate;
public CommandFuture(String name, RestTemplate restTemplate) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
//1:通过 HystrixCommandKey 工厂定义依赖的名称
.andCommandKey(HystrixComnandKey.Factory.asKey("HelloWorld"))
//2:通过 HystrixThreadPoolKey 工厂定义线程池的名称
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
this.name = name;
this.restTemplate = restTemplate;
}
//3:定义远程调用的方法体
@Override
protected String run() {
String result = restTemplate.getForEntity("http://EUREKA-CLIENT/serviceProducer", String.class).getBody();
return result;
}
//4:服务降级的处理逻辑
@Override
protected String getFallback() {
return "远程服务异常";
}
}
以上代码通过继承 HystrixCommand 定义了一个 CommandFuture 来实现异步请求,其中,正常业务执行的逻辑在覆写的 run() 方法体中被执行,服务降级的方法在 getFallback() 中被执行。需要注意的是,这里使用 andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")) 实现了使用 HystrixCommandKey 工厂定义依赖的名称,每个 CommandKey 都代表一个依赖抽象,相同的依赖要使用相同的 CommandKey 名称。依赖隔离的本质敦是对相同
CommandKey 的依赖进行离,使用 andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")) 实现了基于 HystrixThreadPoolKey 工厂定义线程池的名称。当对同一业务的依赖进行资源隔离时,使用 CommandGroup 进行区分,但是当对同一依赖进行不同的远程调用时(例如,一个是 Redis 服务,一个是 HTTP 服务),则可以使用 HystrixThreadPoolKey 进行隔离区分
使用 HystrixCommand
@RequeatMapping(value = "/service/hystrix/future", method = RequestMethod.GET)
public String hystrixFutureHandler() throws ExecutionException, InterruptedException {
//定义基于Future的异步调用,请求会以队列的形式在线程池中被执行
Future<String> future = new CommandFuture("future", restTemplate).queue();
return future.get();
}
2. Callable
预定义一个回调任务,Callable 在发出请求后,主线程继续执行,在请求执行完成并返回结果后,Callable 会自动调用回调任务
定义 HystrixObservableCommand
public class CommandObservable extends HystrixObservabCommand<String> {
private String name;
private RestTemplate restTemplate;
public CommandObservable(String nane, RestTemplate restTemplate) {
super(HystrixConmandGroupKey.Factory.asKey("ExampleGroup"));
this.nane = name;
this.restTemplate = restTemplate;
}
//基于观察者模式的请求发布
@Override
protected Observable<String> construct () {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try {
//执行远程过程调用
if(!subscriber.isUnsubscribed()) {
String result = restTemplate.getForEntity("http://EUREKA-CLIENT/serviceProducer", String.class).getBody();
//将调用结果传递下去
subscriber.onNext(result):
subscriber.onCompleted();
}
} catch(Exception e) {
e.printStackTrace();
subscriber.onError(e);
}
}
}
}
//服务降级的处理逻辑
@Override
protected Observable<String> resumeWithFallback() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try {
if(!subscriber.isUnsubscribed()) {
subscriber.onNext("远程服务异常”);
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
}
}
}
以上代码定义了名为 CommandObservable 的类,该类继承自 HystrixObservableCommand 接口,并通过覆写 HystrixObservableCommand 接口中的 construct() 实现观察者模式。具体实现为通过 Obsenvable.create() 创建并返回一个 Observable 对象,在创建对象时,通过 new Observable.OnSubscribe() 实现消息的监听和处理。其中,call 方法用于消息的接收和业务的处理,在消息处理完成后通过 subscriber.onNext(result) 将调用结果传递下去,当所有任务都执行完成时通过 subscriber.onCompleted() 将总体执行结果发布出去。resumeWithFallback 方法是服务降级的处理逻辑,当服务出现异常时,通过 subscriber.onNext("远程服务异常") 进行服务熔断和异常消息的发布,实现服务降级处理
使用 HystrixObservableCommand
public String hystrixCallableHandler() throws ExecutionException, InterruptedException {
List<String> list = new ArrayList<>();
//定义基于消息订间的异步调用,请求结果会以事件的方式通知
Observable<String> observable = new CommandObservable("observer", restTemplate).observe();
//基于观察者模式的清求结果订阅
observable.subscribe(new Observer<String>() {
//onCompleted方法在所有请求完成后执行@
@Override
public void onCompleted() {
System.out.println("所有请求已经完成...”);
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
//订阅调用事件,请求结果汇聚的地方,用集合将返回的结果收集起来
@Override
public void onNext(String s) {
System.out.printin("结果来了...");
list.add(s);
return list.toString();
}
}
}
以上代码通过 new CommandObservable("observer", restTemplate).observe() 定义了一个实现服务发布的命令。通过调用 abservabe.subscribe() 来实现基于观察者模式的请求结果订阅,其中,订阅的数据结果在 onNext() 中被通知,总体调用结果在 onCompleted() 中被通知。服务处理异常结果在 onError() 中被通知
Hystrix Dashboard
HystrixDashboard 主要用于实时监控 Hystrix 的各项运行指标。通过 HystrixDashboard 可以查询 Hystrix 的实时信息,用于快速定位和发现问题。Hystrix Dashboard 的使用简单、方便,首先在 pom.xml 文件中加入 spring-cloud-netfix-hystrix-dashboard 依赖,然后使用 @EnableHystrixDashboard 注解开启 Dashboard 功能即可。在服务启动后,在浏览器地址栏中输人 http://127.0.0.1:9005/hystrix
,就可以看到监控界面
熔断、限流、降级 —— SpringCloud Hystrix的更多相关文章
- 微服务熔断限流Hystrix之流聚合
简介 上一篇介绍了 Hystrix Dashboard 监控单体应用的例子,在生产环境中,监控的应用往往是一个集群,我们需要将每个实例的监控信息聚合起来分析,这就用到了 Turbine 工具.Turb ...
- 限流降级神器,带你解读阿里巴巴开源 Sentinel 实现原理
Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性. 大家可能会问:Se ...
- 阿里熔断限流Sentinel研究
1. 阿里熔断限流Sentinel研究 1.1. 功能特点 丰富的应用场景:例如秒杀(即突发流量控制在系统容量可以承受的范围).消息削峰填谷.集群流量控制.实时熔断下游不可用应用等 完备的实时监控:S ...
- Spring Cloud Alibaba系列(五)sentinel实现服务限流降级
一.sentinel是什么 sentinel的官方名称叫分布式系统的流量防卫兵.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性.在Spring Clou ...
- SpringBoot进阶教程(六十八)Sentinel实现限流降级
前面两篇文章nginx限流配置和SpringBoot进阶教程(六十七)RateLimiter限流,我们介绍了如何使用nginx和RateLimiter限流,这篇文章介绍另外一种限流方式---Senti ...
- 代理层Nginx限流(降级)预案
典型服务架构介绍 预案适用场景 监控指标 操作手册 相关文档 操作方法 配置语法 配置样例 配置解释 注意事项 典型服务架构介绍 典型的互联网服务访问链路都是分层结构的,从流量入口,到应用层,到后端资 ...
- Envoy熔断限流实践(二)Rainbond基于RLS服务全局限流
Envoy 可以作为 Sevice Mesh 微服务框架中的代理实现方案,Rainbond 内置的微服务框架同样基于 Envoy 实现.本文所描述的全局限速实践也是基于 Envoy 已有的方案所实现. ...
- 微服务熔断限流Hystrix之Dashboard
简介 Hystrix Dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard可以直观地看到各Hystrix Command的请求响应时间,请求成功率等数据 ...
- Envoy熔断限流实践(一)基于Rainbond插件实现熔断
Envoy 可以作为 Sevice Mesh 微服务框架中的代理实现方案,Rainbond 内置的微服务框架同样基于 Envoy 实现.本文所描述的熔断实践基于 Rainbond 特有的插件机制实现. ...
- springcloud组件之hystrix服务熔断,降级,限流
hystrix 简介 Hystrix是什么 在分布式环境中,许多服务依赖项中的一些必然会失败.Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互.Hystrix通过 ...
随机推荐
- 全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装
全网最适合入门的面向对象编程教程:06 类和对象的 Python 实现-自定义类的数据封装 摘要: 本文我们主要介绍了数据封装的基本概念和特性,如何设置自定义类的私有属性和私有方法,protect 属 ...
- PHP易混淆函数的区别及用法汇总(函数和方法的区别)
1.echo和print的区别PHP中echo和print的功能基本相同(输出),但是两者之间还是有细微差别的.echo输出后没有返回值,但print有返回值,当其执行失败时返回flase.因此可以作 ...
- OI生涯回忆&退役之后
一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的进程 --<庄子·秋水> 好吧,现在是2024年7月24日,我现在正坐在某编程机构的办公室电脑旁,写下这些文字,是啊,我已经退役将近两 ...
- 对比python学julia(第二章)--(第三节)玫瑰曲线—数学之美
3.1.问题描述 在数学世界中有一些美丽的曲线图形,有螺旋线.摆线.双纽线.蔓叶线且.心脏线.渐开线.玫瑰曲线.蝴蝶曲线-- 这些形状各异.简有繁别的数学曲线图形为看似枯燥的数学公式披上精彩纷呈的美丽 ...
- 【Vue】单元格合并,与动态校验
效果要求 先看需求效果: 多个数据授权项,配置的时候,业务名称大多数都是一样的,需要合并单元格处理 在elementUI组件文档中有说明[合并列行]: https://element.eleme.io ...
- 【SpringMVC】 Controller接收深度复杂对象封装不到的问题
首先来看数据结构的定义: 一个Form对象,然后里面有一个排版日期对象的List集合 排班集合的每个元素中又有一个String集合 在前端的Post请求中可以看到这个String集合是传递了的 但是D ...
- 【MySQL】Navicat踩坑:Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_general_ci,IMPLICIT) for operation 'instr'
在Navicat客户端上面执行SQL报错 SQL语句: WITH RECURSIVE transfer (start_station, stop_station, stops, path) AS ( ...
- 如何理解自动驾驶的分级:L0、L1、L2、L3、L4、L5
相关: https://baijiahao.baidu.com/s?id=1792281493472406727&wfr=spider&for=pc L0,就是完全没有自动驾驶技术的车 ...
- 使用GPU计算时,单精度float32类型和半精度float16类型运算效率的区别
最近在看资料时发现写着使用float16 半精度类型的数据计算速度要比float32的单精度类型数据计算要快,因为以前没有考虑过数据类型对计算速度的影响,只知道这个会影响最终的计算结果精度.于是,好奇 ...
- 分布式深度学习计算框架依赖环境——NCCL的安装
分布式深度学习计算框架(MindSpore, PyTorch)依赖环境--NCCL, NCCL提供多显卡之间直接进行数据交互的功能(可以跨主机进行). 注意: 本文环境为 Ubuntu18.04 以 ...