Hystrix 源码解析 —— 执行结果缓存
来源:互联网 发布:传奇盛世注灵数据 编辑:程序博客网 时间:2024/06/08 02:35
摘要: 原创出处 http://www.iocoder.cn/Hystrix/command-execute-result-cache/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Hystrix 1.5.X 版本
- 1. 概述
- 2. 好处
- 3. Observable#defer(…)
- 4. AbstractCommand#toObservavle(…)
- 5. HystrixCachedObservable
- 6. HystrixCommandResponseFromCache
- 666. 彩蛋
1. 概述
本文主要分享 Hystrix 执行命令的结果缓存。
建议 :对 RxJava 已经有一定的了解的基础上阅读本文。
Hystrix 执行命令整体流程如下图:
FROM 《【翻译】Hystrix文档-实现原理》「流程图」
- 红圈 :在 《Hystrix 源码解析 —— 执行命令方式》 有详细解析。
- 紫圈 :在
#toObservable()
方法里,如果请求结果缓存这个特性被启用,并且缓存命中,则缓存的回应会立即通过一个 Observable 对象的形式返回;如果缓存未命中,则返回【订阅了执行命令的 Observable】的 ReplySubject 对象缓存执行结果。- ReplySubject 能够重放执行结果,从而实现缓存的功效。本文不对 ReplySubject 做太多拓展,感兴趣的同学可以阅读 《ReactiveX/RxJava文档中文版 —— Subject》 。
在官方提供的示例中,我们使用 CommandUsingRequestCache 进行调试 。
推荐 Spring Cloud 书籍:
- 请支持正版。下载盗版,等于主动编写低级 BUG 。
- 程序猿DD —— 《Spring Cloud微服务实战》
- 周立 —— 《Spring Cloud与Docker微服务架构实战》
- 两书齐买,京东包邮。
2. 好处
点击 《【翻译】Hystrix文档-实现原理》「请求缓存」 ,查看对请求缓存的好处分享,写的真的很赞。
3. Observable#defer(…)
本小节为拓展内容,源码解析 RxJava ( 非 Hystrix ) 的 Observable#defer(...)
的方法实现。考虑到 Hystrix 大量使用,为了更好的理解,解析下源码。
《RxJava 源码解析 —— Observable#defer(…)》
4. AbstractCommand#toObservavle(…)
AbstractCommand#toObservavle(...)
方法,代码如下 :
- 第 2 行 :
_cmd
指向当前命令对象,用于下面实现 FuncX ,ActionX 内部类使用。 - 第 11 至 19 行 :当缓存特性未开启,或者缓存未命中时,使用
applyHystrixSemantics
传入Observable#defer(...)
方法,声明执行命令的 Observable。 第 25 行 :声明缓存 Observable 。Hystrix 执行命令的 Observable 声明关系如下:
第 29 至 33 行 :一条命令只能执行一次。
- 第 36 行 :记录命令开始时间戳。
- 第 38 至 44 行 :TODO【2001】【打印日志】
- 第 47 至 48 行 :缓存存开关、KEY 。
- 第 52 至 58 行 :如果请求结果缓存这个特性被启用,并且缓存命中,则缓存的回应会立即通过一个 Observable 对象的形式返回。
- 第 53 行 :
requestCache
缓存,在 TODO 【2008】【请求缓存】 详细解析。 - 第 53 行 :「6. HystrixCommandResponseFromCache」 详细解析。
- 第 56 行 :
#handleRequestCacheHitAndEmitValues(...)
方法,在第 78 行详细解析。
- 第 53 行 :
- 第 61 至 63 行 :获取执行命令的 Observable 。在 《Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑》 详细解析。
- 第 68 至 81 行 :当缓存特性开启,并且缓存未命中时,创建【订阅了执行命令的 Observable】的 HystrixCommandResponseFromCache 。
- 第 69 至 72 行 :创建 HystrixCommandResponseFromCache ,并添加到
requestCache
。哟,HystrixRequestCache#putIfAbsent(...)
方法,多个线程添加时,只有一个线程添加成功。 - 第 73 至 77 行 :添加失败的线程( 们 ):
- 第 75 行 :调用
HystrixCommandResponseFromCache#unsubscribe()
方法,取消 HystrixCommandResponseFromCache 的订阅。这一步很关键,因为我们不希望缓存不存在时,多个线程去执行命令,最好有且只有一个线程执行命令。在 「5. HystrixCachedObservable」 详细解析。 - 第 77 行 :「6. HystrixCommandResponseFromCache」 详细解析。
- 第 80 行 :调用
HystrixCommandResponseFromCachetoObservable()
方法,获得缓存 Observable 。
- 第 80 行 :调用
- 第 75 行 :调用
- 第 69 至 72 行 :创建 HystrixCommandResponseFromCache ,并添加到
- 第 82 至 84 行 :当缓存特性未开启,使用执行命令 Observable 。
- 第 87 至 91 行 :在返回的 Observable 上,订阅一些清理的处理逻辑。对这几个方法有疑惑的同学,可以阅读 《给 Android 开发者的 RxJava 详解》「 3) Subscribe (订阅) 」 。
5. HystrixCachedObservable
com.netflix.hystrix.HystrixCachedObservable
,缓存 Observable 。
HystrixCachedObservable 构造方法,代码如下 :
第 17 至 19 行 :实际上,HystrixCachedObservable 不是一个 Observable 的子类,而是对传入的 Observable 封装 :使用 ReplaySubject 向传入的 Observable 发起订阅,通过 ReplaySubject 能够重放执行结果,从而实现缓存的功效。这里有几个卡到笔者的并且很有趣的点,我们一一道来 :从上文中,我们可以看到,传入的
originalObservable
为hystrixObservable
执行命令 Observable 。在 Hystrix 里,提供了两种执行命令的隔离方式 :线程池(THREAD
) 和信号量(SEMAPHORE
)。- 当使用
THREAD
隔离时,#subscribe(replaySubject)
调用完成时,实际命令并未开始执行,或者说,这是一个异步的执行命令的过程。那么,会不会影响返回执行结果呢?答案当然是不会,BlockingObservable 在得到执行完成才会结束阻塞,此时已经有执行结果。 当使用
SEMAPHORE
隔离时,#subscribe(replaySubject)
调用完成时,实际命令已经执行完成,所以即使AbstractCommand#toObservavle(...)
的第 75 行 :调用HystrixCommandResponseFromCache#unsubscribe()
方法,也会浪费,重复执行命令。而对于THREAD
隔离的情况,通过取消订阅的方式,只会执行一次命令。当然,如果“恶搞”THREAD
隔离的情况,增加sleep
的调用如下,就能达到重复执行命令的效果。
- 当使用
第 21 至 36 行 :TODO 【2006】【outstandingSubscriptions】原子性没问题么?历史版本使用的是 AtomicInteger 。
HystrixCachedObservable 的其他方法,点击 链接 查看。
6. HystrixCommandResponseFromCache
com.netflix.hystrix.HystrixCommandResponseFromCache
,是 HystrixCachedObservable 的子类。在父类的基础上,增加了对 AbstractCommand.executionResult
的关注。
HystrixCachedObservable#from(Observable, AbstractCommand)
方法,创建 HystrixCommandResponseFromCache 对象,点击 链接 查看。
HystrixCommandResponseFromCache#toObservableWithStateCopiedInto(...)
方法,点击 链接 查看。
- 通过
completionLogicRun
属性,保证#doOnError()
,#doOnCompleted()
,#doOnUnsubscribe()
方法有且只有一个方法执行具体逻辑。#doOnError()
,#doOnCompleted()
执行时,调用#commandCompleted()
方法,从缓存命令(HystrixCommandResponseFromCache.originalCommand
) 复制executionResult
属性给当前命令(commandToCopyStateInto
) 。#doOnUnsubscribe()
执行时,调用#commandUnsubscribed()
方法,使用当前命令(commandToCopyStateInto
)自己的executionResult
,不进行复制。
- 熔断器 Hystrix 源码解析 —— 执行结果缓存
- Hystrix 源码解析 —— 执行结果缓存
- 熔断器 Hystrix 源码解析 —— 执行命令方式
- Hystrix 源码解析 —— 命令合并执行
- Hystrix 源码解析 —— 执行命令方式
- 熔断器 Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑
- 熔断器 Hystrix 源码解析 —— 命令执行(二)之执行隔离策略
- Hystrix 源码解析 —— 命令执行(一)之正常执行逻辑
- Hystrix 源码解析 —— 命令执行(二)之执行隔离策略
- Hystrix 源码解析 —— 命令执行(三)之执行超时
- 熔断器 Hystrix 源码解析 —— 命令执行(三)之执行超时
- Hystrix 源码解析 —— 断路器 HystrixCircuitBreaker
- Hystrix 源码解析 —— 请求执行(四)之失败回退逻辑
- 熔断器 Hystrix 源码解析 —— 请求执行(四)之失败回退逻辑
- 熔断器 Hystrix 源码解析 —— 调试环境搭建
- Hystrix源码解析 —— 调试环境搭建
- Mybatis3源码分析(17)-Sql解析执行-缓存的实现
- OkHttp源码解析(五)——cache缓存
- 正则表达式汇总
- tomcat读取浏览器中文乱码问题
- 深度解读单独的排名并非seo的全部
- flume读取日志数据写入kafka 然后kafka+storm整合
- 水平分库分表的关键步骤和技术难点
- Hystrix 源码解析 —— 执行结果缓存
- 准确率 召回率 F值 ROC AUC
- MyEclipse+Tomcat+MAVEN+SVN项目完整环境搭建
- 关于greenplum
- 输入三个数从大到小排序
- [数据库] 第一范式、第二范式、第三范式、BC范式
- mybatis----常见面试题
- 阿里云 物联网套件 MQTT Swift 搭建-基于Tcp连接-域名连接模式
- 机器学习面试问题集