RocketMQ(七)broker接收消息入口源码

文章详细介绍了RocketMQBroker如何处理Producer发送的消息,包括消息处理器的注册、NettyServerHandler如何分发请求,以及processorRequestCommand方法处理请求的流程。重点分析了SendMessageProcessor在处理发送消息请求中的作用,包括检查系统资源是否允许处理新请求以及实际的请求处理逻辑。
摘要由CSDN通过智能技术生成

此前梳理了RocketMQ的Producer发送消息的源码,首先会查找topic的发布信息,然后找到一个消息队列MessageQueue,默认是轮询的选择,MessageQueue中存储着对应的brokerName,通过brokerName就能找到具体的brokerIP,随后获取Producer客户端与这台broker的channel,随后就可以向这台broker发送消息了,注意消息只会被发送到主broker中,即Master节点。

以上就是RocketMQ的Producer发送消息的大概流程,下面接着看RocketMQ的broker接收消息,处理请求的源码。

1 broker处理请求入口

1.1 registerProcessor注册消息处理器

RocketMQ的各种组件的网络通信都是基于Netty实现的, 我们在此前梳理RocketMQ的broker的启动源码的时候,会发现broker在启动的时候在brokerController#registerProcessor方法中会注册很多的netty消息处理器,不同的消息处理器可以处理不同的消息类型。

    /**
     * BrokerController的方法
     * 注册netty消息处理器
     *
     * 从这里的源码能够看出来,除了pullMessageProcessor处理器只会被注册到remotingServer之外,
     * 其他处理器会被注册到remotingServer和fastRemotingServer这两个netty服务中。
     *
     * 所以Vip通道服务不能够处理拉取消息的请求
     */
    public void registerProcessor() {
        /*
         * SendMessageProcessor
         */
        /**
         * 发送消息处理器
         */
        sendMessageProcessor.registerSendMessageHook(sendMessageHookList);
        sendMessageProcessor.registerConsumeMessageHook(consumeMessageHookList);

        //对于发送类型的请求,使用发送消息处理器sendProcessor来处理
        this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE, sendMessageProcessor, this.sendMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.SEND_MESSAGE_V2, sendMessageProcessor, this.sendMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.SEND_BATCH_MESSAGE, sendMessageProcessor, this.sendMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.CONSUMER_SEND_MSG_BACK, sendMessageProcessor, this.sendMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SEND_MESSAGE, sendMessageProcessor, this.sendMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SEND_MESSAGE_V2, sendMessageProcessor, this.sendMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SEND_BATCH_MESSAGE, sendMessageProcessor, this.sendMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.CONSUMER_SEND_MSG_BACK, sendMessageProcessor, this.sendMessageExecutor);
        /**
         * PullMessageProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.PULL_MESSAGE, this.pullMessageProcessor, this.pullMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.LITE_PULL_MESSAGE, this.pullMessageProcessor, this.litePullMessageExecutor);
        this.pullMessageProcessor.registerConsumeMessageHook(consumeMessageHookList);
        /**
         * PeekMessageProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.PEEK_MESSAGE, this.peekMessageProcessor, this.pullMessageExecutor);
        /**
         * PopMessageProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.POP_MESSAGE, this.popMessageProcessor, this.pullMessageExecutor);

        /**
         * AckMessageProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.ACK_MESSAGE, this.ackMessageProcessor, this.ackMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.ACK_MESSAGE, this.ackMessageProcessor, this.ackMessageExecutor);
        /**
         * ChangeInvisibleTimeProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, this.changeInvisibleTimeProcessor, this.ackMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, this.changeInvisibleTimeProcessor, this.ackMessageExecutor);
        /**
         * notificationProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.NOTIFICATION, this.notificationProcessor, this.pullMessageExecutor);

        /**
         * pollingInfoProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.POLLING_INFO, this.pollingInfoProcessor, this.pullMessageExecutor);

        /**
         * ReplyMessageProcessor
         */

        replyMessageProcessor.registerSendMessageHook(sendMessageHookList);

        this.remotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE, replyMessageProcessor, replyMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE_V2, replyMessageProcessor, replyMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE, replyMessageProcessor, replyMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE_V2, replyMessageProcessor, replyMessageExecutor);

        /**
         * QueryMessageProcessor
         */
        NettyRequestProcessor queryProcessor = new QueryMessageProcessor(this);
        this.remotingServer.registerProcessor(RequestCode.QUERY_MESSAGE, queryProcessor, this.queryMessageExecutor);
        this.remotingServer.registerProcessor(RequestCode.VIEW_MESSAGE_BY_ID, queryProcessor, this.queryMessageExecutor);

        this.fastRemotingServer.registerProcessor(RequestCode.QUERY_MESSAGE, queryProcessor, this.queryMessageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.VIEW_MESSAGE_BY_ID, queryProcessor, this.queryMessageExecutor);

        /**
         * ClientManageProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.HEART_BEAT, clientManageProcessor, this.heartbeatExecutor);
        this.remotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientManageProcessor, this.clientManageExecutor);
        this.remotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientManageProcessor, this.clientManageExecutor);

        this.fastRemotingServer.registerProcessor(RequestCode.HEART_BEAT, clientManageProcessor, this.heartbeatExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.UNREGISTER_CLIENT, clientManageProcessor, this.clientManageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.CHECK_CLIENT_CONFIG, clientManageProcessor, this.clientManageExecutor);

        /**
         * ConsumerManageProcessor
         */
        ConsumerManageProcessor consumerManageProcessor = new ConsumerManageProcessor(this);
        this.remotingServer.registerProcessor(RequestCode.GET_CONSUMER_LIST_BY_GROUP, consumerManageProcessor, this.consumerManageExecutor);
        this.remotingServer.registerProcessor(RequestCode.UPDATE_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);
        this.remotingServer.registerProcessor(RequestCode.QUERY_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);

        this.fastRemotingServer.registerProcessor(RequestCode.GET_CONSUMER_LIST_BY_GROUP, consumerManageProcessor, this.consumerManageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.UPDATE_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.QUERY_CONSUMER_OFFSET, consumerManageProcessor, this.consumerManageExecutor);

        /**
         * QueryAssignmentProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.QUERY_ASSIGNMENT, queryAssignmentProcessor, loadBalanceExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.QUERY_ASSIGNMENT, queryAssignmentProcessor, loadBalanceExecutor);
        this.remotingServer.registerProcessor(RequestCode.SET_MESSAGE_REQUEST_MODE, queryAssignmentProcessor, loadBalanceExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.SET_MESSAGE_REQUEST_MODE, queryAssignmentProcessor, loadBalanceExecutor);

        /**
         * EndTransactionProcessor
         */
        this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, endTransactionProcessor, this.endTransactionExecutor);
        this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, endTransactionProcessor, this.endTransactionExecutor);

        /*
         * Default
         */
        AdminBrokerProcessor adminProcessor = new AdminBrokerProcessor(this);
        this.remotingServer.registerDefaultProcessor(adminProcessor, this.adminBrokerExecutor);
        this.fastRemotingServer.registerDefaultProcessor(adminProcessor, this.adminBrokerExecutor);
    }

其中,SendMessageProcessor整个处理器被专门用来处理发送消息请求,也就是说Producer发送消息类请求都是通过这个处理器来处理的。这些处理器会连同对应的执行器线程池一起构建一个Pair对象,然后以requestCode为key,Pair对象为value注册到processorTable集合缓存中。

/**
     * 用来处理sendProcessor处理器的接口
     * @param requestCode
     * @param processor
     * @param executor
     */
    void registerProcessor(final int requestCode, final NettyRequestProcessor processor,
        final ExecutorService executor);
    /**
     * NettyRemotingServer的方法,注册netty请求处理器
     * @param requestCode       请求编码
     * @param processor         请求处理器
     * @param executor          请求执行器
     */
    @Override
    public void registerProcessor(int requestCode, NettyRequestProcessor processor, ExecutorService executor) {
        ExecutorService executorThis = executor;
        if (null == executor) {
            //默认执行器是publicExecutor,线程数默认4个线程
            executorThis = this.publicExecutor;
        }

        //将处理器和执行器线程池绑定为一个Pair对象
        Pair<NettyRequestProcessor, ExecutorService> pair = new Pair<>(processor, executorThis);
        //以requestCode为key,以Pair对象为value注册到processTable集合缓存中
        this.processorTable.put(requestCode, pair);
    }

1.2 NettyServerHandler处理请求

当Nett服务端收到消息的时候,首先会在NettyServerHandler中进行处理。

    /**
     * broker开放10911端口用来与producer和consume进行通信。所有NettyServerHandler的我们分析的入口:它是NettyRemotingServer类的内部类
     */
    @ChannelHandler.Sharable
    class NettyServerHandler extends SimpleChannelInboundHandler<RemotingCommand> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) {
            int localPort = RemotingHelper.parseSocketAddressPort(ctx.channel().localAddress());
            NettyRemotingAbstract remotingAbstract = NettyRemotingServer.this.remotingServerTable.get(localPort);
            if (localPort != -1 && remotingAbstract != null) {
                //Netty服务端业务请求处理器的入口
                remotingAbstract.processMessageReceived(ctx, msg);
                return;
            }
            // The related remoting server has been shutdown, so close the connected channel
            RemotingHelper.closeChannel(ctx.channel());
        }

具体的处理器方法就是processorMessageReceived方法了,在这个方法中,会根据请求 code将请求分发给不同的处理器进行处理。

    /**
     * NettyRemotingAbstract的方法
     * 这个方法我们在学习Producer发送消息源码的时候就见过了,它会根据接收到的消息是请求还是响应,选择不同的方法处理
     * 如果是请求消息则调用processRequestCommand方法,如果是响应消息则用processResponseCommand方法。
     * 在这里,是Broker端接收Producer的请求,因此会调用processRequestCommand,而如果是Producer端接收到了Broker的响应,则是调用processResponseCommand方法处理。
     * 处理RemotingCommand命令消息,传入的远程处理命令可能是:
     * 1. 来自远程对等组件的查询需求
     * 2. 对该参与者之前发出的请求的响应
     * @param ctx
     * @param msg
     */
    public void processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg) {
        if (msg != null) {
            switch (msg.getType()) {
                //处理来源服务器的请求request
                case REQUEST_COMMAND:
                    //客户端发送消息之后请求会在服务端被processRequestCommand方法处理
                    processRequestCommand(ctx, msg);
                    break;
                //处理来源服务器端的响应response
                case RESPONSE_COMMAND:
                    //客户端发送消息之后服务端的响应会被processResponseCommand方法处理
                    processResponseCommand(ctx, msg);
                    break;
                default:
                    break;
            }
        }
    }

这个方法我们在学习Producer发送消息源码的时候就已经见过了,它会根据接收到的消息是请求还是响应选择不同的方法处理,如果是请求消息则调用processorRequestCommand方法,如果是响消息则用processorResponseCommand方法。在这里,是broker端接收Producer的请i去,因此会调用processorRequestCommand方法处理,而如果是Producer端接收到了broker的响应,则是调用processorResponseCommand方法处理。

1.3 processorReqeustCommand分发处理请求

该方法是服务端用来处理来自客户端请求指令的入口方法,大概流程为:

1.首先从请求中获取requestCode,然后根据此requestCode从processorTable这个本地缓存变量中找到对应的Pair对象,进而找到相应的processor以及对应的处理线程池 。如果该requestCode没有注册的RequestProcessor,则采用DefaultRequestProcessor作为请求处理器。

2.然后会创建一个线程任务Runnable,该线程任务中:

        2.1.首先会获取远程地址,然后执行前置钩子方法;

        2.2.创建响应命令,执行后置方法,写响应进通道;

3.判断如果该请求处理器拒绝该请求,那么返回系统繁忙的响应SYSTEM_BUSY。

4.根据此前创建的Runnable,创建请求任务RequestTask对象,随后通过对应的请求执行器线程池执行这个任务,这里就是支持多线程并发的执行请求处理的逻辑,也就是RocketMQ RPC通信模型的M2。

  /**
     * Process incoming request command issued by remote peer.
     * NettyRemotingAbstract的方法
     * 处理远程对等方发出的传入请求命令
     * 方法主要步骤:
     *      1. 首先从请求中获取requestCode,然后根据此code从processorTable这个本地缓存变量表中找到对应的processor以及对应的处理线程池。
     *      如果该Code没有注册的RequestProcessor,则采用默认的DefaultRequestProcessor作为请求处理器。
     *      2. 然后会创建一个线程任务Runnable,该线程任务中:
     *          (1)首先会远程地址,然后执行前置钩子方法;
     *          (2)创建响应命令,执行后置方法,写响应进通道。
     *      3. 判断如果该请求处理器拒绝该请求,那么返回系统繁忙的响应SYSTEM_BUSY;
     *      4. 根据此前创建的Runnable创建请求任务RequestTask对象,随后通过对应的请求执行器线程池执行这个任务,这里就是至此多线程并发的执行请求处理的逻辑,也是RocketMQ RPC通信模型中的M2。
     * @param ctx channel handler context.
     * @param cmd request command.
     */
    public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {
        //根据RemotingCommand 的业务请求码code去processorTable这个本地缓存变量表中找到对应的processor处理器以及对应的执行器线程池
        final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());
        //如果该Code没有注册的RequestProcessor,则采用DefaultRequestProcessor作为默认请求处理器,使用remotingExecutor作为默认请求执行器
        final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessorPair : matched;
        //获取该请求的唯一id
        final int opaque = cmd.getOpaque();

        if (pair == null) {
            String error = " request type " + cmd.getCode() + " not supported";
            final RemotingCommand response =
                RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);
            response.setOpaque(opaque);
            writeResponse(ctx.channel(), cmd, response);
            log.error(RemotingHelper.parseChannelRemoteAddr(ctx.channel()) + error);
            return;
        }

        /**
         * 1. 创建一个用于执行请求处理的线程任务
         */
        Runnable run = buildProcessRequestHandler(ctx, cmd, pair, opaque);

        /**
         * 2. 如果该请求处理器拒绝该请求,那么返回系统繁忙的响应SYSTEM_BUSY
         */
        if (pair.getObject1().rejectRequest()) {
            final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,
                    "[REJECTREQUEST]system busy, start flow control for a while");
            response.setOpaque(opaque);
            writeResponse(ctx.channel(), cmd, response);
            return;
        }

        /**
         * 3. 构建请求线程任务,然后通过执行器线程执行
         */
        try {
            //构建请求线程任务
            final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);
            //async execute task, current thread return directly
            //通过对应的请求执行器执行,这里支持多线程并发的执行请求处理
            pair.getObject2().submit(requestTask);
        } catch (RejectedExecutionException e) {
            if ((System.currentTimeMillis() % 10000) == 0) {
                log.warn(RemotingHelper.parseChannelRemoteAddr(ctx.channel())
                        + ", too many requests and system thread pool busy, RejectedExecutionException "
                        + pair.getObject2().toString()
                        + " request code: " + cmd.getCode());
            }

            //返回系统繁忙响应
            final RemotingCommand response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_BUSY,
                "[OVERLOAD]system busy, start flow control for a while");
            response.setOpaque(opaque);
            writeResponse(ctx.channel(), cmd, response);
        } catch (Throwable e) {
            AttributesBuilder attributesBuilder = RemotingMetricsManager.newAttributesBuilder()
                .put(LABEL_REQUEST_CODE, RemotingMetricsManager.getRequestCodeDesc(cmd.getCode()))
                .put(LABEL_RESULT, RESULT_PROCESS_REQUEST_FAILED);
            RemotingMetricsManager.rpcLatency.record(cmd.getProcessTimer().elapsed(TimeUnit.MILLISECONDS), attributesBuilder.build());
        }
    }

1.3.1 buildProcessRequestHandler创建一个用于执行请求处理的线程任务

 /**
     * NettyRemotingAbstract的方法
     * 创建一个用于执行请求处理的线程任务
     * @param ctx
     * @param cmd
     * @param pair
     * @param opaque
     * @return
     */
    private Runnable buildProcessRequestHandler(ChannelHandlerContext ctx, RemotingCommand cmd, Pair<NettyRequestProcessor, ExecutorService> pair, int opaque) {
        return () -> {
            Exception exception = null;
            RemotingCommand response;

            try {
                //获取远程地址
                String remoteAddr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
                try {
                    //执行前置钩子方法
                    doBeforeRpcHooks(remoteAddr, cmd);
                } catch (Exception e) {
                    exception = e;
                }

                if (exception == null) {
                    response = pair.getObject1().processRequest(ctx, cmd);
                } else {
                    //创建响应命令
                    response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR, null);
                }

                try {
                    //执行后置方法
                    doAfterRpcHooks(remoteAddr, cmd, response);
                } catch (Exception e) {
                    exception = e;
                }

                if (exception != null) {
                    throw exception;
                }

                //写入响应进channel通道
                writeResponse(ctx.channel(), cmd, response);
            } catch (Throwable e) {
                log.error("process request exception", e);
                log.error(cmd.toString());

                //如果不是单向消息
                if (!cmd.isOnewayRPC()) {
                    response = RemotingCommand.createResponseCommand(RemotingSysResponseCode.SYSTEM_ERROR,
                            UtilAll.exceptionSimpleDesc(e));
                    //设置响应id为请求id
                    response.setOpaque(opaque);
                    //写入响应进channel通道
                    writeResponse(ctx.channel(), cmd, response);
                }
            }
        };
    }

1.3.2 rejectRequest是否拒绝请求

在调用执行器处理之前,会调用处理器的rejectRequest方法,判断该处理器能否处理该请求。

不同的处理器对于rejectRequest方法有不同的实现,如果是SendMessageProcessor,那么它的实现为:检查操作系统页缓存PageCache是否繁忙或者检查临时存储池transientStorePool是否不足,如果有一个不满足要求,则拒绝处理该请求。

    boolean rejectRequest();
}
    /**
     * SendMessageProcessor的方法
     * 是否需要拒绝处理该请求
     * @return
     */
    @Override
    public boolean rejectRequest() {
        //如果没有启用isEnableSlaveActingMaster && 当前的broker为slave节点
        if (!this.brokerController.getBrokerConfig().isEnableSlaveActingMaster() && this.brokerController.getMessageStoreConfig().getBrokerRole() == BrokerRole.SLAVE) {
            return true;
        }

        //检查操作系统页缓存PageCache是否繁忙或者检查临时存储池transientStorePool是否不足,如果其中有一个不满足要求,则拒绝处理该请求
        if (this.brokerController.getMessageStore().isOSPageCacheBusy() || this.brokerController.getMessageStore().isTransientStorePoolDeficient()) {
            return true;
        }

        return false;
    }

1.3.2.1 isOSPageCacheBusy操作系统页缓存是否繁忙

一个broker将所有的信息都追加到同一个逻辑CommitLog日志文件中,因此需要通过获取putMessageLock锁来控制并发。

Diff表示锁的持有时间,当前时间减去获取锁开始时间,这个时间可以看作是处理上一个消息目前所花费的时间。

如果broker持有锁的时间超过osPageCacheBusyTimeOutMills,则算作操作系统页缓存繁忙,那么会拒绝处理当前请求,直观现象就是客户端抛出"**[REJECTREQUEST]system busy, start flow control for a while**"异常。osPageCacheBusyTimeOutMills可以配置,默认为1000ms,即1s。

   /**
     * DefaultMessageStore的方法
     * 操作系统页缓存是否繁忙
     * 函数说明:
     *  1. 一个broker将所有的消息都追加到同一个逻辑CommitLog日志文件中,因此需要通过获取putMessageLock锁来控制并发。
     *  2. Diff表示锁的持有时间,当前时间减去获取锁开始时间,这个时间可以看作是处理上一个消息目前所花费的时间。
     *  3. 如果broker持有锁的时间超过osPageCacheBusyTimeOutMills,则算作操作系统页缓存繁忙,那么会拒绝处理当前请求,直观现象就是客户端抛出**[REJECTREQUEST]system busy, start flow control for a while**异常。
     *  osPageCacheBusyTimeOutMills可以配置,默认为1000ms,即1s。
     * @return
     */
    @Override
    public boolean isOSPageCacheBusy() {
        //一个broker将所有的消息都追加到同一个逻辑CommitLog日志文件中,因此需要通过putMessageLock锁来控制并发
        //begin表示获取CommitLog锁的开始时间
        long begin = this.getCommitLog().getBeginTimeInLock();
        //计算锁的持有时间,当前时间减去获取锁开始时间,这个时间可以看作是处理上一个消息目前锁花费的时间
        long diff = this.systemClock.now() - begin;

        //如果broker持有锁的时间超过osPageCacheBusyTimeOutMills(默认设置为1000ms,即1s,可以进行配置更改),则算操作系统页面繁忙,那么会拒绝处理当前请求
        //直观现场就是客户端抛出[REJECTREQUEST]system busy, start flow control for a while异常
        return diff < 10000000
            && diff > this.messageStoreConfig.getOsPageCacheBusyTimeOutMills();
    }

1.3.2.2 isTransientStorePoolDeficient检查临时存储池是否不足

如果启用commitLog临时存储池,那么检查当前可用的buffers堆外内存的数量是否不足,。RocketMQ中引入的transientStorePoolEnable能缓解pagecache的压力,其原理是基于DirectByteBuffer和MappedByteBuffer的读写分离,消息先写入DirectByteBuffe(堆外内存),随后从MappedByteBuffer(pagecache)读取。

    /**
     * DefaultMessageStore的方法
     * 检查临时存储池是否不足
     *
     * 如果启用commitLog临时存储池,那么检查当前可用的buffers堆外内存的数量是否充足。
     * RocketMQ中引入的transientStorePoolEnable能缓解pagecache的压力,其原理是基于DirectByteBuffer和MappedByteBuffer的读写分离
     * 消息先写入DirectByteBuffer(堆外内存),随后从MappedByteBuffer(pagecache)读取。
     * @return
     */
    @Override
    public boolean isTransientStorePoolDeficient() {
        //如果堆外内存池个数为0,则表示临时存储池不足。
        return remainTransientStoreBufferNumbs() == 0;
    }
    public int remainTransientStoreBufferNumbs() {
        //检查可用buffers
        return this.transientStorePool.availableBufferNums();
    }

仅当transientStorePoolEnable为true(默认false)且当前broker不是SLAVE角色时,才启用commitLog临时存储池。如果没开启commitLog临时存储池,那么返回最大int值。

    /**
     * TransientStorePool的方法
     * @return
     */
    public int availableBufferNums() {
        //如果启动,则返回可用的堆外内存此的数量
        if (storeConfig.isTransientStorePoolEnable()) {
            return availableBuffers.size();
        }
        //如果没开启,则返回最大int值
        return Integer.MAX_VALUE;
    }
    public boolean isTransientStorePoolEnable() {
        return transientStorePoolEnable && BrokerRole.SLAVE != getBrokerRole();
    }

2 processRequest处理请求

生产者发送消息的请求,将会被 broker 的 SendMessageProcessor 处理器处理,并且被  SendMessageProcessor 执行器并发执行。

SendMessageProcessor 属于 NettyRequestProcessor,因此会调用 processRequest 方法执行请求和响应回调函数。

2.1 processRequest处理请求

processRequest 方法根据不同的 RequestCode 进行请求的处理。如果 RequestCode 是CONSUMER_SEND_MSG_BACK,即消费者发送的消息回退请求,那么调用consumerSendMsgBack 方法处理,其他情况下走默认处理逻辑。

默认处理逻辑中,首先是解析请求头,然后构建发送请求消息轨迹上下文,随后执行发送前钩子方法,最后判断如果是批量消息请求,那么调用 sendBatchMessage 方法执行处理批量发送消息逻辑,否则调用 sendMessage 方法处理其他发送消息逻辑,例如单条消息。

    /**
     * SendMessageProcessor的方法
     * 消息的请求处理
     * 方法步骤:
     *      1. 根据不同的RequestCode异步处理请求。
     *      如果RequestCode是CONSUMER_SEND_MSG_BACK,即消费者发送的消息回退请求,那么调用consumerSendMsgBack方法处理,其他情况下走默认处理逻辑。
     *      2. 默认处理逻辑中,首先解析请求头,然后构建发送请求消息轨迹上下文,随后执行发送消息前钩子方法,最后判断如果是批量消息sendBatchMessage方法执行处理批量发送消息逻辑;
     *      否则调用sendMessage方法处理其他发送消息逻辑,例如单条消息。
     * @param ctx
     * @param request
     * @return
     * @throws RemotingCommandException
     */
    @Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx,
        RemotingCommand request) throws RemotingCommandException {
        SendMessageContext sendMessageContext;
        /**
         * 根据不同的请求code选择不同的处理方式
         */
        switch (request.getCode()) {
            //如果是消费者发送的消息回退请求,该请求用于实现消息重试
            //如果是消息消费失败,那么消息将会被通过回退请求发送送回broker
            case RequestCode.CONSUMER_SEND_MSG_BACK:
                return this.consumerSendMsgBack(ctx, request);
            //其他情况, 都是属于生产者发送消息的请求,统一处理
            default:
                //解析请求头
                SendMessageRequestHeader requestHeader = parseRequestHeader(request);
                if (requestHeader == null) {
                    //如果请求头为null,那么返回一个null值结果
                    return null;
                }
                TopicQueueMappingContext mappingContext = this.brokerController.getTopicQueueMappingManager().buildTopicQueueMappingContext(requestHeader, true);
                //获取重写结果
                RemotingCommand rewriteResult = this.brokerController.getTopicQueueMappingManager().rewriteRequestForStaticTopic(requestHeader, mappingContext);
                if (rewriteResult != null) {
                    return rewriteResult;
                }
                //构建发送请求消息轨迹上下文
                sendMessageContext = buildMsgContext(ctx, requestHeader, request);
                try {
                    //执行发送消息前钩子方法
                    this.executeSendMessageHookBefore(sendMessageContext);
                } catch (AbortProcessException e) {
                    final RemotingCommand errorResponse = RemotingCommand.createResponseCommand(e.getResponseCode(), e.getErrorMessage());
                    errorResponse.setOpaque(request.getOpaque());
                    return errorResponse;
                }

                RemotingCommand response;
                if (requestHeader.isBatch()) {
                    //处理批量发送消息逻辑
                    response = this.sendBatchMessage(ctx, request, sendMessageContext, requestHeader, mappingContext,
                        (ctx1, response1) -> executeSendMessageHookAfter(response1, ctx1));
                } else {
                    //处理其他发送消息逻辑,例如单条消息
                    response = this.sendMessage(ctx, request, sendMessageContext, requestHeader, mappingContext,
                        (ctx12, response12) -> executeSendMessageHookAfter(response12, ctx12));
                }

                return response;
        }
    }

2.1.1 parseRequestHeader 解析请求头

该方法会解析请求头为SendMessageRequestHeader对象,在该方法中会通过不同的 RequestCode 选择不同的解析方法,如果是批量消息或者轻量(压缩)消息,那么先解析为

SendMessageRequestHeaderV2, 然后转换为 SendMessageRequestHeader,否则直接解析为

 SendMessageRequestHeader。

我们之前梳理过,在Producer发送消息的时候可能会使用轻量级消息头SendMessageRequestHeaderV2,SendMessageRequestHeaderV2 相比于 SendMessageRequestHeader,其 field 全为a、b、c、d等短变量名,可以加快FastJson反序列化过程,提升传输效率。

    /**
     * SendMessageRequestHeader的方法
     * 解析消息请求头
     * 方法步骤:
     *      1. 该方法会解析请求头为SendMessageRequestHeader对象,在该方法中会通过不同的RequestCode选择不同的解析方法;
     *         如果是批量消息或者轻量(压缩)消息,那么先解析为SendMessageRequestHeaderV2,然后转换为SendMessageRequestHeader,否则直接解析为SendMessageRequestHeader;
     *      2. 我们之前讲过,在Producer发送消息的时候可能会使用轻量级消息头SendMessageRequestHeaderV2,SendMessageRequestHeaderV2相比于SendMessageRequestHeader,其field 全为 a,b,c,d 等短变量名,可以加快FastJson反序列化过程,提升传输效率。;
     * @param request
     * @return
     * @throws RemotingCommandException
     */
    public static SendMessageRequestHeader parseRequestHeader(RemotingCommand request) throws RemotingCommandException {
        SendMessageRequestHeaderV2 requestHeaderV2 = null;
        SendMessageRequestHeader requestHeader = null;
        //根据RequestCode解析为不同类型的请求头对象
        switch (request.getCode()) {
            case RequestCode.SEND_BATCH_MESSAGE:
            case RequestCode.SEND_MESSAGE_V2:
                //如果是SEND_BATCH_MESSAGE 和 SEND_MESSAGE_V2,那么解析为SendMessageRequestHeaderV2,即轻量请求头
                requestHeaderV2 =
                    (SendMessageRequestHeaderV2) request
                        .decodeCommandCustomHeader(SendMessageRequestHeaderV2.class);
            case RequestCode.SEND_MESSAGE:
                if (null == requestHeaderV2) {
                    //如果是SEND_MESSAGE,那么解析为SendMessageRequestHeader, 即普通请求头
                    requestHeader =
                        (SendMessageRequestHeader) request
                            .decodeCommandCustomHeader(SendMessageRequestHeader.class);
                } else {
                    //将V2转换为V1,即普通的SendMessageRequestHeader
                    requestHeader = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV1(requestHeaderV2);
                }
            default:
                break;
        }
        return requestHeader;
    }

3. 总结

本次我们梳理了broker接收消息源码入口的处理逻辑,可以看到最终是通过调用sendMessage方法来处理来自producer发送的消息的。

sendMessage方法内部要做的事情就比较多,保罗存储消息、构建索引、分发消费等等,这部分下次再总结。

参考文献:

(32条消息) RocketMQ源码(9)—Broker接收消息入口源码_刘Java的博客-CSDN博客_rocketmq registerprocessor

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值