第三章-RocketMQ源码解析-基础模块

3.1 ClientConfig

public class ClientConfig {
    public static final String SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY = "com.rocketmq.sendMessageWithVIPChannel";
    private String namesrvAddr = NameServerAddressUtils.getNameServerAddresses();
    private String clientIP = RemotingUtil.getLocalAddress();
    private String instanceName = System.getProperty("rocketmq.client.name", "DEFAULT");
    private int clientCallbackExecutorThreads = Runtime.getRuntime().availableProcessors();
    protected String namespace;
    protected AccessChannel accessChannel = AccessChannel.LOCAL;
	//从name server拉取topic信息的间隔时间,默认30s
    private int pollNameServerInterval = 1000 * 30;
    //心跳间隔时候,默认30s
    private int heartbeatBrokerInterval = 1000 * 30;
    //消费偏移量(上报broker)持久化间隔时间,默认5s
    private int persistConsumerOffsetInterval = 1000 * 5;
    private boolean unitMode = false;
    private String unitName;
    //默认不开通vip通道,vip通道默认是rocketmq内部数据传输使用
    private boolean vipChannelEnabled = Boolean.parseBoolean(System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "false"));
	//开启ssl标识
    private boolean useTLS = TlsSystemConfig.tlsEnable;
	//默认java语言
    private LanguageCode language = LanguageCode.JAVA;
}

3.1.1 buildMQClientId构建clientId

//<clientIp>@<instanceName>[@[unitName]]
public String buildMQClientId() {
    StringBuilder sb = new StringBuilder();
    sb.append(this.getClientIP());

    sb.append("@");
    sb.append(this.getInstanceName());
    if (!UtilAll.isBlank(this.unitName)) {
        sb.append("@");
        sb.append(this.unitName);
    }

    return sb.toString();
}

3.2 MQClientManager

3.2.1 getAndCreateMQClientInstance

//创建并获取MQClientInstance
public MQClientInstance getAndCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {
    //通过格式:<clientIp>@<instanceName>[@[unitName]]构建clientId
    String clientId = clientConfig.buildMQClientId();
    //map中取出clientId对应的实例,如果有,直接返回并退出方法;否则,创建一个新的,并放进map中
    MQClientInstance instance = this.factoryTable.get(clientId);
    if (null == instance) {
        instance =
            new MQClientInstance(clientConfig.cloneClientConfig(),
                this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);
        //并发操作时,其他线程先put了,那就直接返回其他线程放入的实例
        MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);
        if (prev != null) {
            instance = prev;
            log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);
        } else {
            log.info("Created new MQClientInstance for clientId:[{}]", clientId);
        }
    }

    return instance;
}

3.3 MQClientInstance

3.3.1 registerProducer

根据生产者group名将生产者实现类对象绑定并放入map中,一个group只能注册一次,防止覆盖,否则注册失败

public boolean registerProducer(final String group, final DefaultMQProducerImpl producer) {
    if (null == group || null == producer) {
        return false;
    }

    MQProducerInner prev = this.producerTable.putIfAbsent(group, producer);
    if (prev != null) {
        log.warn("the producer group[{}] exist already.", group);
        return false;
    }

    return true;
}

3.3.2 start

public void start() throws MQClientException {

    synchronized (this) {
        switch (this.serviceState) {
            case CREATE_JUST:
                this.serviceState = ServiceState.START_FAILED;
                //这一步就是获取name server的地址,正常都会指定的,否则没有什么意义
                if (null == this.clientConfig.getNamesrvAddr()) {
                    this.mQClientAPIImpl.fetchNameServerAddr();
                }
                /* 启动 request-response channel,具体细节不在这里讲,以后有机会再写netty源码,主要做了以下工作
                	1.设置netty工作线程
                	2.设置netty收发消息时的处理器
                	3.创建延迟3秒且每秒执行一次扫描过期无效的reponse信息的请求
                */
                this.mQClientAPIImpl.start();
                 /* 启动rocketmq内置的一些定时任务,如下:
                	1.延迟10秒且每2分钟执行一次获取最新name server 地址的任务
                	2.延迟10毫秒且每{this.clientConfig.getPollNameServerInterval()	     (pollNameServerInterval可配置,但默认是30秒)}毫秒执行一次更新topic路由信息的任务MQClientInstance.this.updateTopicRouteInfoFromNameServer()
                	3.延迟1秒且每{this.clientConfig.getHeartbeatBrokerInterval()	     (heartbeatBrokerInterval可配置,但默认是30秒)}毫秒执行一次清除下线的broker和发送心态到所有broker的任务
                	4.延迟10秒且每{this.clientConfig.getPersistConsumerOffsetInterval()	     (persistConsumerOffsetInterval可配置,但默认是5秒)}毫秒执行一次持久化消息者offset值的任务,持久化存放在哪呢?//TODO
                	5.延迟1毫秒且每1分钟执行一次动态改变消息推送到消费者的工作线程数的任务
                */
                this.startScheduledTask();
                //启动拉取消息服务,详情见2.3.1
                this.pullMessageService.start();
                // Start rebalance service
                this.rebalanceService.start();
                //因为MQClientInstance这个类,是生产者和消费都共用的,且消费者也会使用消费生产并发送操作,比如消费失败时重推消息,也需要启动生产者实现类,所以看到这段代码不用奇怪,如果是生产者,就算调用start方法,也不会启动2次,内部有针对状态的判断
                this.defaultMQProducer.getDefaultMQProducerImpl().start(false);
                log.info("the client factory [{}] start OK", this.clientId);
                this.serviceState = ServiceState.RUNNING;  //到此,启动完成,变更状态
                break;
            case RUNNING:
                break;
            case SHUTDOWN_ALREADY:
                break;
            case START_FAILED:
                throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);
            default:
                break;
        }
    }
}

3.3.3 updateTopicRouteInfoFromNameServer更新路由

这个方法主要功能就是从namesrv中更新topic的路由信息,具体处理逻辑总结如下:

1.从namesrv获取最新的路由信息,topic分默认TBW102和用户自定义的

2.比较内存中存在的路由信息与第1步中获取的最新路由信息,不同则为变更。如果没有变更,直接释放锁,并return ture退出方法;否则,下一步

3.这个方法同时适用生产者端和消息者端,将路由信息分别转换成生产者发布信息对象内容和消费者订阅信息对象内容,同时存放到各自内存map中(生产者:topicPublishInfoTable,消费者:topicSubscribeInfoTable)

public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault,
    DefaultMQProducer defaultMQProducer) {
    try {
        //这里要加锁,防止其他线程并发修改,默认超时3秒
        if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
            try {
                TopicRouteData topicRouteData;
                //这里主要区分用户自定义topic和系统默认topic(TBW102)
                if (isDefault && defaultMQProducer != null) {
                    //通过netty传输从namesrv中获取该topic的路由信息,路由信息确定一个topic是否有序的、消息队列List(每个元素内容包含对应的brokername、readQueueNums(读队列数)、writeQueueNums(写队列数))、broker主从集群List(每个元素包含cluster、brokerName、borkerId->broker address的对应关系map)
                    topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(),
                        1000 * 3);
                    if (topicRouteData != null) {
                        /*遍历List中每个消息队列信息,这一步主要是整理该生产者能使用的队列数,而不单是服务端broker创建的数,需要取2者中的最小者,规则如下:
1.如果生产者实例创建时设置的消息队列数小于broker创建的数,那么用生产者创建的数量,也就是说broker多余的队列并不会使用
2.相反,生产者实例创建的消息队列更多,那也只会使用broker中的数量,多余的也不会使用
注意:这一块只是针对默认TBW102 topic
                        */
                        for (QueueData data : topicRouteData.getQueueDatas()) {
                            int queueNums = Math.min(defaultMQProducer.getDefaultTopicQueueNums(), data.getReadQueueNums());
                            data.setReadQueueNums(queueNums);
                            data.setWriteQueueNums(queueNums);
                        }
                    }
                } else {
                    //这里同上面介绍,不再赘述
                    topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
                }
                if (topicRouteData != null) {
                    TopicRouteData old = this.topicRouteTable.get(topic);
                    //判断新老路由信息是不是一致的,不一致返回true,否则false
                    boolean changed = topicRouteDataIsChange(old, topicRouteData);
                    if (!changed) {
                        //路由信息没有改变,进一步从发布消息的角度判断是否变更
                        changed = this.isNeedUpdateTopicRouteInfo(topic);
                    } else {
                        log.info("the topic[{}] route info changed, old[{}] ,new[{}]", topic, old, topicRouteData);
                    }

                    if (changed) {
                        TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();

                        for (BrokerData bd : topicRouteData.getBrokerDatas()) {
                            this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
                        }

                        // 更新发布消息,详情见3.3.4
                        {
                            TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
                            publishInfo.setHaveTopicRouterInfo(true);//到这一步路由信息设置完毕,标记状态
                            Iterator<Entry<String, MQProducerInner>> it = this.producerTable.entrySet().iterator();
                            while (it.hasNext()) {
                                Entry<String, MQProducerInner> entry = it.next();
                                MQProducerInner impl = entry.getValue();
                                if (impl != null) {
                                    impl.updateTopicPublishInfo(topic, publishInfo);//更新topic发布信息致内存map
                                }
                            }
                        }

                        // 更新订阅信息,逻辑同更新发布信息
                        {
                            Set<MessageQueue> subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
                            Iterator<Entry<String, MQConsumerInner>> it = this.consumerTable.entrySet().iterator();
                            while (it.hasNext()) {
                                Entry<String, MQConsumerInner> entry = it.next();
                                MQConsumerInner impl = entry.getValue();
                                if (impl != null) {
                                    impl.updateTopicSubscribeInfo(topic, subscribeInfo);
                                }
                            }
                        }
                        log.info("topicRouteTable.put. Topic = {}, TopicRouteData[{}]", topic, cloneTopicRouteData);
                        this.topicRouteTable.put(topic, cloneTopicRouteData);//最后更新新的topic路由信息到内存Map
                        return true;
                    }
                } else {
                    log.warn("updateTopicRouteInfoFromNameServer, getTopicRouteInfoFromNameServer return null, Topic: {}", topic);
                }
            } catch (Exception e) {
                if (!topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) && !topic.equals(MixAll.AUTO_CREATE_TOPIC_KEY_TOPIC)) {
                    log.warn("updateTopicRouteInfoFromNameServer Exception", e);
                }
            } finally {
                this.lockNamesrv.unlock();
            }
        } else {
            log.warn("updateTopicRouteInfoFromNameServer tryLock timeout {}ms", LOCK_TIMEOUT_MILLIS);
        }
    } catch (InterruptedException e) {
        log.warn("updateTopicRouteInfoFromNameServer Exception", e);
    }

    return false;
}

3.3.4 topicRouteData2TopicPublishInfo

topic路由信息对象转换成topic发布信息对象,内部区分是否有序topic,如果是有序的,则按配置的顺序添加到发布信息对象中,否则按路由信息对象中包含的消息队列List来填充topic发布信息对象内容

public static TopicPublishInfo topicRouteData2TopicPublishInfo(final String topic, final TopicRouteData route) {
    TopicPublishInfo info = new TopicPublishInfo();
    info.setTopicRouteData(route);
    if (route.getOrderTopicConf() != null && route.getOrderTopicConf().length() > 0) {
        String[] brokers = route.getOrderTopicConf().split(";");
        for (String broker : brokers) {
            String[] item = broker.split(":");
            int nums = Integer.parseInt(item[1]);
            for (int i = 0; i < nums; i++) {
                MessageQueue mq = new MessageQueue(topic, item[0], i);
                info.getMessageQueueList().add(mq);
            }
        }

        info.setOrderTopic(true);
    } else {
        List<QueueData> qds = route.getQueueDatas();
        Collections.sort(qds);
        for (QueueData qd : qds) {
            if (PermName.isWriteable(qd.getPerm())) {
                BrokerData brokerData = null;
                for (BrokerData bd : route.getBrokerDatas()) {
                    if (bd.getBrokerName().equals(qd.getBrokerName())) {
                        brokerData = bd;
                        break;
                    }
                }

                if (null == brokerData) {
                    continue;
                }

                if (!brokerData.getBrokerAddrs().containsKey(MixAll.MASTER_ID)) {
                    continue;
                }

                for (int i = 0; i < qd.getWriteQueueNums(); i++) {
                    MessageQueue mq = new MessageQueue(topic, qd.getBrokerName(), i);
                    info.getMessageQueueList().add(mq);
                }
            }
        }

        info.setOrderTopic(false);
    }

    return info;
}

3.3.5 doRebalance

public void doRebalance() {
    //遍历consumerTable,拿到消费者对象实例,调用doRebalance开始消费消息,consumerTable里的内容在DefaultMQPullConsumerImpl.start/DefaultMQPushConsumerImpl.start中注册,例如:2.2.1
    for (Map.Entry<String, MQConsumerInner> entry : this.consumerTable.entrySet()) {
        MQConsumerInner impl = entry.getValue();
        if (impl != null) {
            try {
                //impl可能为DefaultMQPullConsumerImpl也可能为DefaultMQPushConsumerImpl,不管是什么,最终都是调用RebalanceImpl.doRebalance,详情见2.5.1
                impl.doRebalance();
            } catch (Throwable e) {
                log.error("doRebalance exception", e);
            }
        }
    }
}

3.4 AllocateMessageQueueStrategy 分配mq策略

3.4.1 AllocateMessageQueueAveragely(平均策略)

public List<MessageQueue> allocate(String consumerGroup, String currentCID, List<MessageQueue> mqAll,
    List<String> cidAll) {
    //检查空和长度
    if (currentCID == null || currentCID.length() < 1) {
        throw new IllegalArgumentException("currentCID is empty");
    }
    if (mqAll == null || mqAll.isEmpty()) {
        throw new IllegalArgumentException("mqAll is null or mqAll empty");
    }
    if (cidAll == null || cidAll.isEmpty()) {
        throw new IllegalArgumentException("cidAll is null or cidAll empty");
    }
	
    List<MessageQueue> result = new ArrayList<MessageQueue>();
    if (!cidAll.contains(currentCID)) { //自己的cid不在broker收录内,直接返回上一层
        log.info("[BUG] ConsumerGroup: {} The consumerId: {} not in cidAll: {}",
            consumerGroup,
            currentCID,
            cidAll);
        return result;
    }
	//当前cid在cidAll中的index
    int index = cidAll.indexOf(currentCID);
    //mqAll大小 与 cidAll大小的求模
    int mod = mqAll.size() % cidAll.size();
    /*
    1.先判断mqAll.size() <= cidAll.size(),也就是说mq队列的数量小于等于客户端的数量,这样求出平均消息的mq数为1;否则下一步
    2.mq的数量大于cid数量且不是cid的倍数(mod > 0 ) && index属于多出来的前面的位置(index < mod),那么平均数就得是mqAll.size() / cidAll.size() + 1,否则就是mqAll.size() / cidAll.size() 
    */
    int averageSize = mqAll.size() <= cidAll.size() ? 1 : 
    			(mod > 0 && index < mod ? mqAll.size() / cidAll.size() + 1 : mqAll.size() / cidAll.size());
    //开始index,mod前的cid起始位置直接就是index * averageSize,mod后的cid起始位置index * averageSize +mod
    int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;
    int range = Math.min(averageSize, mqAll.size() - startIndex);
    for (int i = 0; i < range; i++) {
        result.add(mqAll.get((startIndex + i) % mqAll.size()));
    }
    return result;
}

3.5 MQClientAPIImpl

3.5.1 sendMessage

public SendResult sendMessage(
    final String addr,
    final String brokerName,
    final Message msg,
    final SendMessageRequestHeader requestHeader,
    final long timeoutMillis,
    final CommunicationMode communicationMode,
    final SendCallback sendCallback,
    final TopicPublishInfo topicPublishInfo,
    final MQClientInstance instance,
    final int retryTimesWhenSendFailed,
    final SendMessageContext context,
    final DefaultMQProducerImpl producer
) throws RemotingException, MQBrokerException, InterruptedException {
    long beginStartTime = System.currentTimeMillis();
    RemotingCommand request = null;
    // 分别针对单条消息和批量消息,创建 RemotingCommand,用于最终与Netty打交道,Netty的操作细节,本次文章不会讲解,有机会以后再讲Netty。
    if (sendSmartMsg || msg instanceof MessageBatch) {
        SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
        request = RemotingCommand.createRequestCommand(msg instanceof MessageBatch ? RequestCode.SEND_BATCH_MESSAGE : RequestCode.SEND_MESSAGE_V2, requestHeaderV2);
    } else {
        request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);
    }
    // 设置消息主体
    request.setBody(msg.getBody());

    switch (communicationMode) {
        case ONEWAY: // 单向消息发送最终调用的方法,因为是单向发送,客户端也不需要发送结果,所以,这个方法直接就调用Netty了
            this.remotingClient.invokeOneway(addr, request, timeoutMillis);
            return null;
        case ASYNC:
            final AtomicInteger times = new AtomicInteger();
            long costTimeAsync = System.currentTimeMillis() - beginStartTime;
            if (timeoutMillis < costTimeAsync) {
                throw new RemotingTooMuchRequestException("sendMessage call timeout");
            }
            // 异步消息发送最终调用的方法
            this.sendMessageAsync(addr, brokerName, msg, timeoutMillis - costTimeAsync, request, sendCallback, topicPublishInfo, instance,
                                  retryTimesWhenSendFailed, times, context, producer);
            return null;
        case SYNC:
            long costTimeSync = System.currentTimeMillis() - beginStartTime;
            if (timeoutMillis < costTimeSync) {
                throw new RemotingTooMuchRequestException("sendMessage call timeout");
            }
            // 同步消息发送最终调用的方法
            return this.sendMessageSync(addr, brokerName, msg, timeoutMillis - costTimeSync, request);
        default:
            assert false;
            break;
    }

    return null;
}

3.5.2 sendMessageAsync

private void sendMessageAsync(
    final String addr,
    final String brokerName,
    final Message msg,
    final long timeoutMillis,
    final RemotingCommand request,
    final SendCallback sendCallback,
    final TopicPublishInfo topicPublishInfo,
    final MQClientInstance instance,
    final int retryTimesWhenSendFailed,
    final AtomicInteger times,
    final SendMessageContext context,
    final DefaultMQProducerImpl producer
) throws InterruptedException, RemotingException {
    this.remotingClient.invokeAsync(addr, request, timeoutMillis, new InvokeCallback() {
        @Override
        public void operationComplete(ResponseFuture responseFuture) {
            RemotingCommand response = responseFuture.getResponseCommand();
            if (null == sendCallback && response != null) {
			// 不需要回调异步方法
                try {
                    // 处理返回结果
                    SendResult sendResult = MQClientAPIImpl.this.processSendResponse(brokerName, msg, response);
                    if (context != null && sendResult != null) {
                        context.setSendResult(sendResult);
                        // 消息发送后的 hook 处理,主要就是消息追踪
                        context.getProducer().executeSendMessageHookAfter(context);
                    }
                } catch (Throwable e) {
                }
			   // 记录当前使用的broker信息及时间到本地缓存 map
                producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), false);
                return;
            }
            // 需要回调异步方法
            if (response != null) {
                try {
                     // 处理返回结果
                    SendResult sendResult = MQClientAPIImpl.this.processSendResponse(brokerName, msg, response);
                    assert sendResult != null;
                    if (context != null) {
                        context.setSendResult(sendResult);
                        // 消息发送后的 hook 处理,主要就是消息追踪
                        context.getProducer().executeSendMessageHookAfter(context);
                    }

                    try {
                        // 异步回调发送成功的方法,用户可以自行定义 onSuccess方法中的业务逻辑
                        sendCallback.onSuccess(sendResult);
                    } catch (Throwable e) {
                    }
				  // 记录当前使用的broker信息及时间到本地缓存 map
                    producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), false);
                } catch (Exception e) {
                    // 处理异常,也得记录当前使用的broker信息及时间到本地缓存 map
                    producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), true);
                    onExceptionImpl(brokerName, msg, 0L, request, sendCallback, topicPublishInfo, instance,
                                    retryTimesWhenSendFailed, times, e, context, false, producer);
                }
            } else {
                // 没有拿到netty的返回结果,也得记录当前使用的broker信息及时间到本地缓存 map
                producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), true);
                // 分别对发送失败、响应超时、未知原因做相应的异常处理
                if (!responseFuture.isSendRequestOK()) {
                    MQClientException ex = new MQClientException("send request failed", responseFuture.getCause());
                    onExceptionImpl(brokerName, msg, 0L, request, sendCallback, topicPublishInfo, instance,
                                    retryTimesWhenSendFailed, times, ex, context, true, producer);
                } else if (responseFuture.isTimeout()) {
                    MQClientException ex = new MQClientException("wait response timeout " + responseFuture.getTimeoutMillis() + "ms",
                                                                 responseFuture.getCause());
                    onExceptionImpl(brokerName, msg, 0L, request, sendCallback, topicPublishInfo, instance,
                                    retryTimesWhenSendFailed, times, ex, context, true, producer);
                } else {
                    MQClientException ex = new MQClientException("unknow reseaon", responseFuture.getCause());
                    onExceptionImpl(brokerName, msg, 0L, request, sendCallback, topicPublishInfo, instance,
                                    retryTimesWhenSendFailed, times, ex, context, true, producer);
                }
            }
        }
    });
}

3.5.3 sendMessageSync

private SendResult sendMessageSync(
    final String addr,
    final String brokerName,
    final Message msg,
    final long timeoutMillis,
    final RemotingCommand request
) throws RemotingException, MQBrokerException, InterruptedException {
    // 通过 netty 调用,返回结果
    RemotingCommand response = this.remotingClient.invokeSync(addr, request, timeoutMillis);
    assert response != null;
    // 处理结果
    return this.processSendResponse(brokerName, msg, response);
}

3.5.4 processSendResponse

private SendResult processSendResponse(
    final String brokerName,
    final Message msg,
    final RemotingCommand response
) throws MQBrokerException, RemotingCommandException {
    switch (response.getCode()) {
        case ResponseCode.FLUSH_DISK_TIMEOUT:
        case ResponseCode.FLUSH_SLAVE_TIMEOUT:
        case ResponseCode.SLAVE_NOT_AVAILABLE: {
        }
        case ResponseCode.SUCCESS: {
            SendStatus sendStatus = SendStatus.SEND_OK;
            // 这里主要就是转换状态:将Netty返回的状态转换成MQ客户端可识别的状态
            switch (response.getCode()) {
                case ResponseCode.FLUSH_DISK_TIMEOUT:
                    sendStatus = SendStatus.FLUSH_DISK_TIMEOUT;
                    break;
                case ResponseCode.FLUSH_SLAVE_TIMEOUT:
                    sendStatus = SendStatus.FLUSH_SLAVE_TIMEOUT;
                    break;
                case ResponseCode.SLAVE_NOT_AVAILABLE:
                    sendStatus = SendStatus.SLAVE_NOT_AVAILABLE;
                    break;
                case ResponseCode.SUCCESS:
                    sendStatus = SendStatus.SEND_OK;
                    break;
                default:
                    assert false;
                    break;
            }
            // 返回结果头部组装
            SendMessageResponseHeader responseHeader =
                (SendMessageResponseHeader) response.decodeCommandCustomHeader(SendMessageResponseHeader.class);
     
            // 将原先存在 namespace的topic重新剥离成无 namespace 的
            String topic = msg.getTopic();
            if (StringUtils.isNotEmpty(this.clientConfig.getNamespace())) {
                topic = NamespaceUtil.withoutNamespace(topic, this.clientConfig.getNamespace());
            }

            MessageQueue messageQueue = new MessageQueue(topic, brokerName, responseHeader.getQueueId());

            String uniqMsgId = MessageClientIDSetter.getUniqID(msg);
            if (msg instanceof MessageBatch) {
                StringBuilder sb = new StringBuilder();
                for (Message message : (MessageBatch) msg) {
                    sb.append(sb.length() == 0 ? "" : ",").append(MessageClientIDSetter.getUniqID(message));
                }
                uniqMsgId = sb.toString();
            }
            // 组装 mq客户端可识别的发送结果对象 SendResult
            SendResult sendResult = new SendResult(sendStatus,
                                                   uniqMsgId,
                                                   responseHeader.getMsgId(), messageQueue, responseHeader.getQueueOffset());
            sendResult.setTransactionId(responseHeader.getTransactionId());
            String regionId = response.getExtFields().get(MessageConst.PROPERTY_MSG_REGION);
            String traceOn = response.getExtFields().get(MessageConst.PROPERTY_TRACE_SWITCH);
            if (regionId == null || regionId.isEmpty()) {
                regionId = MixAll.DEFAULT_TRACE_REGION_ID;
            }
            if (traceOn != null && traceOn.equals("false")) {
                sendResult.setTraceOn(false);
            } else {
                sendResult.setTraceOn(true);
            }
            sendResult.setRegionId(regionId);
            return sendResult;
        }
        default:
            break;
    }

    throw new MQBrokerException(response.getCode(), response.getRemark());
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多栖码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值