RocketMQ源码解析之broker(FastFailure)
原创不易,转载请注明出处
文章目录
- 1.FastFailure
- 1.1 FastFailure是啥
- 1.2 什么时候会发生
- 2.源码分析
- 总结
1.FastFailure
1.1 FastFailure是啥
FastFailure从字面意思上就能猜出来,其实就是快速失败,什么是快速失败呢,就是broker感觉自己不行(繁忙)的时候,接下来的一些请求就会直接返回失败,如果有用过dubbo的小伙伴,可能会知道dubbo服务提供者端有个快速失败机制,就是线程池满的时候,再来调用请求就会直接拒绝掉,为啥会有这个机制呢?其实是为了保障broker或者是服务的稳定性与可用性,不然broker或者是服务非常繁忙的时候,处理请求很慢,还哗哗的一堆堆请求不停的打过来,容易将broker或者是服务干宕机。
1.2 什么时候会发生
我们上面说过,当broker感觉自己不行的时候会有快速失败的机制,那什么时候会感觉到不行呢,其实就是OSPageCacheBusy
,我们都知道commitlog 是单线程顺序追加写的,要想实现单线程顺序追加写,就得在追加写之前获取这个锁,这个OSPageCacheBusy
就是按照某个线程持有锁的时间算出来的,当一次写入持有锁时间1s以上,RocketMQ就会认为OSPageCacheBusy
,这个时候就会开启FastFailure机制,将来的请求给快速拒绝掉。
现在想想,什么时候会造成追加写入commitlog很慢?下面是我总结出来的2个原因。
- 内存爆了的时候,jvm进行垃圾回收,stop the world ,然后会造成整个写入过程慢。
- commitlog这个使用的mmap内存文件映射,内存文件映射你可以理解为将文件映射到内存中,文件中的每一个字节都在内存中有对应,然后你操作内存就相当于操作文件,也就是说你往mmap写入的话,其实就是往操作系统的vfs层的pagecache里面写入,但是我们都知道你操作的内存是一层虚拟内存,会将内存划分为4k一个个的内存页,虽然你做了mmap内存映射,实际上操作系统可能并没有为你某个位置分配一个真实的物理内存页,这个时候你往里面写入数据的话,操作系统发现写入的那个虚拟内存页没有对应的物理内存页,这个时候就会请求调页,给你分配一个内存页,如果你内存不足了,或者是正在swap交换内存,这个时候写入os pagecache就会缓慢,造成波动(这块内容涉及到操作系统的一些知识)
2.源码分析
在BrokerController这个类实例化的时候会创建一个BrokerFastFailure 组件,然后启动BrokerController 的时候,就会启动BrokerFastFailure 这个组件,我们一起来看下它的启动
可以看到创建一个定时任务,然后每10ms,也就是10毫秒执行一次,判断如果开启这个快速失败的话,就执行cleanExpiredRequest方法进行清理,这个默认是开启的,我们看下这个cleanExpiredRequest 方法实现
可以看到上面有个while循环,也就是我用红框框起来的这块代码,如果是os pagecache一直繁忙的话,就会一直执行这个循环里面的代码,如果sendThreadPoolQueue(这个就是发送消息的队列,当broker收到消息生产者发送消息的请求后,会交给线程池,然后线程处理不过来的就放到这个队列中排队等待处理,这个是线程池的一个知识)不是空的话,就会从这个队列里面取出任务,然后直接返回SYSTEM_BUSY
系统繁忙,[PCBUSY_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d
这串错误信息最好有个印象,最起码看到它能知道是啥原因。
我们看下判断os pagecache繁忙的代码。
其实就是在锁内的时间超多1s,当前时间减去获取锁的时间(这个获取锁的时间是某个写入消息线程获取锁的开始时间),就会任务是os pagecache 繁忙,当这个锁被释放,就会正常,会走下面这一堆清理队列中超时任务代码
我们可以看到清理的队列有SendThreadPoolQueue,超时时间是200ms,发送消息请求就会被放到这个队列中;PullThreadPoolQueue,超时时间是5000ms,拉取消息请求会在这个队列中;HeartbeatThreadPoolQueue,超时时间是31s,心跳请求放会被放到这个队列中;EndTransactionThreadPoolQueue,超时时间是3s,提交事务,回滚事务的请求就会被放到这个队列中。
我们来看下具体怎么清理的
其实就是遍历队列,然后取出看看创建时间,如果超时了,就会从队列中移除,返回系统繁忙状态码SYSTEM_BUSY
,然后这个提示消息最好有个印象[TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d
,看到后最起码知道是咋回事。好了,到这我们源码分析就结束了。
总结
本文主要介绍了一下RocketMQ broker 里面的FastFailure机制是啥,以及造成这个原因,然后从源码的角度看了一下这个FastFailure 的实现。
还没有评论,来说两句吧...