Hystrix底层核心原理

news2024/7/29 20:41:24

1、Hystrix资源隔离技术

hystrix github 官方文档:Home · Netflix/Hystrix Wiki · GitHub

hystrix可以完成隔离、限流、熔断、降级这些常用保护功能。

hystrix的隔离分为线程池隔离和信号量隔离

1.1、信号量隔离

  • 信号量隔离就是hystrix的限流功能。虽然名字叫隔离,实际上它是通过信号量来实现的,而信号量说白了就是个计数器,当计数器计算达到设定的阈值,直接就做异常处理。
  • 而hystrix信号量隔离限制的是tomcat等Web容器线程数,一段时间仅仅能支持这么多,多余的请求再来请求线程资源,就被拒绝了。由于实际是通过隔离了部分容器线程资源,也算是一种隔离方式
  • 信号量隔离是同步的,所以不支持计算超时时间(需要自己手动实现)
  • 信号量隔离只是起了个限制作用,它的保护能力有限,如果下游服务有问题,长时间不返回结果,那也只能等着。因为本身信号量隔离对单个请求是起不到任何作用的,它只能限制请求过多问题(请求过多则拒绝,不让整个服务挂)

为了解决这个问题,hystrix又产生了线程池隔离。这种隔离方式是通过引入额外线程(这里的额外是相对于Tomcat的线程来说的,引入额外线程会造成额外开销)的方式。

1.2、线程池隔离

Hystrix采用额外的线程来对原来的web容器线程做管理控制,如果一个线程超时未返回,则熔断。既然引入额外的线程就涉及到线程池管理、线程的上下文切换这些额外的开销,所以相比信号量隔离,线程池隔离成本更高。

Hystrix可以为每一个依赖建立一个线程池,使之和其他依赖的使用资源隔离,同时限制他们的并发访问和阻塞扩张。每个依赖可以根据权重分配资源(这里主要是线程),每一部分的依赖出现了问题,也不会影响其他依赖的使用资源。

1

  • Hystrix使用Command模式对依赖调用进行封装,每一个Command就是一个上面所说的依赖,Command可以指定CommandKey
  • Command Group可以把一组Command归为一组,默认情况下,Hystrix会为每一个Command Group建立一个线程池
  • 每一个线程池都有一个Key,这个线程池就是线程隔离的关键,所有的监控、缓存、调用等等都来自于这个线程池,默认的Thread Pool key就是command group名称
  • 默认情况下,每一个Command Group会自动创建一个线程池,尽管如此,还是会有这样的情况:当有一些依赖在一个Command Group中,但是又有隔离的必要的时候(比如一个依赖的超时会用满所有的线程池线程,这样就会影响到其他的依赖),可以为组里的Command指定Thread Pool Key

1.3、Command模式

Command模式:Hystrix中大量使用rxjava来实现Command模式。所有自定义的Command,不管继承于HystrixObservableCommand还是HystrixCommand,最终都继承于AbstractCommand。

关于Command的初始化过程,后面会通过源码讲到。

1.4、如何选择隔离策略

信号量隔离:

  • 对于那些本来延迟就比较小的请求来说,线程池带来的开销是非常高的,因此用信号量隔离更好
  • 适用于不是对外部依赖的访问,因为对外部依赖的访问时长难以控制
  • 针对超大并发量的场景下,此时用线程池的话,可能撑不住那么高的并发,如果硬撑住,可能要耗费大量的线程资源,那么就用信号量,来进行限流保护

2、Hystrix 工作原理

2.1、流程

2.1.1、构造HystrixCommand或HystrixObservableCommand对象

返回单个响应,则:

HystrixCommand command = new HystrixCommand(arg1, arg2);

 
 

返回一个发出响应的可观测对象:

HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);

 
 

2.1.2、执行Command

有四种方式:

  • execute():阻塞,返回单个响应(或者在错误的情况下抛出一个异常)
  • queue():返回一个包含单个响应的Future
  • observe():订阅可返回响应的可观察对象,并且返回一个Observable(复制源Observable)
  • toObservable():返回一个可观察对象,当你订阅它时,它将执行Hystrix命令并发出它的响应

注意:

第一种方式和第二种方式只适用于HystrixCommand,不适用于HystrixObservableCommand

execute()实际上是调用queue().get()。而queue()实际上是调用toObservable(). toblocking (). tofuture()。也就是说,最终每个HystrixCommand都有一个可观察的实现支持,即使是那些打算返回单个简单值的命令


 
 
  1. K value = command.execute();
  2. Future<K> fValue = command.queue();
  3. Observable<K> ohValue = command.observe(); //热observable
  4. Observable<K> ocValue = command.toObservable(); //冷observable

2.1.3、是否缓存了响应

如果这个命令启用了请求缓存,并且缓存中有对请求的响应,那么这个缓存的响应将立即以可观察对象的形式返回

请求缓存位于线程执行之前,即construct()或run()方法调用的前面

如果Hystrix没有实现请求缓存,则我们需要在construct()或run()方法里面手动实现请求缓存,即线程执行之后

2.1.4、断路器是否打开

当你执行命令时,Hystrix检查断路器,看看电路是否打开。如果电路打开(或触发),那么Hystrix将不会执行命令,但会将流路由到(8)获得回退。

如果电路关闭,则流继续流到(5),以检查是否有可用的容量来运行命令。

2.1.5、线程池、队列、信号量容量是否饱满

如果使用线程池隔离,则检查线程池、队列容量是否饱满

如果使用信号量隔离,则检查信号量是否饱满

2.1.6、执行请求

  • HystrixCommand.run() — 返回单个响应或者抛出异常,然后发出一个onCompleted通知
  • HystrixObservableCommand.construct() —返回一个可发出响应的可观测对象或者发出一个onError的事件

2.1.7、计算电路是否健康

Hystrix向断路器报告成功、失败、拒绝和超时,断路器维护一组滚动计数器来计算统计数据。

它使用这些统计数据来确定电路何时应该“跳闸”,在这一点上它会短路任何后续请求,直到恢复期结束,在恢复期结束后,它会在第一次检查后再次关闭电路。

2.1.8、获取回退方法

  • 在HystrixCommand的情况下,为了提供回退逻辑,你需要实现HystrixCommand. getfallback(),它返回一个回退值
  • 在HystrixObservableCommand的情况下,要提供回退逻辑,你需要实现HystrixObservableCommand. resumewithfallback(),它返回一个可以发出回退值的可观察对象
  • 如果没有为Hystrix命令实现回退方法,或者回退本身抛出异常,那么Hystrix仍然会返回一个可观察对象,但它不发出任何东西,并立即以onError通知结束

如果没有实现回退,则不同的执行方法会有不同的现象:

  • execute():抛出异常
  • queue():会成功返回Future,但是如果调用了它的get()方法,Future将抛出异常
  • observe() :返回一个可观察对象,当你订阅它时,它将通过调用订阅者的onError方法立即终止
  • toObservable() :返回一个可观察对象,当你订阅它时,它将通过调用订阅者的onError方法来终止

2.1.9、返回成功的响应

3、Hystrix API


 
 
  1. <dependency>
  2. <groupId>com.netflix.hystrix </groupId>
  3. <artifactId>hystrix-core </artifactId>
  4. <version>1.5.18 </version>
  5. </dependency>

因为HystrixCommand是一个抽象类,所以我们需要继承它并重写run方法


 
 
  1. import com.netflix.hystrix.HystrixCommand;
  2. import com.netflix.hystrix.HystrixCommandGroupKey;
  3. public class CommandHelloWorld extends HystrixCommand<String> {
  4. private final String name;
  5. public CommandHelloWorld (String name) {
  6. super(HystrixCommandGroupKey.Factory.asKey( "HelloGroup"));
  7. this.name = name;
  8. }
  9. @Override
  10. protected String run () {
  11. return "Hello " + name + "!";
  12. }
  13. public static void main (String[] args) {
  14. String s = new CommandHelloWorld( "bobo").execute();
  15. System.out.println(s);
  16. }
  17. }

更多API使用方法请戳这里:How To Use · Netflix/Hystrix Wiki · GitHub

4、源码解读

关于HystrixCommand,包括HystrixObservableCommand,都是继承AbstractCommand的,而AbstractCommand有一个最核心的构造方法,该构造方法包含了该command的所有配置


 
 
  1. protected AbstractCommand (HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool,
  2. HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults,
  3. HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore,
  4. HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) {
  5. this.commandGroup = initGroupKey(group);
  6. this.commandKey = initCommandKey(key, getClass());
  7. this.properties = initCommandProperties( this.commandKey, propertiesStrategy, commandPropertiesDefaults);
  8. this.threadPoolKey = initThreadPoolKey(threadPoolKey, this.commandGroup, this.properties.executionIsolationThreadPoolKeyOverride().get());
  9. this.metrics = initMetrics(metrics, this.commandGroup, this.threadPoolKey, this.commandKey, this.properties);
  10. this.circuitBreaker = initCircuitBreaker( this.properties.circuitBreakerEnabled().get(), circuitBreaker, this.commandGroup, this.commandKey, this.properties, this.metrics);
  11. this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);
  12. //Strategies from plugins
  13. this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
  14. this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();
  15. HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand( this.commandKey, this.commandGroup, this.metrics, this.circuitBreaker, this.properties);
  16. this.executionHook = initExecutionHook(executionHook);
  17. this.requestCache = HystrixRequestCache.getInstance( this.commandKey, this.concurrencyStrategy);
  18. this.currentRequestLog = initRequestLog( this.properties.requestLogEnabled().get(), this.concurrencyStrategy);
  19. /* fallback semaphore override if applicable */
  20. this.fallbackSemaphoreOverride = fallbackSemaphore;
  21. /* execution semaphore override if applicable */
  22. this.executionSemaphoreOverride = executionSemaphore;
  23. }

而前面说到,Thread Pool,Command Group,Command Key都在AbstractCommand这里实现

而正好上面的代码中,其中有一行是这样的:

this.threadPool = initThreadPool(threadPool, this.threadPoolKey, threadPoolPropertiesDefaults);
 
 

这一行代码表示加载线程池,点进去看看

再点进去看看

点进去HystrixThreadPoolDefault的构造方法,看看做了什么

再点进去看看,就能看到最终创建线程池的代码了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/560415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业应该如何选择一个靠谱的软件测试供应商?

人们的生活越来越离不开软件产品&#xff0c;随着选择越多&#xff0c;产品质量愈发重要&#xff0c;因此企业选择一个靠谱的软件测试供应商是一项关键任务&#xff0c;因为测试结果将直接影响到产品的质量、用户的体验和公司的声誉。以下是一些选择靠谱的软件测试供应商的技巧…

C++常用的支持中文的GUI库Qt 6之二:项目的结构、资源文件的使用

C常用的支持中文的GUI库Qt 6之二&#xff1a;项目的结构、资源文件的使用 上一篇Qt 6的下载、安装与简单使用https://mp.csdn.net/mp_blog/creation/editor/130730203&#xff0c;本文介绍Qt 6的项目的结构、资源文件的使用与发布。 基础 这一部分&#xff0c;初学时不明白是…

交通 | 考虑网络效应的共享出行差异化定价

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/210182/ 编者按&#xff1a; 本文考虑了单程式共享汽车的定价问题&#xff0c;在考虑顾客需求网络效应以及实现影响的场景下&#xff0c;根据空间以及时间确定汽车租赁的单价以实现系统利润最大化。 1.引言 在过…

【C++】unordered_map unordered_set 练习题

文章目录 unordered系列关联式容器unordered_mapunordered_map的文档介绍unordered_map的构造接口使用: unordered_multimapunorder_map&&unorder_multimap对比:unordered_setunordered_set的文档介绍unordered_set的构造接口使用 unordered_multisetOJ练习961.在长度2…

( 回溯算法) 27. 移除元素 ——【Leetcode每日一题】

❓27. 移除元素 难度&#xff1a;简单 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以…

ip地址段分解与合并

1、为什么要分解和合并ip地址段 无他&#xff0c;工作需要嘛&#xff0c;谁没事去划分ip地址段 优点&#xff1a;可以节省大量的时间&#xff0c;减少算错的可能性 2、工具下载 下载链接&#xff1a; https://github.com/zhanhb/cidr-merger github在国内使用不太友好&#…

14、IIC主机控制--引脚软件模拟

时序图&#xff1a; 软件基于STM32 HAL库 IIC–定时器精确延时 软件用涉及到使用定时器做精确延时&#xff0c;可以参考我的文章–“CubeMx 定时器高精度延时” 延时使用的文件&#xff1a; tim.c /*********************************************************************…

Linux基础内容(21)—— 进程消息队列和信号量

Linux基础内容&#xff08;20&#xff09;—— 共享内存_哈里沃克的博客-CSDN博客 目录 1.消息队列 1.定义 2.操作 2.信号量 1.定义 2.细节 3.延申 4.操作 3.IPC的特点共性 1.消息队列 1.定义 定义&#xff1a;是操作系统提供的内核级队列 2.操作 msgget&#xff1a;…

Java实现MQTT传输协议通信

Java实现MQTT传输协议通信 1. MQTT1.1 概述1.2 发布和订阅模型1.3 客户端1.4 服务器1.5 订阅、主题、会话1.6 协议中的方法2. Java使用MQTT2.1 添加 pom 依赖2.3 订阅方2.4 发布方2.4 MQTT 连接创建方式2.4.1 普通 TCP 连接2.4.2 TLS/SSL 连接1. MQTT

java_day01_单元测试_配置文件

一、软件的生命周期 **软件的可行性分析:**分析该软件是否值的研发,会消耗多少成本,能带来多少的利益等分析 **需求分析:**分析该软件具体该具备有那些功能,产品经理与客户一起讨论 **软件设计:**该软件应该使用什么样的架构,用什么样的数据库,每个模块的具体功能 **程序编…

2023年8大黑客编程语言

以下是2023年最适合黑客攻击的8种编程语言的列表。 道德黑客被定义为合法进入各种网络的做法&#xff0c;目的是识别黑客可能利用的潜在弱点来访问网络。此类黑客攻击旨在在任何漏洞进入危险攻击者手中之前发现它们&#xff0c;然后及时修复它们以防止攻击。让我们进入文章&am…

【数字通信】PAM基带信号的功率谱原理推导详解

PAM信号可以说是最简单的数字通信信号,很多理论最初都是由该信号的表达式推导得到并进行拓展的,纵观各类数字信号的表达式,或多或少都有PAM信号的“影子”,也就是说PAM信号相关的理论知识是最基本的,很有必要搞清楚,本博客主要讨论PAM基带信号的功率谱的原理及推导过程,…

我干了8年测试,告诉你现在软件测试还能不能找到工作!

观点&#xff1a;如果你还是以前的思维来学习测试&#xff0c;那你肯定是找不到工作&#xff01; 我做测试工作有将近8年的时间&#xff0c;蚂蚁金服做过2年&#xff0c;因为加班太多离职了。目前在一家国企上市公司&#xff0c;一年能拿三四十个左右&#xff0c;对比头部互联…

系统集成项目管理工程师 下午 真题 及考点(2018年下半年)

文章目录 一&#xff1a;第4章 项目管理一般知识&#xff0c;项目管理办公室的职责。第6章 项目整体管理二&#xff1a;第5章 项目立项管理。第14章 项目采购管理&#xff0c;采购文件。第13章 项目合同管理&#xff0c;按项目 付款方式 划分的合同分类三&#xff1a;第9章 项目…

GB/T28181-2022针对H.265编码细化及技术实现

技术背景 新版国家标准GB/T28181-2022《公共安全视频监控联网系统信息传输、交换、控制技术要求》已于2022年12月30日发布&#xff0c;并将于2023年7月1日正式实施。 国家标准GB/T28181-2022《公共安全视频监控联网系统信息传输、交换、控制技术要求》规定了公共安全视频监控…

RocketMQ学习

各MQ 并发性能比较 吞吐量 kafka 17.3w/s rocketMQ 11.6w/s RabbitMQ 5.96w/s RocketMQ组件 broker 核心业务组件 nameServe 保存broker 的ip、端口、上下线信息等。 类似注册中心 启动nameServe 时会调用 runserver 启动broker &#xff0c;会默认读取/conf/broker.conf …

第3章“程序的机器级表示”:程序编码 和 数据格式

文章目录 3.2 程序编码3.2.1 机器级代码3.2.2 代码示例3.2.3 关于格式的注解 3.3 数据格式 3.2 程序编码 假设写一个 C 程序&#xff0c;有两个文件 p1.c 和 p2.c。然后用 Unix 命令行编译这段代码&#xff1a; unix> gcc -O2 -o p p1.c p2.c命令 gcc 表明的就是 GNU C 编…

嵌入式 Linux 入门(十一、make 和 MakeFile)

嵌入式 Linux 入门第十一课&#xff0c;Make 工具和 Makefile 的引入...... 矜辰所致目录 前言一、Linux 下多文件编译二、make 工具和 Makefile2.1 make 和 Makefile 是什么?2.2 通过 STM32 提前熟悉 Makefile2.3 GCC 与 make 的关系/区别&#xff1f; 三、一个简单的 Makefi…

〖Python网络爬虫实战㉙〗- Selenium案例实战(三)

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付…

OpenGL之着色器

文章目录 什么是着色器数据类型输入与输出Uniform三角形渐变色例子从文件中读取 什么是着色器 着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的&#xff0c;它包含一些针对向量和矩阵操作的有用特性。着色器的开头总是要声明版本&#xff0c;接着是输入和输…