RocketMQ架構 - 同步發送

前言

圍繞 defaultMQProducer.send(message) 這行代碼展開分析。

源碼解析

經過下面的一系列代碼分析,RocketMQ發送消息的底層藉助Netty的Channel#writeAndFlush(…)方法實現。
在這裏插入圖片描述
在這裏插入圖片描述
通過defaultMQProducer.setSendMsgTimeout(int timeout)方法改變默認的發送消息的超時時間。默認是3秒。

在這裏插入圖片描述
通過第二個參數CommunicationMode.SYNC,傳遞同步的通信方式。

在這裏插入圖片描述

private SendResult sendDefaultImpl(
    Message msg,
    final CommunicationMode communicationMode,
    final SendCallback sendCallback,
    final long timeout
) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
	/* 確保ServiceState是running狀態 */
    this.makeSureStateOK();
    /* 要求消息體不爲空、消息大小不超過消息最大的大小(默認是4MB)*/
    /* 要求topic不爲空,長度不能>=255,不能是TBW102,不能包含非法字符 */
    Validators.checkMessage(msg, this.defaultMQProducer);
	.....
    TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
     /* ok() : TopicPublishInfo#ArrayList<MessageQueue>屬性有元素 */
    if (topicPublishInfo != null && topicPublishInfo.ok()) {
		.....
		/* 同步發送的生產者的重試次數爲1 */
        int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
        int times = 0;
        String[] brokersSent = new String[timesTotal];
        for (; times < timesTotal; times++) {
       
            /* MQFaultStrategy選取一個MessageQueue */
            MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, null);
            if (mqSelected != null) {
                mq = mqSelected;
                brokersSent[times] = mq.getBrokerName();
               		......
               		/* 發送消息的核心方法 */
                    sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
                    ......
                    switch (communicationMode) {
                    .....
                        case SYNC:
                            if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                            	/* Indicate whether to retry another broker on sending failure internally. */
                                if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
                                	/* 重試 */
                                    continue;
                                }
                            }
                            return sendResult;
                    }
            ......
            } else {
                break;
            }
        }
        if (sendResult != null) {
            return sendResult;
        }

		/* 對NameServer地址列表進行檢查,要求不爲空,有內容 */
		/* MQClientInstance -> MQClientAPIImpl -> NettyRemotingClient#List<String> nameServerAddressList */
		List<String> nsList = this.getmQClientFactory().getMQClientAPIImpl().getNameServerAddressList();
        if (null == nsList || nsList.isEmpty()) {
            throw 。。。。。。
        }
        
        throw 。。。。。。      

在這裏插入圖片描述

private SendResult sendKernelImpl(final Message msg,
                                  final MessageQueue mq,
                                  final CommunicationMode communicationMode,
                                  final SendCallback sendCallback,
                                  final TopicPublishInfo topicPublishInfo,
                                  final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    。。。。。。
    String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
    if (null == brokerAddr) {
        tryToFindTopicPublishInfo(mq.getTopic());
        brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(mq.getBrokerName());
    }

    SendMessageContext context = null;
    if (brokerAddr != null) {
     	/* 如果vipChannelEnabled設置爲true,broker的端口號減2 */
        brokerAddr = MixAll.brokerVIPChannel(this.defaultMQProducer.isSendMessageWithVIPChannel(), brokerAddr);

        byte[] prevBody = msg.getBody();
        try {
            //for MessageBatch,ID has been set in the generating process
            if (!(msg instanceof MessageBatch)) {
            	/* UNIQ_KEY - 唯一的id */
                MessageClientIDSetter.setUniqID(msg);
            }

            int sysFlag = 0;
            boolean msgBodyCompressed = false;
            /* 對於非MessagePatch類型的消息,如果大小超過了4KB,會嘗試壓縮消息, */
            if (this.tryToCompressMessage(msg)) {
                sysFlag |= MessageSysFlag.COMPRESSED_FLAG;
                msgBodyCompressed = true;
            }

			/* 判斷是否是事務消息 */
			/* TRAN_MSG屬性值 */
            final String tranMsg = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
            if (tranMsg != null && Boolean.parseBoolean(tranMsg)) {
                sysFlag |= MessageSysFlag.TRANSACTION_PREPARED_TYPE;
            }

			/* ArrayList<CheckForbiddenHook>不爲空,有內容 */
            if (hasCheckForbiddenHook()) {
                CheckForbiddenContext checkForbiddenContext = new CheckForbiddenContext();
                checkForbiddenContext.set。。。。。。
            }

			/* ArrayList<SendMessageHook>屬性不爲空,有內容 */
            if (this.hasSendMessageHook()) {
                context = new SendMessageContext();
                context.set。。。。。。
                
                /* 獲取TRAN_MSG屬性值 */
                String isTrans = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
                if (isTrans != null && isTrans.equals("true")) {
                    context.setMsgType(MessageType.Trans_Msg_Half);
                }

                if (msg.getProperty("__STARTDELIVERTIME") != null || msg.getProperty(MessageConst.PROPERTY_DELAY_TIME_LEVEL) != null) {
                    context.setMsgType(MessageType.Delay_Msg);
                }
                。。。。。。
            }

			/* 構建請求頭 */
            SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
            requestHeader.set。。。。。。
            
            /* %RETRY% */
            if (requestHeader.getTopic().startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
            	/* 從Message中獲取RECONSUME_TIME屬性值 */
                String reconsumeTimes = MessageAccessor.getReconsumeTime(msg);
                if (reconsumeTimes != null) {
                	/* 更新消費次數 */
                    requestHeader.setReconsumeTimes(Integer.valueOf(reconsumeTimes));
                    MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_RECONSUME_TIME);
                }
				/* 從Message中獲取MAX_RECONSUME_TIMES屬性值 */
                String maxReconsumeTimes = MessageAccessor.getMaxReconsumeTimes(msg);
                if (maxReconsumeTimes != null) {
                	/* 更新最大消費次數 */
                    requestHeader.setMaxReconsumeTimes(Integer.valueOf(maxReconsumeTimes));
                    MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_MAX_RECONSUME_TIMES);
                }
            }

            SendResult sendResult = null;
            switch (communicationMode) {
                ......
                case SYNC:
                    ......
                    /* 發送消息的核心方法 */
                    sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(
                        brokerAddr,
                        mq.getBrokerName(),
                        msg,
                        requestHeader,
                        timeout - costTimeSync,
                        communicationMode,
                        context,
                        this);
                    break;             
            }
			/* ArrayList<SendMessageHook>屬性不爲空,有元素 */
            if (this.hasSendMessageHook()) {
            	/* SendMessageContext */
                context.setSendResult(sendResult);
                /* 日誌記錄 */
                this.executeSendMessageHookAfter(context);
            }
            return sendResult;
        .....
        } finally {
            msg.setBody(prevBody);
        }
    }
    throw ......
}

在這裏插入圖片描述

@Override
public RemotingCommand invokeSync(String addr, final RemotingCommand request, long timeoutMillis)
    throws InterruptedException, RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException {
	......
    final Channel channel = this.getAndCreateChannel(addr);
    if (channel != null && channel.isActive()) {
        try {
            doBeforeRpcHooks(addr, request);
            .....
            RemotingCommand response = this.invokeSyncImpl(channel, request, timeoutMillis - costTime);
            doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(channel), request, response);
            return response;
       ......
    } else {
        this.closeChannel(addr, channel);
        throw new RemotingConnectException(addr);
    }
}
public RemotingCommand invokeSyncImpl(final Channel channel, final RemotingCommand request,
    final long timeoutMillis)
    throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {
    final int opaque = request.getOpaque();

    try {
        final ResponseFuture responseFuture = new ResponseFuture(channel, opaque, timeoutMillis, null, null);
        this.responseTable.put(opaque, responseFuture);
        final SocketAddress addr = channel.remoteAddress();
        /* !!!RocketMQ發送消息的核心方法 */
        channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture f) throws Exception {
                if (f.isSuccess()) {
                    responseFuture.setSendRequestOK(true);
                    return;
                } else {
                    responseFuture.setSendRequestOK(false);
                }

                responseTable.remove(opaque);
                responseFuture.setCause(f.cause());
                responseFuture.putResponse(null);
                log.warn("send a request command to channel <" + addr + "> failed.");
            }
        });

        RemotingCommand responseCommand = responseFuture.waitResponse(timeoutMillis);
        if (null == responseCommand) {
            if (responseFuture.isSendRequestOK()) {
                throw new RemotingTimeoutException(RemotingHelper.parseSocketAddressAddr(addr), timeoutMillis,
                    responseFuture.getCause());
            } else {
                throw new RemotingSendRequestException(RemotingHelper.parseSocketAddressAddr(addr), responseFuture.getCause());
            }
        }

        return responseCommand;
    } finally {
        this.responseTable.remove(opaque);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章