问题
在压测的过程中,RocketMQ的生产端,出现异常,返回信息为[REJECTREQUEST]system busy, start flow control for a while。
问题分析
通过在RocketMQ的源码中做全局搜索(没有代码的同学可以去GitHub拉一份),我们能够定位出,暴露问题的代码在类NettyRemotingAbstract:
final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessor : matched;
final int opaque = cmd.getOpaque();
...
...
...
if (pair.getObject1().rejectRequest()) {
final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,
"[REJECTREQUEST]system busy, start flow control for a while");
response.setOpaque(opaque);
ctx.writeAndFlush(response);
return;
}
这段代码的逻辑是,系统开始当前处理请求时,先计算处理上一个请求所需要的时间,如果如果处理时间超过默认的配置,则认为系统繁忙,会拒绝请求,并给生产端返回消息:[REJECTREQUEST]system busy, start flow control for a while。这段文字解释是比较粗糙的,后面主要厘清这样几个问题。是RocketMq的哪个模块在处理问题?处理的是什么请求?处理时间长的原因是什么?如何解决这个问题?
处理的请求
消息是由Producer端产生,由Broker消费的。这里就必须说明上文中提到的Pair,Pair主要由两类对象组合而成,分别是消息处理器(SendMessageProcessor)和对应的消息处理器线程池管理服务对象(ExecutorService),Broker在启动的时候,会将,PullMessageProcessor的作用是,将Producer生产的消息写入内存中,这个工作由CommitLog对象完成,在写入内存之前,记录一个时间,在完成内存中的写入后,将起始时间归0。当有新的消息请求由Producer发送后,Broker查看上一个请求耗费的时间,如果耗费的时间超过默认值,则返回:[REJECTREQUEST]system busy, start flow control for a while。
这里有一个设计很巧妙,我们看一下内存读写忙的判断语句:
public boolean isOSPageCacheBusy() {
long begin = this.getCommitLog().getBeginTimeInLock();
long diff = this.systemClock.now() - begin;
//diff小于10000000的判断,作用在于如果长时间没有消息请求,现在突然有了一条消息过来,时间差必然超过10000000
//如果没有 diff < 10000000,则刚刚的情况很会被误判为上一条请求超时
return diff < 10000000
&& diff > this.messageStoreConfig.getOsPageCacheBusyTimeOutMills();
}