一文彻底读懂 hystrix-go 源码(下)

简介: 一文彻底读懂 hystrix-go 源码

看看指针类型 rolling.Number:


1668518595810.jpg


rolling.Number 是真正存储各个执行事件状态信息的底层存储结构。它是如何只保存 10 秒内的信息的。


1668518608369.jpg


惊不惊喜?

newMetricExchange(name) 还有一个细节,会单独开启一个 g 运行 go m.Monitor() 去接收 channel 类型的 Updates 信息,即执行事件状态信息。


1668518623136.jpg


在接收到事件信息后,调用 IncrementMetrics 先做状态信息的整合,最终把整合后的执行状态事件信息上报 collector.Update(r)


1668518651426.jpg

1668518660188.jpg


接着回头看初始化流量控制中心 newExecutorPool(name)。先看看 executorPool 结构。


1668518670983.jpg


主要关注两个字段,Tickets 表示的就是访问令牌带缓冲通道的 channel ,初始化 channel 容量取决于一开始你设置的 MaxConcurrentRequests。当有请求到来时,从 channel 中拿出一个令牌,调用后重新归还。 poolMetrics 就是流量控制的具体指标。


1668518683394.jpg


Executed 表示当前桶已经处理的请求数量。此外在 newExecutorPool(name) 函数中,和刚才套路一样,启动一个 go m.Monitor() 专门去更新当前桶的最大值。


1668518701307.jpg

1668518712557.jpg


到这里,GetCircuit(name) 获取一个熔断器的代码讲完了。

回到 GoC ,我们得到一个 circuitBreakers 的指针。


1668518722758.jpg


接下来我们创建一个条件变量 sync.NewCond。条件变量的场景是当共享资源发生变化时,通知那些被互斥锁锁住的线程。

在这里它是用来协调通知你可以归还访问令牌了。


接着有一句 returnOnce := &sync.Once{}, 关于 sync.Once, 之前解析过一篇文章你真的了解 sync.Once 吗。这里它存在的意义是什么?我们往下看,就会发现其实到后面开启了两个 Goroutine。


1668518749919.jpg


它的作用是确保由最快那个 Goroutine 运行 errWithFallback()reportAllEvent(),而且保证只会执行一次。


接着往下看,


1668518762360.jpg


这个函数就是就是用来上报执行事件的。


1668518775128.jpg


前面都好懂,我们从这段开始看:


1668518786727.jpg


这里操作这句话是什么意思?因为存在一种情况:当前熔断器是开启的,并且已经过了 SleepWindow 时间,此时请求就属于半开的状态,允许尝试执行,如果执行成功,那么就说明服务恢复了,可以关闭熔断器了。接下来,


1668518826014.jpg


组装执行状态状态事件,然后塞进 Updates 通道中。正好被初始化 metricExchange 另开的 Goroutine 接收,这样,这个上报流程就对应上了。


接下来就是刚才截图的两个 Goroutine, 我们先看第一个。


1668518841555.jpg


截图了上前半部分。上来一个 defer func() { cmd.finished <- true }() 作为正常运行结束的通知。然后就是 cmd.circuit.AllowRequest() 判断是否能请求。


1668518857157.jpg


有两种情况可以往下走,第一种熔断器是关闭的。对应 circuit.IsOpen()


1668518889114.jpg


首先判断熔断器是否被强制开启或者已经开启,如果是,直接返回 true。否则说明是当前熔断器处于关闭。接着判断过去十秒内各个桶值的和是否小于设置的 RequestVolumeThreshold 值,如果小于,说明熔断器还应该是关闭状态,返回 false 。如果大于等于,那么应该进一步去判断错误百分比 是否超出自己的设置的 ErrorPercentThreshold。如果超出了,那么说明错误率过高,此时需要开启熔断器。


1668518907375.jpg


这段代码很好懂,有意思的是 int(errPct + 0.5)。+0.5 是为了四舍五入,如果直接浮点数转换成整形,那么必然就是去除小数点。加了 0.5,那么假设ErrorPercentThreshold 设置 50,如果是 <45.5, 熔断器就继续关闭,否则开启熔断器。


如果 circuit.IsOpen() 不符合,那么再看 circuit.allowSingleTest()。虽然熔断器是开启的,但是如果当前的时间已经大于 (上次开启熔断器的时间 +SleepWindow 的时间),这时候熔断器属于半开的状态,可以执行下一步。那么就会返回 true。


如果 cmd.circuit.AllowRequest 返回 false,那么就是执行 returnTicket 归还令牌 (尽管这时候还没有令牌可言)。这段代码很有趣,通过变量 ticketChecked 加 sync.NewCond 实现的逻辑。cmd.errorWithFallback(),上报熔断器已开启事件 (circuit open) 以及运行 fallBakck 保底函数 (如果存在的话),执行结束,响应。


1668518935745.jpg


如果返回 true,接着玩下走,


1668518949841.jpg


如果拿不到访问令牌,那么和刚才一样,上报当前请求已超过并发数事件 (max concurrency),运行保底操作,响应。


如果拿到访问令牌,那么真正执行自己的业务代码 run(ctx)。套路和上面相似。

再看第二个 Goroutine


1668518962173.jpg


三个 case 分别表示:正常执行结束、业务执行被取消以及超时。至于为什么说 Do 是同步操作,因为在 Doc 最后,


1668518978715.jpg


而 Go 则是一个异步过程。

到这里,从 Do 开始执行的大体流程就走完了。

最后我们可以直接给出它的大体流程图。


1668518998469.jpg


他们结构体之间的一些关系图。

1668519012208.jpg


相关文章
|
11月前
|
NoSQL IDE Go
Go 语言源码级调试器 Delve
Go 语言源码级调试器 Delve
88 0
|
2月前
|
缓存 监控 Java
Hystrix 源码解读
Hystrix 源码解读
25 2
|
10月前
|
存储 Cloud Native Go
Go语言 WaitGroup 源码知多少
Go语言 WaitGroup 源码知多少
|
4月前
|
存储 测试技术 Go
掌握Go语言:深入探究Go语言中的命令源码文件与参数处理技巧(3)
掌握Go语言:深入探究Go语言中的命令源码文件与参数处理技巧(3)
|
存储 SpringCloudAlibaba 监控
二十一.SpringCloud源码剖析-Hystrix的初始化
ystrix不是停更了吗?你在这写什么?是,Hystrix是停止更新版本了,说不定后面又继续更新了呢?比如阿里的dubbo不也是停更一段时间后又继续更新了么。Hystrix只是停止开发新的版本,并不是完全停止维护,有Bug依然会修复,Hystrix已经是比较稳定的,很多项目依旧在使用它。 再者说Hystrix是SpringCloud 第一代技术标准中的非常重要的一个组件,可以看做是我们学习SpringCloud全家桶的一个必不可少的过程。在实际项目中你当然可以使用Spring Cloud Alibaba 等更先进的技术来作为微服务架构,使用sentinel代替Hystrix,但是如果你只会使
|
4月前
|
Kubernetes Go 数据库
分享48个Go源码,总有一款适合您
分享48个Go源码,总有一款适合您
152 0
|
11月前
|
存储 安全 编译器
Go语言源码剖析-String和unsafe包
Go语言源码剖析-String和unsafe包
62 0
|
存储 缓存 安全
Go源码解析之chan.go
Go源码解析之chan.go
224 0
|
存储 缓存 监控
Go源码解析之proc.go
Go源码解析之proc.go
235 0
|
存储 搜索推荐 编译器
Go源码解析之select.go
Go源码解析之select.go
87 0