Producer作为生产者是RocketMQ的重要组成部分,下面我们通过一个消息发送的例子来说明Producer的工作原理。
一个消息发送的例子:
- // 使用GroupName初始化Producer
- DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
- // 指定NameSrv的地址: 也可以通过环境变量NAMESRV_ADDR来指定,则不需要下面这一行。
- producer.setNamesrvAddr("name-server1-ip:9876;name-server2-ip:9876");
- // 启动实例
- producer.start();
- try {
- // 创建消息实例,指定 topic, tag, message body.
- Message msg = new Message("TopicTest"/* Topic */,
- "TagA"/* Tag */,
- ("Hello RocketMQ !!").getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
- );
- // 发送消息给Broker
- SendResult sendResult = producer.send(msg);
- System.out.printf("%s%n", sendResult);
- } catch (Exception e) {
- e.printStackTrace();
- Thread.sleep(1000);
- }
- // 关闭生产者
- producer.shutdown();
通过上面的例子可以看出,Producer的消息发送流程如下:
1、首先创建一个DefaultMQProducer 实例,然后设置NameSrv的地址,再启动实例。
2、当需要发送消息时,创建消息实例,然后发送消息给Broker。
可见,Producer主要是通过类DefaultMQProducer来实现发送流程。DefaultMQProducer的构造函数如下:
- public DefaultMQProducer(final String producerGroup) {
- this(producerGroup, null);
- }
- public DefaultMQProducer(final String producerGroup, RPCHook rpcHook) {
- this.producerGroup = producerGroup;
- defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
- }
在DefaultMQProducer的构造函数中,主要是创建了类DefaultMQProducerImpl的实例,通过类DefaultMQProducerImpl的实例来实现各种逻辑。
DefaultMQProducerImpl#start()方法分析
- public void start(final boolean startFactory) throws MQClientException {
- switch (this.serviceState) {
- case CREATE_JUST:
- // 标记初始化失败,这个技巧不错。
- this.serviceState = ServiceState.START_FAILED;
-
- this.checkConfig();
-
- if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {
- this.defaultMQProducer.changeInstanceNameToPID();
- }
-
- // 获取MQClient 对象
- this.mQClientFactory = MQClientManager.getInstance().getAndCreateMQClientInstance(this.defaultMQProducer, rpcHook);
-
- // 注册Producer
- boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);
- if (!registerOK) {
- this.serviceState = ServiceState.CREATE_JUST;
- throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()
- + "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),
- null);
- }
-
- //把 MixAll.DEFAULT_TOPIC 放入其中
- this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());
-
- // 启动MQClient对象
- if (startFactory) {
- mQClientFactory.start();
- }
-
- log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),
- this.defaultMQProducer.isSendMessageWithVIPChannel());
-
- // 标记初始化成功
- this.serviceState = ServiceState.RUNNING;
- break;
- case RUNNING:
- case START_FAILED:
- case SHUTDOWN_ALREADY:
- throw new MQClientException("The producer service state not OK, maybe started once, "//
- + this.serviceState//
- + FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),
- null);
- default:
- break;
- }
-
- this.mQClientFactory.sendHeartbeatToAllBrokerWithLock();
- }
逻辑流程:
关于启动MQClientInstance有如下逻辑:
关于启动一系列定时任务有:
定时任务使用的是scheduleAtFixedRate,如果上一次任务超时则一下次任务会立即执行。
DefaultMQProducer消息发送
DefaultMQProducer的消息发送主要是通过DefaultMQProducerImpl的send方法来实现的,其流程如下:
- /**
- * 发送消息。
- * 1. 获取消息路由信息
- * 2. 选择要发送到的消息队列
- * 3. 执行消息发送核心方法
- * 4. 对发送结果进行封装返回
- *
- * @param msg 消息
- * @param communicationMode 通信模式
- * @param sendCallback 发送回调
- * @param timeout 发送消息请求超时时间
- * @return 发送结果
- * @throws MQClientException 当Client发生异常
- * @throws RemotingException 当请求发生异常
- * @throws MQBrokerException 当Broker发生异常
- * @throws InterruptedException 当线程被打断
- */
- private SendResult sendDefaultImpl(Message msg,
- final CommunicationMode communicationMode,
- final SendCallback sendCallback,
- final long timeout
- ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
- // 校验 Producer 处于运行状态
- this.makeSureStateOK();
- // 校验消息格式
- Validators.checkMessage(msg, this.defaultMQProducer);
- //
- final long invokeID = random.nextLong(); // 调用编号;用于下面打印日志,标记为同一次发送消息
- long beginTimestampFirst = System.currentTimeMillis();
- long beginTimestampPrev = beginTimestampFirst;
- @SuppressWarnings("UnusedAssignment")
- long endTimestamp = beginTimestampFirst;
- // 获取 Topic路由信息
- TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
- if (topicPublishInfo != null && topicPublishInfo.ok()) {
- MessageQueue mq = null; // 最后选择消息要发送到的队列
- Exception exception = null;
- SendResult sendResult = null; // 最后一次发送结果
- int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1; // 同步3次调用
- int times = 0; // 第几次发送
- String[] brokersSent = new String[timesTotal]; // 存储每次发送消息选择的broker名
- // 循环调用发送消息,直到成功
- for (; times < timesTotal; times++) {
- String lastBrokerName = null == mq ? null : mq.getBrokerName();
- @SuppressWarnings("SpellCheckingInspection")
- // 选择消息要发送到的队列,默认策略下,按顺序轮流发送,当一次发送失败时,按顺序选择下一个Broker的MessageQueue
- MessageQueue tmpmq = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
- if (tmpmq != null) {
- mq = tmpmq;
- brokersSent[times] = mq.getBrokerName();
- try {
- beginTimestampPrev = System.currentTimeMillis();
- // 调用发送消息核心方法
- sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout);
- endTimestamp = System.currentTimeMillis();
- // 更新Broker可用性信息,发送时间超过550ms后会有不可用时长,至少30S,不可用时间只有在开启了延迟容错机制才有效果
- this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
- switch (communicationMode) {
- case ASYNC:
- return null;
- case ONEWAY:
- return null;
- case SYNC:
- if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
- // 同步发送成功但存储有问题时 && 配置存储异常时重新发送开关 时,进行重试
- if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
- continue;
- }
- }
- return sendResult;
- default:
- break;
- }
- } catch (RemotingException e) { // 打印异常,更新Broker可用性信息,停用10M,更新继续循环
- endTimestamp = System.currentTimeMillis();
- this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
- log.warn(String
- .format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev,
- mq), e);
- log.warn(msg.toString());
- exception = e;
- continue;
- } catch (MQClientException e) { // 打印异常,更新Broker可用性信息,停用10M,继续循环
- endTimestamp = System.currentTimeMillis();
- this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
- log.warn(String
- .format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev,
- mq), e);
- log.warn(msg.toString());
- exception = e;
- continue;
- } catch (MQBrokerException e) { // 打印异常,更新Broker可用性信息,部分情况下的异常,直接返回,结束循环
- endTimestamp = System.currentTimeMillis();
- this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, true);
- log.warn(String
- .format("sendKernelImpl exception, resend at once, InvokeID: %s, RT: %sms, Broker: %s", invokeID, endTimestamp - beginTimestampPrev,
- mq), e);
- log.warn(msg.toString());
- exception = e;
- switch (e.getResponseCode()) {
- // 如下异常continue,进行发送消息重试
- case ResponseCode.TOPIC_NOT_EXIST:
- case ResponseCode.SERVICE_NOT_AVAILABLE:
- case ResponseCode.SYSTEM_ERROR:
- case ResponseCode.NO_PERMISSION:
- case ResponseCode.NO_BUYER_ID:
- case ResponseCode.NOT_IN_CURRENT_UNIT:
- continue;
- // 如果有发送结果,进行返回,否则,抛出异常;
- default:
- if (sendResult != null) {
- return sendResult;
- }
- throw e;
- }
- } catch (InterruptedException e) {
- endTimestamp = System.currentTimeMillis();
- this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);
- log.warn(String.format("sendKernelImpl exception, throw exception, InvokeID: %s, RT: %sms, Broker: %s", invokeID,
- endTimestamp - beginTimestampPrev, mq), e);
- log.warn(msg.toString());
- throw e;
- }
- } else {
- break;
- }
- }
- // 返回发送结果
- if (sendResult != null) {
- return sendResult;
- }
- // 根据不同情况,抛出不同的异常
- String info = String.format("Send [%d] times, still failed, cost [%d]ms, Topic: %s, BrokersSent: %s", times,
- System.currentTimeMillis() - beginTimestampFirst,
- msg.getTopic(), Arrays.toString(brokersSent)) + FAQUrl.suggestTodo(FAQUrl.SEND_MSG_FAILED);
- MQClientException mqClientException = new MQClientException(info, exception);
- if (exception instanceof MQBrokerException) {
- mqClientException.setResponseCode(((MQBrokerException)exception).getResponseCode());
- } else if (exception instanceof RemotingConnectException) {
- mqClientException.setResponseCode(ClientErrorCode.CONNECT_BROKER_EXCEPTION);
- } else if (exception instanceof RemotingTimeoutException) {
- mqClientException.setResponseCode(ClientErrorCode.ACCESS_BROKER_TIMEOUT);
- } else if (exception instanceof MQClientException) {
- mqClientException.setResponseCode(ClientErrorCode.BROKER_NOT_EXIST_EXCEPTION);
- }
- throw mqClientException;
- }
- // Namesrv找不到异常
- List
nsList = this.getmQClientFactory().getMQClientAPIImpl().getNameServerAddressList(); - if (null == nsList || nsList.isEmpty()) {
- throw new MQClientException(
- "No name server address, please set it." + FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL), null).setResponseCode(
- ClientErrorCode.NO_NAME_SERVER_EXCEPTION);
- }
- // 消息路由找不到异常
- throw new MQClientException("No route info of this topic, " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO_TOPIC_ROUTE_INFO),
- null).setResponseCode(ClientErrorCode.NOT_FOUND_TOPIC_EXCEPTION);
- }
同步发送消息的逻辑还是比较简单的:
总结一下Producer发送消息流程:
1. 获取消息路由信息 2. 选择要发送到的消息队列 3. 执行消息发送核心方法 4. 对发送结果进行封装返回
DefaultMQProducerImpl#tryToFindTopicPublishInfo
DefaultMQProducerImpl查找路由的流程如下:
- /**
- * 获取 Topic发布信息
- * 如果获取不到,或者状态不正确,则从 Namesrv获取一次
- *
- * @param topic Topic
- * @return topic 信息
- */
- private TopicPublishInfo tryToFindTopicPublishInfo(final String topic) {
- // 缓存中获取 Topic发布信息
- TopicPublishInfo topicPublishInfo = this.topicPublishInfoTable.get(topic);
- // 当无可用的 Topic发布信息时,从Namesrv获取一次
- if (null == topicPublishInfo || !topicPublishInfo.ok()) {
- this.topicPublishInfoTable.putIfAbsent(topic, new TopicPublishInfo());
- this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic);
- topicPublishInfo = this.topicPublishInfoTable.get(topic);
- }
- // 若获取的 Topic发布信息时候可用,则返回
- if (topicPublishInfo.isHaveTopicRouterInfo() || topicPublishInfo.ok()) {
- return topicPublishInfo;
- } else { // 使用 {@link DefaultMQProducer#createTopicKey} 对应的 Topic发布信息。用于 Topic发布信息不存在 && Broker支持自动创建Topic
- this.mQClientFactory.updateTopicRouteInfoFromNameServer(topic, true, this.defaultMQProducer);
- topicPublishInfo = this.topicPublishInfoTable.get(topic);
- return topicPublishInfo;
- }
- }
TopicPublishInfo的定义如下:
- /**
- * Topic发布信息
- */
- public class TopicPublishInfo {
-
- /**
- * 是否顺序消息
- */
- private boolean orderTopic = false;
- /**
- * 是否有路由信息
- */
- private boolean haveTopicRouterInfo = false;
- /**
- * 消息队列数组
- */
- private List
messageQueueList = new ArrayList(); - /**
- * 线程变量(Index)
- */
- private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();
- /**
- * Topic消息路由信息
- */
- private TopicRouteData topicRouteData;
- }
使用函数updateTopicRouteInfoFromNameServer来获取路由信息
- /**
- * 更新单个 Topic 路由信息
- * 若 isDefault=true && defaultMQProducer!=null 时,使用{@link DefaultMQProducer#createTopicKey}
- *
- * @param topic Topic
- * @param isDefault 是否默认
- * @param defaultMQProducer producer
- * @return 是否更新成功
- */
- public boolean updateTopicRouteInfoFromNameServer(final String topic, boolean isDefault, DefaultMQProducer defaultMQProducer) {
- try {
- if (this.lockNamesrv.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
- try {
- TopicRouteData topicRouteData;
- // 使用默认TopicKey获取TopicRouteData。
- // 当broker开启自动创建topic开关时,会使用MixAll.DEFAULT_TOPIC进行创建。
- // 当producer的createTopic为MixAll.DEFAULT_TOPIC时,则可以获得TopicRouteData。
- // 目的:用于新的topic,发送消息时,未创建路由信息,先使用createTopic的路由信息,等到发送到broker时,进行自动创建。
- // @see TopicConfigManager
- if (isDefault && defaultMQProducer != null) {
- topicRouteData = this.mQClientAPIImpl.getDefaultTopicRouteInfoFromNameServer(defaultMQProducer.getCreateTopicKey(), 1000 * 3);
- if (topicRouteData != null) {
- 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);
- 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会被设置到下面的publishInfo/subscribeInfo
- TopicRouteData cloneTopicRouteData = topicRouteData.cloneTopicRouteData();
-
- // 更新 Broker 地址相关信息,当某个Broker心跳超时后,会被从BrokerData的brokerAddrs中移除(由Namesrv定时操作)
- // Namesrv存在Slave的BrokerData,所以brokerAddrTable含有Slave的brokerAddr
- for (BrokerData bd : topicRouteData.getBrokerDatas()) {
- this.brokerAddrTable.put(bd.getBrokerName(), bd.getBrokerAddrs());
- }
-
- // 更新生产者里的TopicPublishInfo,Slave在注册Broker时不会生成QueueData,但会生成BrokerData
- TopicPublishInfo publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData);
- publishInfo.setHaveTopicRouterInfo(true);
- for (Entry
entry : this.producerTable.entrySet()) { - MQProducerInner impl = entry.getValue();
- if (impl != null) {
- impl.updateTopicPublishInfo(topic, publishInfo);
- }
- }
-
- // 更新订阅者(消费者)里的队列信息,Slave在注册Broker时不会生成QueueData,但会生成BrokerData
- Set
subscribeInfo = topicRouteData2TopicSubscribeInfo(topic, topicRouteData); - for (Entry
entry : this.consumerTable.entrySet()) { - MQConsumerInner impl = entry.getValue();
- if (impl != null) {
- impl.updateTopicSubscribeInfo(topic, subscribeInfo);
- }
- }
- log.info("topicRouteTable.put TopicRouteData[{}]", cloneTopicRouteData);
- this.topicRouteTable.put(topic, cloneTopicRouteData);
- 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.DEFAULT_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;
- }
选择队列
- /**
- * 根据 Topic发布信息 选择一个消息队列
- * 默认情形下向所有Broker的MessageQueue按顺序轮流发送
- *
- * @param tpInfo Topic发布信息
- * @param lastBrokerName
- * @return 消息队列
- */
- public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {
- if (this.sendLatencyFaultEnable) { //如果开启了延迟容错机制,默认未开启
- try {
- //循环所有MessageQueue
- // 当 lastBrokerName == null 时,获取第一个可用的MessageQueue
- // 当 lastBrokerName != null 时, 获取 brokerName=lastBrokerName && 可用的MessageQueue
- int index = tpInfo.getSendWhichQueue().getAndIncrement();
- for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) {
- int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size();
- if (pos < 0) {
- pos = 0;
- }
- MessageQueue mq = tpInfo.getMessageQueueList().get(pos);
- if (latencyFaultTolerance.isAvailable(mq.getBrokerName())) {
- if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName)) {
- return mq;
- }
- }
- }
- // 选择一个相对好的broker,并获得其对应的一个消息队列,按 可用性 > 延迟 > 开始可用时间 选择
- final String notBestBroker = latencyFaultTolerance.pickOneAtLeast();
- int writeQueueNums = tpInfo.getQueueIdByBroker(notBestBroker);
- if (writeQueueNums > 0) {
- final MessageQueue mq = tpInfo.selectOneMessageQueue();
- if (notBestBroker != null) {
- mq.setBrokerName(notBestBroker);
- mq.setQueueId(tpInfo.getSendWhichQueue().getAndIncrement() % writeQueueNums);
- }
- return mq;
- } else {
- latencyFaultTolerance.remove(notBestBroker);
- }
- } catch (Exception e) {
- log.error("Error occurred when selecting message queue", e);
- }
- // 选择一个消息队列,不考虑队列的可用性
- return tpInfo.selectOneMessageQueue();
- }
- // 默认情况下,获得 lastBrokerName 对应的一个消息队列,不考虑该队列的可用性
- return tpInfo.selectOneMessageQueue(lastBrokerName);
- }
DefaultMQProducerImpl#sendKernelImpl:发送消息核心方法
- /**
- * 发送消息核心方法, 并返回发送结果
- *
- * @param msg 消息
- * @param mq 消息队列
- * @param communicationMode 通信模式
- * @param sendCallback 发送回调
- * @param topicPublishInfo Topic发布信息
- * @param timeout 发送消息请求超时时间
- * @return 发送结果
- * @throws MQClientException 当Client发生异常
- * @throws RemotingException 当请求发生异常
- * @throws MQBrokerException 当Broker发生异常
- * @throws InterruptedException 当线程被打断
- */
- 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 {
- // 获取 broker的Master IP地址
- 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) {
- // 是否使用broker vip通道。broker会开启两个端口对外服务,VIP通道的端口是: 原始端口-2
- brokerAddr = MixAll.brokerVIPChannel(this.defaultMQProducer.isSendMessageWithVIPChannel(), brokerAddr);
- byte[] prevBody = msg.getBody(); // 记录消息内容。下面逻辑可能改变消息内容,例如消息压缩。
- try {
- // 设置uniqID,填充入Properties
- MessageClientIDSetter.setUniqID(msg);
- // 消息压缩
- int sysFlag = 0;
- if (this.tryToCompressMessage(msg)) {
- sysFlag |= MessageSysFlag.COMPRESSED_FLAG;
- }
- // 事务
- final String tranMsg = msg.getProperty(MessageConst.PROPERTY_TRANSACTION_PREPARED);
- if (tranMsg != null && Boolean.parseBoolean(tranMsg)) {
- sysFlag |= MessageSysFlag.TRANSACTION_PREPARED_TYPE; //5
- }
- // hook:发送消息校验
- if (hasCheckForbiddenHook()) {
- CheckForbiddenContext checkForbiddenContext = new CheckForbiddenContext();
- checkForbiddenContext.setNameSrvAddr(this.defaultMQProducer.getNamesrvAddr());
- checkForbiddenContext.setGroup(this.defaultMQProducer.getProducerGroup());
- checkForbiddenContext.setCommunicationMode(communicationMode);
- checkForbiddenContext.setBrokerAddr(brokerAddr);
- checkForbiddenContext.setMessage(msg);
- checkForbiddenContext.setMq(mq);
- checkForbiddenContext.setUnitMode(this.isUnitMode());
- this.executeCheckForbiddenHook(checkForbiddenContext);
- }
- // hook:发送消息前逻辑
- if (this.hasSendMessageHook()) {
- context = new SendMessageContext();
- context.setProducer(this);
- context.setProducerGroup(this.defaultMQProducer.getProducerGroup());
- context.setCommunicationMode(communicationMode);
- context.setBornHost(this.defaultMQProducer.getClientIP());
- context.setBrokerAddr(brokerAddr);
- context.setMessage(msg);
- context.setMq(mq);
- 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);
- }
- this.executeSendMessageHookBefore(context);
- }
- // 构建发送消息请求
- SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
- requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
- requestHeader.setTopic(msg.getTopic());
- requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey());
- requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums());
- requestHeader.setQueueId(mq.getQueueId());
- requestHeader.setSysFlag(sysFlag);
- requestHeader.setBornTimestamp(System.currentTimeMillis());
- requestHeader.setFlag(msg.getFlag());
- requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
- requestHeader.setReconsumeTimes(0);
- requestHeader.setUnitMode(this.isUnitMode());
- if (requestHeader.getTopic().startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) { // 消息重发Topic
- String reconsumeTimes = MessageAccessor.getReconsumeTime(msg);
- if (reconsumeTimes != null) {
- //如果当前消息时发送给"%RETRY%+consume"时,重置requestHeader里的reconsumeTimes
- requestHeader.setReconsumeTimes(Integer.valueOf(reconsumeTimes));
- //清空消息里的RECONSUME_TIME信息
- MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_RECONSUME_TIME);
- }
- String maxReconsumeTimes = MessageAccessor.getMaxReconsumeTimes(msg);
- if (maxReconsumeTimes != null) {
- // 默认最大消费次数是16,但可以指定当前消息的最大消费次数,在业务上可能有用
- requestHeader.setMaxReconsumeTimes(Integer.valueOf(maxReconsumeTimes));
- MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_MAX_RECONSUME_TIMES);
- }
- }
- // 发送消息
- SendResult sendResult = null;
- switch (communicationMode) {
- case ASYNC:
- sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(//
- brokerAddr, // 1
- mq.getBrokerName(), // 2
- msg, // 3
- requestHeader, // 4
- timeout, // 5
- communicationMode, // 6
- sendCallback, // 7
- topicPublishInfo, // 8
- this.mQClientFactory, // 9
- this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed(), // 10
- context, //
- this);
- break;
- case ONEWAY:
- case SYNC:
- sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(
- brokerAddr,
- mq.getBrokerName(),
- msg,
- requestHeader,
- timeout,
- communicationMode,
- context,
- this);
- break;
- default:
- assert false;
- break;
- }
- // hook:发送消息后逻辑
- if (this.hasSendMessageHook()) {
- context.setSendResult(sendResult);
- this.executeSendMessageHookAfter(context);
- }
- // 返回发送结果
- return sendResult;
- } catch (RemotingException e) {
- if (this.hasSendMessageHook()) {
- context.setException(e);
- this.executeSendMessageHookAfter(context);
- }
- throw e;
- } catch (MQBrokerException e) {
- if (this.hasSendMessageHook()) {
- context.setException(e);
- this.executeSendMessageHookAfter(context);
- }
- throw e;
- } catch (InterruptedException e) {
- if (this.hasSendMessageHook()) {
- context.setException(e);
- this.executeSendMessageHookAfter(context);
- }
- throw e;
- } finally {
- msg.setBody(prevBody);
- }
- }
- // broker为空抛出异常
- throw new MQClientException("The broker[" + mq.getBrokerName() + "] not exist", null);
- }
最终通过MQClientAPIImpl的发送函数sendMessage将消息发送给Broker。