RocketMQ压测问题分析

883 阅读2分钟

问题

在压测的过程中,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();
    }