如何从微小细节着手,参与开源贡献(二)

lemonvita
发布于 2022-7-19 10:41
浏览
0收藏

自适应算法


本来我以为看到这位大佬的测试已经是非常有收获了,没想到接下去的闲逛又让我发现了一个更了不得的东西。

 

既然上面分析出来,在QPS比较高的情况下,收益才能抵消被抵消,那么有没有可能实现一个自适应的算法,在QPS较低的时候直接从系统获取,QPS较高时,从缓存获取。

 

果不其然,Sentinel(Java版,版本>=1.8.2)已经实现了!

 

issue参考:https://github.com/alibaba/Sentinel/pull/1746


我们捋一下它的实现:如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区我们首先看最核心的缓存时间戳的循环(每毫秒执行1次),在这个循环中,它将缓存时间戳分成了三个状态:

 

• RUNNING:运行态,执行缓存时间戳策略,并统计写时间戳的QPS(把对缓存时间戳的读写QPS分开统计)
• IDLE:空闲态(初始状态),什么都不做,只休眠300毫秒
• PREPARE:准备态,缓存时间戳,但不统计QPS


这三个状态怎么流转呢?答案在开头调用的check方法中:如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区首先check逻辑有个间隔,也就是每隔一段时间(3秒)来做一次状态转换;

 

其次如果当前状态是空闲态并且读QPS大于HITS_UPPER_BOUNDARY(1200),则切换为准备态

 

如果当前状态是运行态且读QPS小于HITS_LOWER_BOUNDARY(800),则切换为空闲态

 

发现似乎少了切换到运行态的分支,看上面的循环中,第三个准备态的分支运行一次就将状态切换为运行态了。

 

这是为啥?其实准备态只是为了让程序从空闲态切换到运行态时过渡的更平滑,因为空闲态下缓存时间戳不再更新,如果没有过渡直接切换到运行态,那可能切换后获取的时间戳是有误差的。

 

文字可能不直观,我们画一个状态流转图:如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区最后这些准备好了,获取时需要做两件事:一是统计读时间戳的QPS,二是获取时间戳;如果是空闲态准备态则直接获取系统时间返回,如果是运行态则从缓存中拿时间戳。如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区当程序比较空闲时,不会缓存时间戳,降低CPU的消耗,QPS较高时缓存时间戳,也能降低CPU的消耗,并且能降低获取时间戳的时延,可谓是一举两得。

 

但这中间我有个疑问,这里QPS的高低边界不知道是如何得出的,是拍脑袋还是压测出来的,不过这个数值似乎并不一定绝对准确,可能和机器的配置也有关系,所以我倾向这个值可以配置,而不是在代码中写死,关于这点,这段代码的作者也解释了原因:如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区最后可能你会问,这QPS咋统计呀?

 

这可是Sentinel的强项,利用LeapArray统计,由于这不是本文重点,就不展开了,有兴趣可以参考我之前的文章《Sentinel-Go 源码系列(三)滑动时间窗口算法的工程实现》,虽然文章是Go的,但算法和Java的是一模一样,甚至实现都是照搬。

 

有没有测试数据支撑呢?有另一位大佬在评论区贴出了他的测试数据,我们看一下:如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区在低负载下,CPU消耗降低的特别明显,高负载则没什么变化,这也符合我们的预期。

 

看到这里你是不是觉得该点题了?没错,Sentinel-Go还没实现上述的自适应算法,这是个绝佳的机会,有技术含量,又有参考(Java版),是不是心动了?

 

社区中也有该issue:

 

https://github.com/alibaba/sentinel-golang/issues/419
如何从微小细节着手,参与开源贡献(二)-鸿蒙开发者社区这个issue在2021年8月有个哥们认领了,但截止目前还没贡献代码,四舍五入等于他放弃了,所以你懂我意思吧?

 

最后说一句


如果你觉得文章还可以,麻烦动动小手,点个关注在看,你的鼓励是我持续创作的动力!

 

对了,如果觉得还不过瘾,可以再看看这些相关文章:

 

《参与开源项目很难吗?》
《Sentinel-Go 源码系列(一)|开篇》
《Sentinel-Go 源码系列(二)|初始化流程和责任链设计模式》
《Sentinel-Go 源码系列(三)滑动时间窗口算法的工程实现》
《Sentinel在docker中获取CPU利用率的一个BUG》
《低开销获取时间戳》


感谢阅读,我们下期再见~

标签
已于2022-7-19 10:41:37修改
收藏
回复
举报
回复
    相关推荐