diff --git a/.travis.yml b/.travis.yml index 34d38c2c54..df6a7356bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: trusty +dist: bionic notifications: email: @@ -9,22 +9,38 @@ notifications: language: java -jdk: - - oraclejdk8 - matrix: include: # On OSX, run with default JDK only. # - os: osx - # On Linux, run with specific JDKs only. - - os: linux - env: CUSTOM_JDK="oraclejdk8" + # On Linux we install latest OpenJDK 1.8 from Ubuntu repositories + - name: Linux x86_64 + arch: amd64 + - name: Linux aarch64 + arch: arm64 + +cache: + directories: + - $HOME/.m2/repository before_install: + - lscpu - echo 'MAVEN_OPTS="$MAVEN_OPTS -Xmx1024m -XX:MaxPermSize=512m -XX:+BytecodeVerificationLocal"' >> ~/.mavenrc - cat ~/.mavenrc - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export JAVA_HOME=$(/usr/libexec/java_home); fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then jdk_switcher use "$CUSTOM_JDK"; fi + +install: | + if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + sudo apt update + sudo apt install -y openjdk-8-jdk maven + export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-${TRAVIS_CPU_ARCH}/" + export PATH="$JAVA_HOME/bin:/usr/share/maven/bin:$PATH" + fi + +before_script: + - java -version + - mvn -version + - ulimit -c unlimited script: - travis_retry mvn -B clean apache-rat:check diff --git a/LICENSE b/LICENSE index 7f77f44e73..f49a4e16e6 100644 --- a/LICENSE +++ b/LICENSE @@ -15,7 +15,7 @@ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, - "control" means (properties) the power, direct or indirect, to cause the + "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. diff --git a/NOTICE b/NOTICE index 65ebdd0361..086ee9fa17 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache RocketMQ -Copyright 2016-2020 The Apache Software Foundation +Copyright 2016-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index 91d195c341..1b1398cb79 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/apache/rocketmq.svg)](http://isitmaintained.com/project/apache/rocketmq "Average time to resolve an issue") [![Percentage of issues still open](http://isitmaintained.com/badge/open/apache/rocketmq.svg)](http://isitmaintained.com/project/apache/rocketmq "Percentage of issues still open") -![Twitter Follow](https://img.shields.io/twitter/follow/ApacheRocketMQ?style=social) +[![Twitter Follow](https://img.shields.io/twitter/follow/ApacheRocketMQ?style=social)](https://twitter.com/intent/follow?screen_name=ApacheRocketMQ) **[Apache RocketMQ](https://rocketmq.apache.org) is a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.** diff --git a/acl/pom.xml b/acl/pom.xml index ed5e709d3c..36dae6ad04 100644 --- a/acl/pom.xml +++ b/acl/pom.xml @@ -13,7 +13,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT rocketmq-acl rocketmq-acl ${project.version} diff --git a/acl/src/main/java/org/apache/rocketmq/acl/plain/RemoteAddressStrategyFactory.java b/acl/src/main/java/org/apache/rocketmq/acl/plain/RemoteAddressStrategyFactory.java index 0c3548eeb5..6ec90ee3fa 100644 --- a/acl/src/main/java/org/apache/rocketmq/acl/plain/RemoteAddressStrategyFactory.java +++ b/acl/src/main/java/org/apache/rocketmq/acl/plain/RemoteAddressStrategyFactory.java @@ -193,7 +193,7 @@ public class RemoteAddressStrategyFactory { throw new AclException(String.format("RangeRemoteAddressStrategy netaddress examine scope Exception start is %s , end is %s", start, end)); } } - return this.end > 0 ? true : false; + return this.end > 0; } private boolean ipv6Analysis(String[] strArray, int index) { diff --git a/broker/pom.xml b/broker/pom.xml index 8d52edbf1f..0137b55678 100644 --- a/broker/pom.xml +++ b/broker/pom.xml @@ -13,7 +13,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageHook.java b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageHook.java index a74b6d66ce..a89bace193 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageHook.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageHook.java @@ -17,9 +17,9 @@ package org.apache.rocketmq.broker.mqtrace; public interface SendMessageHook { - public String hookName(); + String hookName(); - public void sendMessageBefore(final SendMessageContext context); + void sendMessageBefore(final SendMessageContext context); - public void sendMessageAfter(final SendMessageContext context); + void sendMessageAfter(final SendMessageContext context); } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java index ebc9dd8acc..bd05758759 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java @@ -232,4 +232,20 @@ public class ConsumerOffsetManager extends ConfigManager { } } + public void removeOffset(final String group) { + Iterator>> it = this.offsetTable.entrySet().iterator(); + while (it.hasNext()) { + Entry> next = it.next(); + String topicAtGroup = next.getKey(); + if (topicAtGroup.contains(group)) { + String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR); + if (arrays.length == 2 && group.equals(arrays[1])) { + it.remove(); + log.warn("clean group offset {}", topicAtGroup); + } + } + } + + } + } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java index a237bf61ac..de7f3fce81 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java @@ -19,7 +19,6 @@ package org.apache.rocketmq.broker.out; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; -import java.util.Vector; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -123,7 +122,7 @@ public class BrokerOuterAPI { final int timeoutMills, final boolean compressed) { - final List registerBrokerResultList = new Vector<>(); + final List registerBrokerResultList = new CopyOnWriteArrayList<>(); List nameServerAddressList = this.remotingClient.getNameServerAddressList(); if (nameServerAddressList != null && nameServerAddressList.size() > 0) { @@ -147,7 +146,7 @@ public class BrokerOuterAPI { @Override public void run() { try { - RegisterBrokerResult result = registerBroker(namesrvAddr,oneway, timeoutMills,requestHeader,body); + RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body); if (result != null) { registerBrokerResultList.add(result); } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java index dcdb701635..0a1d214b87 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java @@ -714,6 +714,10 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements this.brokerController.getSubscriptionGroupManager().deleteSubscriptionGroupConfig(requestHeader.getGroupName()); + if (requestHeader.isRemoveOffset()) { + this.brokerController.getConsumerOffsetManager().removeOffset(requestHeader.getGroupName()); + } + if (this.brokerController.getBrokerConfig().isAutoDeleteUnusedStats()) { this.brokerController.getBrokerStatsManager().onGroupDeleted(requestHeader.getGroupName()); } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java index 957dbbaaa3..8879a722fc 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PullMessageProcessor.java @@ -584,7 +584,7 @@ public class PullMessageProcessor extends AsyncNettyRequestProcessor implements this.brokerController.getPullMessageExecutor().submit(new RequestTask(run, channel, request)); } - public void registerConsumeMessageHook(List sendMessageHookList) { - this.consumeMessageHookList = sendMessageHookList; + public void registerConsumeMessageHook(List consumeMessageHookList) { + this.consumeMessageHookList = consumeMessageHookList; } } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ReplyMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ReplyMessageProcessor.java index 565857a347..2890fc4db7 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ReplyMessageProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ReplyMessageProcessor.java @@ -251,28 +251,29 @@ public class ReplyMessageProcessor extends AbstractSendMessageProcessor implemen // Failed case CREATE_MAPEDFILE_FAILED: - log.info("create mapped file failed, server is busy or broken."); + log.warn("create mapped file failed, server is busy or broken."); break; case MESSAGE_ILLEGAL: - log.info( + log.warn( "the message is illegal, maybe msg properties length limit 32k."); break; case PROPERTIES_SIZE_EXCEEDED: - log.info( + log.warn( "the message is illegal, maybe msg body or properties length not matched. msg body length limit 128k."); break; case SERVICE_NOT_AVAILABLE: - log.info( - "service not available now, maybe disk full, maybe your broker machine memory too small."); + log.warn( + "service not available now. It may be caused by one of the following reasons: " + + "the broker's disk is full, messages are put to the slave, message store has been shut down, etc."); break; case OS_PAGECACHE_BUSY: - log.info("[PC_SYNCHRONIZED]broker busy, start flow control for a while"); + log.warn("[PC_SYNCHRONIZED]broker busy, start flow control for a while"); break; case UNKNOWN_ERROR: - log.info("UNKNOWN_ERROR"); + log.warn("UNKNOWN_ERROR"); break; default: - log.info("UNKNOWN_ERROR DEFAULT"); + log.warn("UNKNOWN_ERROR DEFAULT"); break; } diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/SendMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/SendMessageProcessor.java index 20b5cfaae8..724cf54c81 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/processor/SendMessageProcessor.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/SendMessageProcessor.java @@ -218,6 +218,8 @@ public class SendMessageProcessor extends AbstractSendMessageProcessor implement String originMsgId = MessageAccessor.getOriginMessageId(msgExt); MessageAccessor.setOriginMessageId(msgInner, UtilAll.isBlank(originMsgId) ? msgExt.getMsgId() : originMsgId); + msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgExt.getProperties())); + CompletableFuture putMessageResult = this.brokerController.getMessageStore().asyncPutMessage(msgInner); return putMessageResult.thenApply((r) -> { if (r != null) { @@ -480,7 +482,8 @@ public class SendMessageProcessor extends AbstractSendMessageProcessor implement case SERVICE_NOT_AVAILABLE: response.setCode(ResponseCode.SERVICE_NOT_AVAILABLE); response.setRemark( - "service not available now, maybe disk full, " + diskUtil() + ", maybe your broker machine memory too small."); + "service not available now. It may be caused by one of the following reasons: " + + "the broker's disk is full [" + diskUtil() + "], messages are put to the slave, message store has been shut down, etc."); break; case OS_PAGECACHE_BUSY: response.setCode(ResponseCode.SYSTEM_ERROR); diff --git a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java index 86f606593f..1f2bb4d8ba 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/topic/TopicConfigManager.java @@ -45,7 +45,7 @@ public class TopicConfigManager extends ConfigManager { private static final long LOCK_TIMEOUT_MILLIS = 3000; private static final int SCHEDULE_TOPIC_QUEUE_NUM = 18; - private transient final Lock lockTopicConfigTable = new ReentrantLock(); + private transient final Lock topicConfigTableLock = new ReentrantLock(); private final ConcurrentMap topicConfigTable = new ConcurrentHashMap(1024); @@ -159,7 +159,7 @@ public class TopicConfigManager extends ConfigManager { boolean createNew = false; try { - if (this.lockTopicConfigTable.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + if (this.topicConfigTableLock.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { try { topicConfig = this.topicConfigTable.get(topic); if (topicConfig != null) @@ -176,9 +176,7 @@ public class TopicConfigManager extends ConfigManager { if (PermName.isInherited(defaultTopicConfig.getPerm())) { topicConfig = new TopicConfig(topic); - int queueNums = - clientDefaultTopicQueueNums > defaultTopicConfig.getWriteQueueNums() ? defaultTopicConfig - .getWriteQueueNums() : clientDefaultTopicQueueNums; + int queueNums = Math.min(clientDefaultTopicQueueNums, defaultTopicConfig.getWriteQueueNums()); if (queueNums < 0) { queueNums = 0; @@ -213,7 +211,7 @@ public class TopicConfigManager extends ConfigManager { this.persist(); } } finally { - this.lockTopicConfigTable.unlock(); + this.topicConfigTableLock.unlock(); } } } catch (InterruptedException e) { @@ -239,7 +237,7 @@ public class TopicConfigManager extends ConfigManager { boolean createNew = false; try { - if (this.lockTopicConfigTable.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + if (this.topicConfigTableLock.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { try { topicConfig = this.topicConfigTable.get(topic); if (topicConfig != null) @@ -257,7 +255,7 @@ public class TopicConfigManager extends ConfigManager { this.dataVersion.nextVersion(); this.persist(); } finally { - this.lockTopicConfigTable.unlock(); + this.topicConfigTableLock.unlock(); } } } catch (InterruptedException e) { @@ -279,7 +277,7 @@ public class TopicConfigManager extends ConfigManager { boolean createNew = false; try { - if (this.lockTopicConfigTable.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + if (this.topicConfigTableLock.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { try { topicConfig = this.topicConfigTable.get(TopicValidator.RMQ_SYS_TRANS_CHECK_MAX_TIME_TOPIC); if (topicConfig != null) @@ -297,7 +295,7 @@ public class TopicConfigManager extends ConfigManager { this.dataVersion.nextVersion(); this.persist(); } finally { - this.lockTopicConfigTable.unlock(); + this.topicConfigTableLock.unlock(); } } } catch (InterruptedException e) { diff --git a/broker/src/main/java/org/apache/rocketmq/broker/transaction/queue/TransactionalMessageServiceImpl.java b/broker/src/main/java/org/apache/rocketmq/broker/transaction/queue/TransactionalMessageServiceImpl.java index 1f5e01efa4..74df47a088 100644 --- a/broker/src/main/java/org/apache/rocketmq/broker/transaction/queue/TransactionalMessageServiceImpl.java +++ b/broker/src/main/java/org/apache/rocketmq/broker/transaction/queue/TransactionalMessageServiceImpl.java @@ -164,7 +164,7 @@ public class TransactionalMessageServiceImpl implements TransactionalMessageServ break; } if (removeMap.containsKey(i)) { - log.info("Half offset {} has been committed/rolled back", i); + log.debug("Half offset {} has been committed/rolled back", i); Long removedOpOffset = removeMap.remove(i); doneOpOffset.add(removedOpOffset); } else { diff --git a/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java b/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java index 68d58ef440..9ea1eeee31 100644 --- a/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java +++ b/broker/src/test/java/org/apache/rocketmq/broker/BrokerOuterAPITest.java @@ -124,7 +124,7 @@ public class BrokerOuterAPITest { boolean success = Iterables.any(booleanList, new Predicate() { public boolean apply(Boolean input) { - return input ? true : false; + return input; } }); diff --git a/client/pom.xml b/client/pom.xml index c775ee8fae..164082c9b2 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 @@ -47,6 +47,18 @@ org.apache.commons commons-lang3 + + io.opentracing + opentracing-api + 0.33.0 + provided + + + io.opentracing + opentracing-mock + 0.33.0 + test + org.apache.logging.log4j log4j-core diff --git a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java index d0ae5e1b83..3ab214b8db 100644 --- a/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java +++ b/client/src/main/java/org/apache/rocketmq/client/ClientConfig.java @@ -94,7 +94,7 @@ public class ClientConfig { public void changeInstanceNameToPID() { if (this.instanceName.equals("DEFAULT")) { - this.instanceName = String.valueOf(UtilAll.getPid()); + this.instanceName = UtilAll.getPid() + "#" + System.nanoTime(); } } @@ -178,8 +178,8 @@ public class ClientConfig { } public String getNamesrvAddr() { - if (StringUtils.isNotEmpty(namesrvAddr) && NameServerAddressUtils.NAMESRV_ENDPOINT_PATTERN.matcher(namesrvAddr.trim()).matches()) { - return namesrvAddr.substring(NameServerAddressUtils.ENDPOINT_PREFIX.length()); + if (StringUtils.isNotEmpty(namesrvAddr) && NameServerAddressUtils.validateInstanceEndpoint(namesrvAddr.trim())) { + return NameServerAddressUtils.getNameSrvAddrFromNamesrvEndpoint(namesrvAddr); } return namesrvAddr; } diff --git a/client/src/main/java/org/apache/rocketmq/client/common/ThreadLocalIndex.java b/client/src/main/java/org/apache/rocketmq/client/common/ThreadLocalIndex.java index ab223c3eca..c0b04caea1 100644 --- a/client/src/main/java/org/apache/rocketmq/client/common/ThreadLocalIndex.java +++ b/client/src/main/java/org/apache/rocketmq/client/common/ThreadLocalIndex.java @@ -23,19 +23,14 @@ public class ThreadLocalIndex { private final ThreadLocal threadLocalIndex = new ThreadLocal(); private final Random random = new Random(); - public int getAndIncrement() { + public int incrementAndGet() { Integer index = this.threadLocalIndex.get(); if (null == index) { index = Math.abs(random.nextInt()); - if (index < 0) - index = 0; this.threadLocalIndex.set(index); } index = Math.abs(index + 1); - if (index < 0) - index = 0; - this.threadLocalIndex.set(index); return index; } diff --git a/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueByMachineRoom.java b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueByMachineRoom.java index 37568317cb..31f4e3522e 100644 --- a/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueByMachineRoom.java +++ b/client/src/main/java/org/apache/rocketmq/client/consumer/rebalance/AllocateMessageQueueByMachineRoom.java @@ -49,7 +49,7 @@ public class AllocateMessageQueueByMachineRoom implements AllocateMessageQueueSt int startIndex = mod * currentIndex; int endIndex = startIndex + mod; for (int i = startIndex; i < endIndex; i++) { - result.add(mqAll.get(i)); + result.add(premqAll.get(i)); } if (rem > currentIndex) { result.add(premqAll.get(currentIndex + mod * cidAll.size())); diff --git a/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionContext.java b/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionContext.java new file mode 100644 index 0000000000..5271ade82b --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionContext.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.hook; + +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.common.message.Message; + +public class EndTransactionContext { + private String producerGroup; + private Message message; + private String brokerAddr; + private String msgId; + private String transactionId; + private LocalTransactionState transactionState; + private boolean fromTransactionCheck; + + public String getProducerGroup() { + return producerGroup; + } + + public void setProducerGroup(String producerGroup) { + this.producerGroup = producerGroup; + } + + public Message getMessage() { + return message; + } + + public void setMessage(Message message) { + this.message = message; + } + + public String getBrokerAddr() { + return brokerAddr; + } + + public void setBrokerAddr(String brokerAddr) { + this.brokerAddr = brokerAddr; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public LocalTransactionState getTransactionState() { + return transactionState; + } + + public void setTransactionState(LocalTransactionState transactionState) { + this.transactionState = transactionState; + } + + public boolean isFromTransactionCheck() { + return fromTransactionCheck; + } + + public void setFromTransactionCheck(boolean fromTransactionCheck) { + this.fromTransactionCheck = fromTransactionCheck; + } +} diff --git a/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionHook.java b/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionHook.java new file mode 100644 index 0000000000..834cb27312 --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/hook/EndTransactionHook.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.hook; + +public interface EndTransactionHook { + String hookName(); + + void endTransaction(final EndTransactionContext context); +} diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/ClientRemotingProcessor.java b/client/src/main/java/org/apache/rocketmq/client/impl/ClientRemotingProcessor.java index 3ef60af179..a541b2d379 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/ClientRemotingProcessor.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/ClientRemotingProcessor.java @@ -282,8 +282,6 @@ public class ClientRemotingProcessor extends AsyncNettyRequestProcessor implemen if (requestResponseFuture.getRequestCallback() != null) { requestResponseFuture.getRequestCallback().onSuccess(replyMsg); - } else { - requestResponseFuture.putResponseMessage(replyMsg); } } else { String bornHost = replyMsg.getBornHostString(); diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java index 7a4d556541..63b2045d1d 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/MQClientAPIImpl.java @@ -1467,10 +1467,11 @@ public class MQClientAPIImpl { throw new MQClientException(response.getCode(), response.getRemark()); } - public void deleteSubscriptionGroup(final String addr, final String groupName, final long timeoutMillis) + public void deleteSubscriptionGroup(final String addr, final String groupName, final boolean removeOffset, final long timeoutMillis) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { DeleteSubscriptionGroupRequestHeader requestHeader = new DeleteSubscriptionGroupRequestHeader(); requestHeader.setGroupName(groupName); + requestHeader.setRemoveOffset(removeOffset); RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.DELETE_SUBSCRIPTIONGROUP, requestHeader); RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr), diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/AssignedMessageQueue.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/AssignedMessageQueue.java index fad0b4f1b5..4d18a9be1b 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/AssignedMessageQueue.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/AssignedMessageQueue.java @@ -83,9 +83,12 @@ public class AssignedMessageQueue { return -1; } - public void updatePullOffset(MessageQueue messageQueue, long offset) { + public void updatePullOffset(MessageQueue messageQueue, long offset, ProcessQueue processQueue) { MessageQueueState messageQueueState = assignedMessageQueueState.get(messageQueue); if (messageQueueState != null) { + if (messageQueueState.getProcessQueue() != processQueue) { + return; + } messageQueueState.setPullOffset(offset); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageOrderlyService.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageOrderlyService.java index a171098181..130effad9e 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageOrderlyService.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageOrderlyService.java @@ -478,7 +478,7 @@ public class ConsumeMessageOrderlyService implements ConsumeMessageService { ConsumeReturnType returnType = ConsumeReturnType.SUCCESS; boolean hasException = false; try { - this.processQueue.getLockConsume().lock(); + this.processQueue.getConsumeLock().lock(); if (this.processQueue.isDropped()) { log.warn("consumeMessage, the message queue not be able to consume, because it's dropped. {}", this.messageQueue); @@ -494,7 +494,7 @@ public class ConsumeMessageOrderlyService implements ConsumeMessageService { messageQueue); hasException = true; } finally { - this.processQueue.getLockConsume().unlock(); + this.processQueue.getConsumeLock().unlock(); } if (null == status diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java index 676c03cef4..280de46ff2 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultLitePullConsumerImpl.java @@ -432,8 +432,7 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner { throw new IllegalArgumentException("Topic can not be null or empty."); } setSubscriptionType(SubscriptionType.SUBSCRIBE); - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(defaultLitePullConsumer.getConsumerGroup(), - topic, subExpression); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subExpression); this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData); this.defaultLitePullConsumer.setMessageQueueListener(new MessageQueueListenerImpl()); assignedMessageQueue.setRebalanceImpl(this.rebalanceImpl); @@ -613,9 +612,9 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner { } } - private void updatePullOffset(MessageQueue messageQueue, long nextPullOffset) { + private void updatePullOffset(MessageQueue messageQueue, long nextPullOffset, ProcessQueue processQueue) { if (assignedMessageQueue.getSeekOffset(messageQueue) == -1) { - assignedMessageQueue.updatePullOffset(messageQueue, nextPullOffset); + assignedMessageQueue.updatePullOffset(messageQueue, nextPullOffset, processQueue); } } @@ -741,6 +740,9 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner { } long offset = nextPullOffset(messageQueue); + if (this.isCancelled() || processQueue.isDropped()) { + return; + } long pullDelayTimeMills = 0; try { SubscriptionData subscriptionData; @@ -749,12 +751,13 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner { subscriptionData = rebalanceImpl.getSubscriptionInner().get(topic); } else { String topic = this.messageQueue.getTopic(); - subscriptionData = FilterAPI.buildSubscriptionData(defaultLitePullConsumer.getConsumerGroup(), - topic, SubscriptionData.SUB_ALL); + subscriptionData = FilterAPI.buildSubscriptionData(topic, SubscriptionData.SUB_ALL); } PullResult pullResult = pull(messageQueue, subscriptionData, offset, defaultLitePullConsumer.getPullBatchSize()); - + if (this.isCancelled() || processQueue.isDropped()) { + return; + } switch (pullResult.getPullStatus()) { case FOUND: final Object objLock = messageQueueLock.fetchLockObject(messageQueue); @@ -771,7 +774,7 @@ public class DefaultLitePullConsumerImpl implements MQConsumerInner { default: break; } - updatePullOffset(messageQueue, pullResult.getNextBeginOffset()); + updatePullOffset(messageQueue, pullResult.getNextBeginOffset(), processQueue); } catch (Throwable e) { pullDelayTimeMills = pullTimeDelayMillsWhenException; log.error("An error occurred in pull message process.", e); diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java index afd72a0800..eed5fa43f0 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPullConsumerImpl.java @@ -205,8 +205,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner { } try { - return FilterAPI.buildSubscriptionData(this.defaultMQPullConsumer.getConsumerGroup(), - mq.getTopic(), subExpression); + return FilterAPI.buildSubscriptionData(mq.getTopic(), subExpression); } catch (Exception e) { throw new MQClientException("parse subscription error", e); } @@ -301,8 +300,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner { public void subscriptionAutomatically(final String topic) { if (!this.rebalanceImpl.getSubscriptionInner().containsKey(topic)) { try { - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPullConsumer.getConsumerGroup(), - topic, SubscriptionData.SUB_ALL); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, SubscriptionData.SUB_ALL); this.rebalanceImpl.subscriptionInner.putIfAbsent(topic, subscriptionData); } catch (Exception ignore) { } @@ -365,7 +363,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner { for (String t : topics) { SubscriptionData ms = null; try { - ms = FilterAPI.buildSubscriptionData(this.groupName(), t, SubscriptionData.SUB_ALL); + ms = FilterAPI.buildSubscriptionData(t, SubscriptionData.SUB_ALL); } catch (Exception e) { log.error("parse subscription error", e); } @@ -742,8 +740,7 @@ public class DefaultMQPullConsumerImpl implements MQConsumerInner { Set registerTopics = this.defaultMQPullConsumer.getRegisterTopics(); if (registerTopics != null) { for (final String topic : registerTopics) { - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPullConsumer.getConsumerGroup(), - topic, SubscriptionData.SUB_ALL); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, SubscriptionData.SUB_ALL); this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java index a9dbc3157b..e08f780030 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/DefaultMQPushConsumerImpl.java @@ -827,8 +827,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner { for (final Map.Entry entry : sub.entrySet()) { final String topic = entry.getKey(); final String subString = entry.getValue(); - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(), - topic, subString); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subString); this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData); } } @@ -842,8 +841,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner { break; case CLUSTERING: final String retryTopic = MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup()); - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(), - retryTopic, SubscriptionData.SUB_ALL); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(retryTopic, SubscriptionData.SUB_ALL); this.rebalanceImpl.getSubscriptionInner().put(retryTopic, subscriptionData); break; default: @@ -874,8 +872,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner { public void subscribe(String topic, String subExpression) throws MQClientException { try { - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(), - topic, subExpression); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subExpression); this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData); if (this.mQClientFactory != null) { this.mQClientFactory.sendHeartbeatToAllBrokerWithLock(); @@ -887,8 +884,7 @@ public class DefaultMQPushConsumerImpl implements MQConsumerInner { public void subscribe(String topic, String fullClassName, String filterClassSource) throws MQClientException { try { - SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(this.defaultMQPushConsumer.getConsumerGroup(), - topic, "*"); + SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, "*"); subscriptionData.setSubString(fullClassName); subscriptionData.setClassFilterMode(true); subscriptionData.setFilterClassSource(filterClassSource); diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ProcessQueue.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ProcessQueue.java index 4b9ea62c33..21798d8c6e 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ProcessQueue.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/ProcessQueue.java @@ -44,11 +44,11 @@ public class ProcessQueue { public final static long REBALANCE_LOCK_INTERVAL = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockInterval", "20000")); private final static long PULL_MAX_IDLE_TIME = Long.parseLong(System.getProperty("rocketmq.client.pull.pullMaxIdleTime", "120000")); private final InternalLogger log = ClientLogger.getLog(); - private final ReadWriteLock lockTreeMap = new ReentrantReadWriteLock(); + private final ReadWriteLock treeMapLock = new ReentrantReadWriteLock(); private final TreeMap msgTreeMap = new TreeMap(); private final AtomicLong msgCount = new AtomicLong(); private final AtomicLong msgSize = new AtomicLong(); - private final Lock lockConsume = new ReentrantLock(); + private final Lock consumeLock = new ReentrantLock(); /** * A subset of msgTreeMap, will only be used when orderly consume */ @@ -83,7 +83,7 @@ public class ProcessQueue { for (int i = 0; i < loop; i++) { MessageExt msg = null; try { - this.lockTreeMap.readLock().lockInterruptibly(); + this.treeMapLock.readLock().lockInterruptibly(); try { if (!msgTreeMap.isEmpty() && System.currentTimeMillis() - Long.parseLong(MessageAccessor.getConsumeStartTimeStamp(msgTreeMap.firstEntry().getValue())) > pushConsumer.getConsumeTimeout() * 60 * 1000) { msg = msgTreeMap.firstEntry().getValue(); @@ -92,7 +92,7 @@ public class ProcessQueue { break; } } finally { - this.lockTreeMap.readLock().unlock(); + this.treeMapLock.readLock().unlock(); } } catch (InterruptedException e) { log.error("getExpiredMsg exception", e); @@ -103,7 +103,7 @@ public class ProcessQueue { pushConsumer.sendMessageBack(msg, 3); log.info("send expire msg back. topic={}, msgId={}, storeHost={}, queueId={}, queueOffset={}", msg.getTopic(), msg.getMsgId(), msg.getStoreHost(), msg.getQueueId(), msg.getQueueOffset()); try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { if (!msgTreeMap.isEmpty() && msg.getQueueOffset() == msgTreeMap.firstKey()) { try { @@ -113,7 +113,7 @@ public class ProcessQueue { } } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("getExpiredMsg exception", e); @@ -127,7 +127,7 @@ public class ProcessQueue { public boolean putMessage(final List msgs) { boolean dispatchToConsume = false; try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { int validMsgCnt = 0; for (MessageExt msg : msgs) { @@ -156,7 +156,7 @@ public class ProcessQueue { } } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("putMessage exception", e); @@ -167,13 +167,13 @@ public class ProcessQueue { public long getMaxSpan() { try { - this.lockTreeMap.readLock().lockInterruptibly(); + this.treeMapLock.readLock().lockInterruptibly(); try { if (!this.msgTreeMap.isEmpty()) { return this.msgTreeMap.lastKey() - this.msgTreeMap.firstKey(); } } finally { - this.lockTreeMap.readLock().unlock(); + this.treeMapLock.readLock().unlock(); } } catch (InterruptedException e) { log.error("getMaxSpan exception", e); @@ -186,7 +186,7 @@ public class ProcessQueue { long result = -1; final long now = System.currentTimeMillis(); try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); this.lastConsumeTimestamp = now; try { if (!msgTreeMap.isEmpty()) { @@ -206,7 +206,7 @@ public class ProcessQueue { } } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (Throwable t) { log.error("removeMessage exception", t); @@ -245,12 +245,12 @@ public class ProcessQueue { public void rollback() { try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { this.msgTreeMap.putAll(this.consumingMsgOrderlyTreeMap); this.consumingMsgOrderlyTreeMap.clear(); } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("rollback exception", e); @@ -259,7 +259,7 @@ public class ProcessQueue { public long commit() { try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { Long offset = this.consumingMsgOrderlyTreeMap.lastKey(); msgCount.addAndGet(0 - this.consumingMsgOrderlyTreeMap.size()); @@ -271,7 +271,7 @@ public class ProcessQueue { return offset + 1; } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("commit exception", e); @@ -282,14 +282,14 @@ public class ProcessQueue { public void makeMessageToConsumeAgain(List msgs) { try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { for (MessageExt msg : msgs) { this.consumingMsgOrderlyTreeMap.remove(msg.getQueueOffset()); this.msgTreeMap.put(msg.getQueueOffset(), msg); } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("makeMessageToCosumeAgain exception", e); @@ -300,7 +300,7 @@ public class ProcessQueue { List result = new ArrayList(batchSize); final long now = System.currentTimeMillis(); try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); this.lastConsumeTimestamp = now; try { if (!this.msgTreeMap.isEmpty()) { @@ -319,7 +319,7 @@ public class ProcessQueue { consuming = false; } } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("take Messages exception", e); @@ -330,11 +330,11 @@ public class ProcessQueue { public boolean hasTempMessage() { try { - this.lockTreeMap.readLock().lockInterruptibly(); + this.treeMapLock.readLock().lockInterruptibly(); try { return !this.msgTreeMap.isEmpty(); } finally { - this.lockTreeMap.readLock().unlock(); + this.treeMapLock.readLock().unlock(); } } catch (InterruptedException e) { } @@ -344,7 +344,7 @@ public class ProcessQueue { public void clear() { try { - this.lockTreeMap.writeLock().lockInterruptibly(); + this.treeMapLock.writeLock().lockInterruptibly(); try { this.msgTreeMap.clear(); this.consumingMsgOrderlyTreeMap.clear(); @@ -352,7 +352,7 @@ public class ProcessQueue { this.msgSize.set(0); this.queueOffsetMax = 0L; } finally { - this.lockTreeMap.writeLock().unlock(); + this.treeMapLock.writeLock().unlock(); } } catch (InterruptedException e) { log.error("rollback exception", e); @@ -367,8 +367,8 @@ public class ProcessQueue { this.lastLockTimestamp = lastLockTimestamp; } - public Lock getLockConsume() { - return lockConsume; + public Lock getConsumeLock() { + return consumeLock; } public long getLastPullTimestamp() { @@ -397,7 +397,7 @@ public class ProcessQueue { public void fillProcessQueueInfo(final ProcessQueueInfo info) { try { - this.lockTreeMap.readLock().lockInterruptibly(); + this.treeMapLock.readLock().lockInterruptibly(); if (!this.msgTreeMap.isEmpty()) { info.setCachedMsgMinOffset(this.msgTreeMap.firstKey()); @@ -421,7 +421,7 @@ public class ProcessQueue { info.setLastConsumeTimestamp(this.lastConsumeTimestamp); } catch (Exception e) { } finally { - this.lockTreeMap.readLock().unlock(); + this.treeMapLock.readLock().unlock(); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java index e5166f35b5..9582391622 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/consumer/RebalancePushImpl.java @@ -88,11 +88,11 @@ public class RebalancePushImpl extends RebalanceImpl { if (this.defaultMQPushConsumerImpl.isConsumeOrderly() && MessageModel.CLUSTERING.equals(this.defaultMQPushConsumerImpl.messageModel())) { try { - if (pq.getLockConsume().tryLock(1000, TimeUnit.MILLISECONDS)) { + if (pq.getConsumeLock().tryLock(1000, TimeUnit.MILLISECONDS)) { try { return this.unlockDelay(mq, pq); } finally { - pq.getLockConsume().unlock(); + pq.getConsumeLock().unlock(); } } else { log.warn("[WRONG]mq is consuming, so can not unlock it, {}. maybe hanged for a while, {}", diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java index 48cc18851d..81e6d8468e 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/factory/MQClientInstance.java @@ -1043,6 +1043,11 @@ public class MQClientInstance { slave = brokerId != MixAll.MASTER_ID; found = brokerAddr != null; + if (!found && slave) { + brokerAddr = map.get(brokerId + 1); + found = brokerAddr != null; + } + if (!found && !onlyThisBroker) { Entry entry = map.entrySet().iterator().next(); brokerAddr = entry.getValue(); @@ -1100,7 +1105,7 @@ public class MQClientInstance { return null; } - public void resetOffset(String topic, String group, Map offsetTable) { + public synchronized void resetOffset(String topic, String group, Map offsetTable) { DefaultMQPushConsumerImpl consumer = null; try { MQConsumerInner impl = this.consumerTable.get(group); @@ -1209,6 +1214,9 @@ public class MQClientInstance { public ConsumerRunningInfo consumerRunningInfo(final String consumerGroup) { MQConsumerInner mqConsumerInner = this.consumerTable.get(consumerGroup); + if (mqConsumerInner == null) { + return null; + } ConsumerRunningInfo consumerRunningInfo = mqConsumerInner.consumerRunningInfo(); diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java b/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java index 2f9146d910..fac3ed3561 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/producer/DefaultMQProducerImpl.java @@ -44,6 +44,8 @@ import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.exception.RequestTimeoutException; import org.apache.rocketmq.client.hook.CheckForbiddenContext; import org.apache.rocketmq.client.hook.CheckForbiddenHook; +import org.apache.rocketmq.client.hook.EndTransactionContext; +import org.apache.rocketmq.client.hook.EndTransactionHook; import org.apache.rocketmq.client.hook.SendMessageContext; import org.apache.rocketmq.client.hook.SendMessageHook; import org.apache.rocketmq.client.impl.CommunicationMode; @@ -101,6 +103,7 @@ public class DefaultMQProducerImpl implements MQProducerInner { private final ConcurrentMap topicPublishInfoTable = new ConcurrentHashMap(); private final ArrayList sendMessageHookList = new ArrayList(); + private final ArrayList endTransactionHookList = new ArrayList(); private final RPCHook rpcHook; private final BlockingQueue asyncSenderThreadPoolQueue; private final ExecutorService defaultAsyncSenderExecutor; @@ -171,6 +174,11 @@ public class DefaultMQProducerImpl implements MQProducerInner { log.info("register sendMessage Hook, {}", hook.hookName()); } + public void registerEndTransactionHook(final EndTransactionHook hook) { + this.endTransactionHookList.add(hook); + log.info("register endTransaction Hook, {}", hook.hookName()); + } + public void start() throws MQClientException { this.start(true); } @@ -386,6 +394,7 @@ public class DefaultMQProducerImpl implements MQProducerInner { if (exception != null) { remark = "checkLocalTransactionState Exception: " + RemotingHelper.exceptionSimpleDesc(exception); } + doExecuteEndTransactionHook(msg, uniqueKey, brokerAddr, localTransactionState, true); try { DefaultMQProducerImpl.this.mQClientFactory.getMQClientAPIImpl().endTransactionOneway(brokerAddr, thisHeader, remark, @@ -967,6 +976,36 @@ public class DefaultMQProducerImpl implements MQProducerInner { } } + public boolean hasEndTransactionHook() { + return !this.endTransactionHookList.isEmpty(); + } + + public void executeEndTransactionHook(final EndTransactionContext context) { + if (!this.endTransactionHookList.isEmpty()) { + for (EndTransactionHook hook : this.endTransactionHookList) { + try { + hook.endTransaction(context); + } catch (Throwable e) { + log.warn("failed to executeEndTransactionHook", e); + } + } + } + } + + public void doExecuteEndTransactionHook(Message msg, String msgId, String brokerAddr, LocalTransactionState state, + boolean fromTransactionCheck) { + if (hasEndTransactionHook()) { + EndTransactionContext context = new EndTransactionContext(); + context.setProducerGroup(defaultMQProducer.getProducerGroup()); + context.setBrokerAddr(brokerAddr); + context.setMessage(msg); + context.setMsgId(msgId); + context.setTransactionId(msg.getTransactionId()); + context.setTransactionState(state); + context.setFromTransactionCheck(fromTransactionCheck); + executeEndTransactionHook(context); + } + } /** * DEFAULT ONEWAY ------------------------------------------------------- */ @@ -1266,7 +1305,7 @@ public class DefaultMQProducerImpl implements MQProducerInner { } try { - this.endTransaction(sendResult, localTransactionState, localException); + this.endTransaction(msg, sendResult, localTransactionState, localException); } catch (Exception e) { log.warn("local transaction execute " + localTransactionState + ", but end broker transaction failed", e); } @@ -1290,6 +1329,7 @@ public class DefaultMQProducerImpl implements MQProducerInner { } public void endTransaction( + final Message msg, final SendResult sendResult, final LocalTransactionState localTransactionState, final Throwable localException) throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException { @@ -1318,6 +1358,7 @@ public class DefaultMQProducerImpl implements MQProducerInner { break; } + doExecuteEndTransactionHook(msg, sendResult.getMsgId(), brokerAddr, localTransactionState, false); requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup()); requestHeader.setTranStateTableOffset(sendResult.getQueueOffset()); requestHeader.setMsgId(sendResult.getMsgId()); diff --git a/client/src/main/java/org/apache/rocketmq/client/impl/producer/TopicPublishInfo.java b/client/src/main/java/org/apache/rocketmq/client/impl/producer/TopicPublishInfo.java index deb02cff28..2f8337edef 100644 --- a/client/src/main/java/org/apache/rocketmq/client/impl/producer/TopicPublishInfo.java +++ b/client/src/main/java/org/apache/rocketmq/client/impl/producer/TopicPublishInfo.java @@ -70,9 +70,9 @@ public class TopicPublishInfo { if (lastBrokerName == null) { return selectOneMessageQueue(); } else { - int index = this.sendWhichQueue.getAndIncrement(); for (int i = 0; i < this.messageQueueList.size(); i++) { - int pos = Math.abs(index++) % this.messageQueueList.size(); + int index = this.sendWhichQueue.incrementAndGet(); + int pos = Math.abs(index) % this.messageQueueList.size(); if (pos < 0) pos = 0; MessageQueue mq = this.messageQueueList.get(pos); @@ -85,7 +85,7 @@ public class TopicPublishInfo { } public MessageQueue selectOneMessageQueue() { - int index = this.sendWhichQueue.getAndIncrement(); + int index = this.sendWhichQueue.incrementAndGet(); int pos = Math.abs(index) % this.messageQueueList.size(); if (pos < 0) pos = 0; diff --git a/client/src/main/java/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java b/client/src/main/java/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java index 72d43476f8..827d97265f 100644 --- a/client/src/main/java/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java +++ b/client/src/main/java/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java @@ -80,7 +80,7 @@ public class LatencyFaultToleranceImpl implements LatencyFaultTolerance if (half <= 0) { return tmpList.get(0).getName(); } else { - final int i = this.whichItemWorst.getAndIncrement() % half; + final int i = this.whichItemWorst.incrementAndGet() % half; return tmpList.get(i).getName(); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/latency/MQFaultStrategy.java b/client/src/main/java/org/apache/rocketmq/client/latency/MQFaultStrategy.java index 651d043a6e..ea3d07e6d0 100644 --- a/client/src/main/java/org/apache/rocketmq/client/latency/MQFaultStrategy.java +++ b/client/src/main/java/org/apache/rocketmq/client/latency/MQFaultStrategy.java @@ -58,7 +58,7 @@ public class MQFaultStrategy { public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) { if (this.sendLatencyFaultEnable) { try { - int index = tpInfo.getSendWhichQueue().getAndIncrement(); + int index = tpInfo.getSendWhichQueue().incrementAndGet(); for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) { int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size(); if (pos < 0) @@ -74,7 +74,7 @@ public class MQFaultStrategy { final MessageQueue mq = tpInfo.selectOneMessageQueue(); if (notBestBroker != null) { mq.setBrokerName(notBestBroker); - mq.setQueueId(tpInfo.getSendWhichQueue().getAndIncrement() % writeQueueNums); + mq.setQueueId(tpInfo.getSendWhichQueue().incrementAndGet() % writeQueueNums); } return mq; } else { diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java b/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java index 24caf14054..1c4a9315a8 100644 --- a/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java +++ b/client/src/main/java/org/apache/rocketmq/client/producer/DefaultMQProducer.java @@ -29,6 +29,7 @@ import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; import org.apache.rocketmq.client.log.ClientLogger; import org.apache.rocketmq.client.trace.AsyncTraceDispatcher; import org.apache.rocketmq.client.trace.TraceDispatcher; +import org.apache.rocketmq.client.trace.hook.EndTransactionTraceHookImpl; import org.apache.rocketmq.client.trace.hook.SendMessageTraceHookImpl; import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.message.Message; @@ -167,6 +168,8 @@ public class DefaultMQProducer extends ClientConfig implements MQProducer { traceDispatcher = dispatcher; this.defaultMQProducerImpl.registerSendMessageHook( new SendMessageTraceHookImpl(traceDispatcher)); + this.defaultMQProducerImpl.registerEndTransactionHook( + new EndTransactionTraceHookImpl(traceDispatcher)); } catch (Throwable e) { log.error("system mqtrace hook init failed ,maybe can't send msg trace data"); } @@ -252,6 +255,8 @@ public class DefaultMQProducer extends ClientConfig implements MQProducer { traceDispatcher = dispatcher; this.getDefaultMQProducerImpl().registerSendMessageHook( new SendMessageTraceHookImpl(traceDispatcher)); + this.defaultMQProducerImpl.registerEndTransactionHook( + new EndTransactionTraceHookImpl(traceDispatcher)); } catch (Throwable e) { log.error("system mqtrace hook init failed ,maybe can't send msg trace data"); } @@ -916,24 +921,24 @@ public class DefaultMQProducer extends ClientConfig implements MQProducer { long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { return this.defaultMQProducerImpl.send(batch(msgs), messageQueue, timeout); } - + @Override public void send(Collection msgs, SendCallback sendCallback) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { this.defaultMQProducerImpl.send(batch(msgs), sendCallback); } - + @Override public void send(Collection msgs, SendCallback sendCallback, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { this.defaultMQProducerImpl.send(batch(msgs), sendCallback, timeout); } - + @Override public void send(Collection msgs, MessageQueue mq, SendCallback sendCallback) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { this.defaultMQProducerImpl.send(batch(msgs), queueWithNamespace(mq), sendCallback); } - + @Override public void send(Collection msgs, MessageQueue mq, SendCallback sendCallback, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException { diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/TransactionMQProducer.java b/client/src/main/java/org/apache/rocketmq/client/producer/TransactionMQProducer.java index 63b512df7d..4eb758df40 100644 --- a/client/src/main/java/org/apache/rocketmq/client/producer/TransactionMQProducer.java +++ b/client/src/main/java/org/apache/rocketmq/client/producer/TransactionMQProducer.java @@ -51,6 +51,10 @@ public class TransactionMQProducer extends DefaultMQProducer { super(namespace, producerGroup, rpcHook); } + public TransactionMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook, boolean enableMsgTrace, final String customizedTraceTopic) { + super(namespace, producerGroup, rpcHook, enableMsgTrace, customizedTraceTopic); + } + @Override public void start() throws MQClientException { this.defaultMQProducerImpl.initTransactionEnv(); diff --git a/client/src/main/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHash.java b/client/src/main/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHash.java index 11e2822b22..ba8ea8b58a 100644 --- a/client/src/main/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHash.java +++ b/client/src/main/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHash.java @@ -25,12 +25,10 @@ public class SelectMessageQueueByHash implements MessageQueueSelector { @Override public MessageQueue select(List mqs, Message msg, Object arg) { - int value = arg.hashCode(); + int value = arg.hashCode() % mqs.size(); if (value < 0) { value = Math.abs(value); } - - value = value % mqs.size(); return mqs.get(value); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java b/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java index 1af6e60aab..8210b41cd4 100644 --- a/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java +++ b/client/src/main/java/org/apache/rocketmq/client/trace/AsyncTraceDispatcher.java @@ -370,7 +370,7 @@ public class AsyncTraceDispatcher implements TraceDispatcher { @Override public void onException(Throwable e) { - log.info("send trace data ,the traceData is " + data); + log.error("send trace data failed, the traceData is {}", data, e); } }; if (traceBrokerSet.isEmpty()) { @@ -387,7 +387,7 @@ public class AsyncTraceDispatcher implements TraceDispatcher { filterMqs.add(queue); } } - int index = sendWhichQueue.getAndIncrement(); + int index = sendWhichQueue.incrementAndGet(); int pos = Math.abs(index) % filterMqs.size(); if (pos < 0) { pos = 0; @@ -398,7 +398,7 @@ public class AsyncTraceDispatcher implements TraceDispatcher { } } catch (Exception e) { - log.info("send trace data,the traceData is" + data); + log.error("send trace data failed, the traceData is {}", data, e); } } diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java index f93aa38b82..70c147e1eb 100644 --- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java +++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceBean.java @@ -16,6 +16,7 @@ */ package org.apache.rocketmq.client.trace; +import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.common.UtilAll; import org.apache.rocketmq.common.message.MessageType; @@ -32,7 +33,9 @@ public class TraceBean { private int retryTimes; private int bodyLength; private MessageType msgType; - + private LocalTransactionState transactionState; + private String transactionId; + private boolean fromTransactionCheck; public MessageType getMsgType() { return msgType; @@ -141,4 +144,28 @@ public class TraceBean { public void setBodyLength(int bodyLength) { this.bodyLength = bodyLength; } + + public LocalTransactionState getTransactionState() { + return transactionState; + } + + public void setTransactionState(LocalTransactionState transactionState) { + this.transactionState = transactionState; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public boolean isFromTransactionCheck() { + return fromTransactionCheck; + } + + public void setFromTransactionCheck(boolean fromTransactionCheck) { + this.fromTransactionCheck = fromTransactionCheck; + } } diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java index 27622cd302..1ad4b61051 100644 --- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java +++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceConstants.java @@ -25,4 +25,20 @@ public class TraceConstants { public static final char FIELD_SPLITOR = (char) 2; public static final String TRACE_INSTANCE_NAME = "PID_CLIENT_INNER_TRACE_PRODUCER"; public static final String TRACE_TOPIC_PREFIX = TopicValidator.SYSTEM_TOPIC_PREFIX + "TRACE_DATA_"; + public static final String TO_PREFIX = "To_"; + public static final String FROM_PREFIX = "From_"; + public static final String END_TRANSACTION = "EndTransaction"; + public static final String ROCKETMQ_SERVICE = "rocketmq"; + public static final String ROCKETMQ_SUCCESS = "rocketmq.success"; + public static final String ROCKETMQ_TAGS = "rocketmq.tags"; + public static final String ROCKETMQ_KEYS = "rocketmq.keys"; + public static final String ROCKETMQ_SOTRE_HOST = "rocketmq.store_host"; + public static final String ROCKETMQ_BODY_LENGTH = "rocketmq.body_length"; + public static final String ROCKETMQ_MSG_ID = "rocketmq.mgs_id"; + public static final String ROCKETMQ_MSG_TYPE = "rocketmq.mgs_type"; + public static final String ROCKETMQ_REGION_ID = "rocketmq.region_id"; + public static final String ROCKETMQ_TRANSACTION_ID = "rocketmq.transaction_id"; + public static final String ROCKETMQ_TRANSACTION_STATE = "rocketmq.transaction_state"; + public static final String ROCKETMQ_IS_FROM_TRANSACTION_CHECK = "rocketmq.is_from_transaction_check"; + public static final String ROCKETMQ_RETRY_TIMERS = "rocketmq.retry_times"; } diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java index acf0dea8b8..2625d353f8 100644 --- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java +++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceDataEncoder.java @@ -16,6 +16,7 @@ */ package org.apache.rocketmq.client.trace; +import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.common.message.MessageType; import java.util.ArrayList; @@ -109,6 +110,27 @@ public class TraceDataEncoder { subAfterContext.setGroupName(line[8]); } resList.add(subAfterContext); + } else if (line[0].equals(TraceType.EndTransaction.name())) { + TraceContext endTransactionContext = new TraceContext(); + endTransactionContext.setTraceType(TraceType.EndTransaction); + endTransactionContext.setTimeStamp(Long.parseLong(line[1])); + endTransactionContext.setRegionId(line[2]); + endTransactionContext.setGroupName(line[3]); + TraceBean bean = new TraceBean(); + bean.setTopic(line[4]); + bean.setMsgId(line[5]); + bean.setTags(line[6]); + bean.setKeys(line[7]); + bean.setStoreHost(line[8]); + bean.setMsgType(MessageType.values()[Integer.parseInt(line[9])]); + bean.setClientHost(line[10]); + bean.setTransactionId(line[11]); + bean.setTransactionState(LocalTransactionState.valueOf(line[12])); + bean.setFromTransactionCheck(Boolean.parseBoolean(line[13])); + + endTransactionContext.setTraceBeans(new ArrayList(1)); + endTransactionContext.getTraceBeans().add(bean); + resList.add(endTransactionContext); } } return resList; @@ -173,10 +195,28 @@ public class TraceDataEncoder { .append(ctx.getContextCode()).append(TraceConstants.CONTENT_SPLITOR) .append(ctx.getTimeStamp()).append(TraceConstants.CONTENT_SPLITOR) .append(ctx.getGroupName()).append(TraceConstants.FIELD_SPLITOR); - + } } break; + case EndTransaction: { + TraceBean bean = ctx.getTraceBeans().get(0); + sb.append(ctx.getTraceType()).append(TraceConstants.CONTENT_SPLITOR)// + .append(ctx.getTimeStamp()).append(TraceConstants.CONTENT_SPLITOR)// + .append(ctx.getRegionId()).append(TraceConstants.CONTENT_SPLITOR)// + .append(ctx.getGroupName()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getTopic()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getMsgId()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getTags()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getKeys()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getStoreHost()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getMsgType().ordinal()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getClientHost()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getTransactionId()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.getTransactionState().name()).append(TraceConstants.CONTENT_SPLITOR)// + .append(bean.isFromTransactionCheck()).append(TraceConstants.FIELD_SPLITOR); + } + break; default: } transferBean.setTransData(sb.toString()); diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/TraceType.java b/client/src/main/java/org/apache/rocketmq/client/trace/TraceType.java index 79b19c17e4..8870ddcbdb 100644 --- a/client/src/main/java/org/apache/rocketmq/client/trace/TraceType.java +++ b/client/src/main/java/org/apache/rocketmq/client/trace/TraceType.java @@ -20,4 +20,5 @@ public enum TraceType { Pub, SubBefore, SubAfter, + EndTransaction, } diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/ConsumeMessageOpenTracingHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/ConsumeMessageOpenTracingHookImpl.java new file mode 100644 index 0000000000..28fccae06f --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/ConsumeMessageOpenTracingHookImpl.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.trace.hook; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMapAdapter; +import io.opentracing.tag.Tags; +import org.apache.rocketmq.client.hook.ConsumeMessageContext; +import org.apache.rocketmq.client.hook.ConsumeMessageHook; +import org.apache.rocketmq.client.trace.TraceConstants; +import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.protocol.NamespaceUtil; + +import java.util.ArrayList; +import java.util.List; + + +public class ConsumeMessageOpenTracingHookImpl implements ConsumeMessageHook { + + private Tracer tracer; + + public ConsumeMessageOpenTracingHookImpl(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public String hookName() { + return "ConsumeMessageOpenTracingHook"; + } + + @Override + public void consumeMessageBefore(ConsumeMessageContext context) { + if (context == null || context.getMsgList() == null || context.getMsgList().isEmpty()) { + return; + } + List spanList = new ArrayList<>(); + for (MessageExt msg : context.getMsgList()) { + if (msg == null) { + continue; + } + Tracer.SpanBuilder spanBuilder = tracer + .buildSpan(TraceConstants.FROM_PREFIX + msg.getTopic()) + .withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_CONSUMER); + SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(msg.getProperties())); + if (spanContext != null) { + spanBuilder.asChildOf(spanContext); + } + Span span = spanBuilder.start(); + + span.setTag(Tags.PEER_SERVICE, TraceConstants.ROCKETMQ_SERVICE); + span.setTag(Tags.MESSAGE_BUS_DESTINATION, NamespaceUtil.withoutNamespace(msg.getTopic())); + span.setTag(TraceConstants.ROCKETMQ_MSG_ID, msg.getMsgId()); + span.setTag(TraceConstants.ROCKETMQ_TAGS, msg.getTags()); + span.setTag(TraceConstants.ROCKETMQ_KEYS, msg.getKeys()); + span.setTag(TraceConstants.ROCKETMQ_BODY_LENGTH, msg.getStoreSize()); + span.setTag(TraceConstants.ROCKETMQ_RETRY_TIMERS, msg.getReconsumeTimes()); + span.setTag(TraceConstants.ROCKETMQ_REGION_ID, msg.getProperty(MessageConst.PROPERTY_MSG_REGION)); + spanList.add(span); + } + context.setMqTraceContext(spanList); + } + + @Override + public void consumeMessageAfter(ConsumeMessageContext context) { + if (context == null || context.getMsgList() == null || context.getMsgList().isEmpty()) { + return; + } + List spanList = (List) context.getMqTraceContext(); + if (spanList == null) { + return; + } + for (Span span : spanList) { + span.setTag(TraceConstants.ROCKETMQ_SUCCESS, context.isSuccess()); + span.finish(); + } + } +} diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java new file mode 100644 index 0000000000..62d310f196 --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionOpenTracingHookImpl.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.trace.hook; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMapAdapter; +import io.opentracing.tag.Tags; +import org.apache.rocketmq.client.hook.EndTransactionContext; +import org.apache.rocketmq.client.hook.EndTransactionHook; +import org.apache.rocketmq.client.trace.TraceConstants; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageType; + +public class EndTransactionOpenTracingHookImpl implements EndTransactionHook { + + private Tracer tracer; + + public EndTransactionOpenTracingHookImpl(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public String hookName() { + return "EndTransactionOpenTracingHook"; + } + + @Override + public void endTransaction(EndTransactionContext context) { + if (context == null) { + return; + } + Message msg = context.getMessage(); + Tracer.SpanBuilder spanBuilder = tracer + .buildSpan(TraceConstants.END_TRANSACTION) + .withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_PRODUCER); + SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(msg.getProperties())); + if (spanContext != null) { + spanBuilder.asChildOf(spanContext); + } + + Span span = spanBuilder.start(); + span.setTag(Tags.PEER_SERVICE, TraceConstants.ROCKETMQ_SERVICE); + span.setTag(Tags.MESSAGE_BUS_DESTINATION, msg.getTopic()); + span.setTag(TraceConstants.ROCKETMQ_TAGS, msg.getTags()); + span.setTag(TraceConstants.ROCKETMQ_KEYS, msg.getKeys()); + span.setTag(TraceConstants.ROCKETMQ_SOTRE_HOST, context.getBrokerAddr()); + span.setTag(TraceConstants.ROCKETMQ_MSG_ID, context.getMsgId()); + span.setTag(TraceConstants.ROCKETMQ_MSG_TYPE, MessageType.Trans_msg_Commit.name()); + span.setTag(TraceConstants.ROCKETMQ_TRANSACTION_ID, context.getTransactionId()); + span.setTag(TraceConstants.ROCKETMQ_TRANSACTION_STATE, context.getTransactionState().name()); + span.setTag(TraceConstants.ROCKETMQ_IS_FROM_TRANSACTION_CHECK, context.isFromTransactionCheck()); + span.finish(); + } + +} diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionTraceHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionTraceHookImpl.java new file mode 100644 index 0000000000..cbd755ba39 --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/EndTransactionTraceHookImpl.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.trace.hook; + +import org.apache.rocketmq.client.hook.EndTransactionContext; +import org.apache.rocketmq.client.hook.EndTransactionHook; +import org.apache.rocketmq.client.trace.AsyncTraceDispatcher; +import org.apache.rocketmq.client.trace.TraceBean; +import org.apache.rocketmq.client.trace.TraceContext; +import org.apache.rocketmq.client.trace.TraceDispatcher; +import org.apache.rocketmq.client.trace.TraceType; +import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.common.message.MessageType; +import org.apache.rocketmq.common.protocol.NamespaceUtil; + +import java.util.ArrayList; + +public class EndTransactionTraceHookImpl implements EndTransactionHook { + + private TraceDispatcher localDispatcher; + + public EndTransactionTraceHookImpl(TraceDispatcher localDispatcher) { + this.localDispatcher = localDispatcher; + } + + @Override + public String hookName() { + return "EndTransactionTraceHook"; + } + + @Override + public void endTransaction(EndTransactionContext context) { + //if it is message trace data,then it doesn't recorded + if (context == null || context.getMessage().getTopic().startsWith(((AsyncTraceDispatcher) localDispatcher).getTraceTopicName())) { + return; + } + Message msg = context.getMessage(); + //build the context content of TuxeTraceContext + TraceContext tuxeContext = new TraceContext(); + tuxeContext.setTraceBeans(new ArrayList(1)); + tuxeContext.setTraceType(TraceType.EndTransaction); + tuxeContext.setGroupName(NamespaceUtil.withoutNamespace(context.getProducerGroup())); + //build the data bean object of message trace + TraceBean traceBean = new TraceBean(); + traceBean.setTopic(NamespaceUtil.withoutNamespace(context.getMessage().getTopic())); + traceBean.setTags(context.getMessage().getTags()); + traceBean.setKeys(context.getMessage().getKeys()); + traceBean.setStoreHost(context.getBrokerAddr()); + traceBean.setMsgType(MessageType.Trans_msg_Commit); + traceBean.setClientHost(((AsyncTraceDispatcher)localDispatcher).getHostProducer().getmQClientFactory().getClientId()); + traceBean.setMsgId(context.getMsgId()); + traceBean.setTransactionState(context.getTransactionState()); + traceBean.setTransactionId(context.getTransactionId()); + traceBean.setFromTransactionCheck(context.isFromTransactionCheck()); + String regionId = msg.getProperty(MessageConst.PROPERTY_MSG_REGION); + if (regionId == null || regionId.isEmpty()) { + regionId = MixAll.DEFAULT_TRACE_REGION_ID; + } + tuxeContext.setRegionId(regionId); + tuxeContext.getTraceBeans().add(traceBean); + tuxeContext.setTimeStamp(System.currentTimeMillis()); + localDispatcher.append(tuxeContext); + } + +} diff --git a/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java b/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java new file mode 100644 index 0000000000..60c18a22a7 --- /dev/null +++ b/client/src/main/java/org/apache/rocketmq/client/trace/hook/SendMessageOpenTracingHookImpl.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.trace.hook; + +import io.opentracing.Span; +import io.opentracing.SpanContext; +import io.opentracing.Tracer; +import io.opentracing.propagation.Format; +import io.opentracing.propagation.TextMapAdapter; +import io.opentracing.tag.Tags; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.hook.SendMessageHook; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.client.trace.TraceConstants; +import org.apache.rocketmq.common.message.Message; + +public class SendMessageOpenTracingHookImpl implements SendMessageHook { + + private Tracer tracer; + + public SendMessageOpenTracingHookImpl(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public String hookName() { + return "SendMessageOpenTracingHook"; + } + + @Override + public void sendMessageBefore(SendMessageContext context) { + if (context == null) { + return; + } + Message msg = context.getMessage(); + Tracer.SpanBuilder spanBuilder = tracer + .buildSpan(TraceConstants.TO_PREFIX + msg.getTopic()) + .withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_PRODUCER); + SpanContext spanContext = tracer.extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(msg.getProperties())); + if (spanContext != null) { + spanBuilder.asChildOf(spanContext); + } + Span span = spanBuilder.start(); + tracer.inject(span.context(), Format.Builtin.TEXT_MAP, new TextMapAdapter(msg.getProperties())); + span.setTag(Tags.PEER_SERVICE, TraceConstants.ROCKETMQ_SERVICE); + span.setTag(Tags.MESSAGE_BUS_DESTINATION, msg.getTopic()); + span.setTag(TraceConstants.ROCKETMQ_TAGS, msg.getTags()); + span.setTag(TraceConstants.ROCKETMQ_KEYS, msg.getKeys()); + span.setTag(TraceConstants.ROCKETMQ_SOTRE_HOST, context.getBrokerAddr()); + span.setTag(TraceConstants.ROCKETMQ_MSG_TYPE, context.getMsgType().name()); + span.setTag(TraceConstants.ROCKETMQ_BODY_LENGTH, msg.getBody().length); + context.setMqTraceContext(span); + } + + @Override + public void sendMessageAfter(SendMessageContext context) { + if (context == null || context.getMqTraceContext() == null) { + return; + } + if (context.getSendResult() == null) { + return; + } + + if (context.getSendResult().getRegionId() == null) { + return; + } + + Span span = (Span) context.getMqTraceContext(); + span.setTag(TraceConstants.ROCKETMQ_SUCCESS, context.getSendResult().getSendStatus().equals(SendStatus.SEND_OK)); + span.setTag(TraceConstants.ROCKETMQ_MSG_ID, context.getSendResult().getMsgId()); + span.setTag(TraceConstants.ROCKETMQ_REGION_ID, context.getSendResult().getRegionId()); + span.finish(); + } +} diff --git a/client/src/test/java/org/apache/rocketmq/client/common/ThreadLocalIndexTest.java b/client/src/test/java/org/apache/rocketmq/client/common/ThreadLocalIndexTest.java index 1be93ce0f3..de35b9181b 100644 --- a/client/src/test/java/org/apache/rocketmq/client/common/ThreadLocalIndexTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/common/ThreadLocalIndexTest.java @@ -22,11 +22,18 @@ import static org.assertj.core.api.Assertions.assertThat; public class ThreadLocalIndexTest { @Test - public void testGetAndIncrement() throws Exception { + public void testIncrementAndGet() throws Exception { ThreadLocalIndex localIndex = new ThreadLocalIndex(); - int initialVal = localIndex.getAndIncrement(); + int initialVal = localIndex.incrementAndGet(); - assertThat(localIndex.getAndIncrement()).isEqualTo(initialVal + 1); + assertThat(localIndex.incrementAndGet()).isEqualTo(initialVal + 1); + } + + @Test + public void testIncrementAndGet2() throws Exception { + ThreadLocalIndex localIndex = new ThreadLocalIndex(); + int initialVal = localIndex.incrementAndGet(); + assertThat(initialVal >= 0); } } \ No newline at end of file diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java index 66832706eb..04b760eec2 100644 --- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultLitePullConsumerTest.java @@ -20,8 +20,12 @@ package org.apache.rocketmq.client.consumer; import java.io.ByteArrayOutputStream; import java.lang.reflect.Field; import java.net.InetSocketAddress; -import java.util.*; - +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.consumer.store.OffsetStore; import org.apache.rocketmq.client.consumer.store.ReadOffsetType; @@ -46,16 +50,15 @@ import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader; import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; +import org.apache.rocketmq.remoting.RPCHook; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; @@ -69,8 +72,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -@RunWith(PowerMockRunner.class) -@PrepareForTest(DefaultLitePullConsumerImpl.class) +@RunWith(MockitoJUnitRunner.class) public class DefaultLitePullConsumerTest { @Spy private MQClientInstance mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig()); @@ -92,7 +94,10 @@ public class DefaultLitePullConsumerTest { @Before public void init() throws Exception { - PowerMockito.suppress(PowerMockito.method(DefaultLitePullConsumerImpl.class, "updateTopicSubscribeInfoWhenSubscriptionChanged")); + ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); + factoryTable.forEach((s, instance) -> instance.shutdown()); + factoryTable.clear(); + Field field = MQClientInstance.class.getDeclaredField("rebalanceService"); field.setAccessible(true); RebalanceService rebalanceService = (RebalanceService) field.get(mQClientFactory); @@ -180,7 +185,9 @@ public class DefaultLitePullConsumerTest { when(mQAdminImpl.minOffset(any(MessageQueue.class))).thenReturn(0L); when(mQAdminImpl.maxOffset(any(MessageQueue.class))).thenReturn(500L); MessageQueue messageQueue = createMessageQueue(); - litePullConsumer.assign(Collections.singletonList(messageQueue)); + List messageQueues = Collections.singletonList(messageQueue); + litePullConsumer.assign(messageQueues); + litePullConsumer.pause(messageQueues); long offset = litePullConsumer.committed(messageQueue); litePullConsumer.seek(messageQueue, offset); Field field = DefaultLitePullConsumerImpl.class.getDeclaredField("assignedMessageQueue"); @@ -196,7 +203,9 @@ public class DefaultLitePullConsumerTest { when(mQAdminImpl.minOffset(any(MessageQueue.class))).thenReturn(0L); when(mQAdminImpl.maxOffset(any(MessageQueue.class))).thenReturn(500L); MessageQueue messageQueue = createMessageQueue(); - litePullConsumer.assign(Collections.singletonList(messageQueue)); + List messageQueues = Collections.singletonList(messageQueue); + litePullConsumer.assign(messageQueues); + litePullConsumer.pause(messageQueues); litePullConsumer.seekToBegin(messageQueue); Field field = DefaultLitePullConsumerImpl.class.getDeclaredField("assignedMessageQueue"); field.setAccessible(true); @@ -211,7 +220,9 @@ public class DefaultLitePullConsumerTest { when(mQAdminImpl.minOffset(any(MessageQueue.class))).thenReturn(0L); when(mQAdminImpl.maxOffset(any(MessageQueue.class))).thenReturn(500L); MessageQueue messageQueue = createMessageQueue(); - litePullConsumer.assign(Collections.singletonList(messageQueue)); + List messageQueues = Collections.singletonList(messageQueue); + litePullConsumer.assign(messageQueues); + litePullConsumer.pause(messageQueues); litePullConsumer.seekToEnd(messageQueue); Field field = DefaultLitePullConsumerImpl.class.getDeclaredField("assignedMessageQueue"); field.setAccessible(true); @@ -226,7 +237,9 @@ public class DefaultLitePullConsumerTest { when(mQAdminImpl.minOffset(any(MessageQueue.class))).thenReturn(0L); when(mQAdminImpl.maxOffset(any(MessageQueue.class))).thenReturn(100L); MessageQueue messageQueue = createMessageQueue(); - litePullConsumer.assign(Collections.singletonList(messageQueue)); + List messageQueues = Collections.singletonList(messageQueue); + litePullConsumer.assign(messageQueues); + litePullConsumer.pause(messageQueues); try { litePullConsumer.seek(messageQueue, -1); failBecauseExceptionWasNotThrown(MQClientException.class); @@ -515,9 +528,6 @@ public class DefaultLitePullConsumerTest { public void testConsumerAfterShutdown() throws Exception { DefaultLitePullConsumer defaultLitePullConsumer = createSubscribeLitePullConsumer(); - DefaultLitePullConsumer mockConsumer = spy(defaultLitePullConsumer); - when(mockConsumer.poll(anyLong())).thenReturn(new ArrayList<>()); - new AsyncConsumer().executeAsync(defaultLitePullConsumer); Thread.sleep(100); @@ -574,9 +584,9 @@ public class DefaultLitePullConsumerTest { when(mQClientFactory.getMQClientAPIImpl().pullMessage(anyString(), any(PullMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), nullable(PullCallback.class))) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override - public Object answer(InvocationOnMock mock) throws Throwable { + public PullResult answer(InvocationOnMock mock) throws Throwable { PullMessageRequestHeader requestHeader = mock.getArgument(1); MessageClientExt messageClientExt = new MessageClientExt(); messageClientExt.setTopic(topic); @@ -602,6 +612,7 @@ public class DefaultLitePullConsumerTest { DefaultLitePullConsumer litePullConsumer = new DefaultLitePullConsumer(consumerGroup + System.currentTimeMillis()); litePullConsumer.setNamesrvAddr("127.0.0.1:9876"); litePullConsumer.subscribe(topic, "*"); + suppressUpdateTopicRouteInfoFromNameServer(litePullConsumer); litePullConsumer.start(); initDefaultLitePullConsumer(litePullConsumer); return litePullConsumer; @@ -610,6 +621,7 @@ public class DefaultLitePullConsumerTest { private DefaultLitePullConsumer createStartLitePullConsumer() throws Exception { DefaultLitePullConsumer litePullConsumer = new DefaultLitePullConsumer(consumerGroup + System.currentTimeMillis()); litePullConsumer.setNamesrvAddr("127.0.0.1:9876"); + suppressUpdateTopicRouteInfoFromNameServer(litePullConsumer); litePullConsumer.start(); initDefaultLitePullConsumer(litePullConsumer); return litePullConsumer; @@ -625,6 +637,7 @@ public class DefaultLitePullConsumerTest { litePullConsumer.setNamesrvAddr("127.0.0.1:9876"); litePullConsumer.setMessageModel(MessageModel.BROADCASTING); litePullConsumer.subscribe(topic, "*"); + suppressUpdateTopicRouteInfoFromNameServer(litePullConsumer); litePullConsumer.start(); initDefaultLitePullConsumer(litePullConsumer); return litePullConsumer; @@ -646,4 +659,15 @@ public class DefaultLitePullConsumerTest { } return new PullResultExt(pullStatus, requestHeader.getQueueOffset() + messageExtList.size(), 123, 2048, messageExtList, 0, outputStream.toByteArray()); } + + private static void suppressUpdateTopicRouteInfoFromNameServer(DefaultLitePullConsumer litePullConsumer) throws IllegalAccessException { + DefaultLitePullConsumerImpl defaultLitePullConsumerImpl = (DefaultLitePullConsumerImpl) FieldUtils.readDeclaredField(litePullConsumer, "defaultLitePullConsumerImpl", true); + if (litePullConsumer.getMessageModel() == MessageModel.CLUSTERING) { + litePullConsumer.changeInstanceNameToPID(); + } + MQClientInstance mQClientFactory = spy(MQClientManager.getInstance().getOrCreateMQClientInstance(litePullConsumer, (RPCHook) FieldUtils.readDeclaredField(defaultLitePullConsumerImpl, "rpcHook", true))); + ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); + factoryTable.put(litePullConsumer.buildMQClientId(), mQClientFactory); + doReturn(false).when(mQClientFactory).updateTopicRouteInfoFromNameServer(anyString()); + } } diff --git a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java index 9395451594..c2c60824ec 100644 --- a/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/consumer/DefaultMQPushConsumerTest.java @@ -17,15 +17,17 @@ package org.apache.rocketmq.client.consumer; import java.io.ByteArrayOutputStream; -import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; @@ -37,21 +39,22 @@ import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.impl.CommunicationMode; import org.apache.rocketmq.client.impl.FindBrokerResult; import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; import org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService; import org.apache.rocketmq.client.impl.consumer.ConsumeMessageOrderlyService; import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; import org.apache.rocketmq.client.impl.consumer.ProcessQueue; -import org.apache.rocketmq.client.impl.consumer.PullAPIWrapper; import org.apache.rocketmq.client.impl.consumer.PullMessageService; import org.apache.rocketmq.client.impl.consumer.PullRequest; import org.apache.rocketmq.client.impl.consumer.PullResultExt; -import org.apache.rocketmq.client.impl.consumer.RebalancePushImpl; +import org.apache.rocketmq.client.impl.consumer.RebalanceImpl; import org.apache.rocketmq.client.impl.factory.MQClientInstance; import org.apache.rocketmq.common.message.MessageClientExt; import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader; +import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.exception.RemotingException; import org.junit.After; import org.junit.Assert; @@ -60,10 +63,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; @@ -76,8 +77,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -@RunWith(PowerMockRunner.class) -@PrepareForTest(DefaultMQPushConsumerImpl.class) +@RunWith(MockitoJUnitRunner.class) public class DefaultMQPushConsumerTest { private String consumerGroup; private String topic = "FooBar"; @@ -86,57 +86,20 @@ public class DefaultMQPushConsumerTest { @Mock private MQClientAPIImpl mQClientAPIImpl; - private PullAPIWrapper pullAPIWrapper; - private RebalancePushImpl rebalancePushImpl; + private RebalanceImpl rebalanceImpl; private DefaultMQPushConsumer pushConsumer; @Before public void init() throws Exception { - consumerGroup = "FooBarGroup" + System.currentTimeMillis(); - pushConsumer = new DefaultMQPushConsumer(consumerGroup); - pushConsumer.setNamesrvAddr("127.0.0.1:9876"); - pushConsumer.setPullInterval(60 * 1000); + ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); + factoryTable.forEach((s, instance) -> instance.shutdown()); + factoryTable.clear(); - pushConsumer.registerMessageListener(new MessageListenerConcurrently() { - @Override - public ConsumeConcurrentlyStatus consumeMessage(List msgs, - ConsumeConcurrentlyContext context) { - return null; - } - }); - - DefaultMQPushConsumerImpl pushConsumerImpl = pushConsumer.getDefaultMQPushConsumerImpl(); - PowerMockito.suppress(PowerMockito.method(DefaultMQPushConsumerImpl.class, "updateTopicSubscribeInfoWhenSubscriptionChanged")); - rebalancePushImpl = spy(new RebalancePushImpl(pushConsumer.getDefaultMQPushConsumerImpl())); - Field field = DefaultMQPushConsumerImpl.class.getDeclaredField("rebalanceImpl"); - field.setAccessible(true); - field.set(pushConsumerImpl, rebalancePushImpl); - - pushConsumer.subscribe(topic, "*"); - pushConsumer.start(); - - mQClientFactory = spy(pushConsumerImpl.getmQClientFactory()); - field = DefaultMQPushConsumerImpl.class.getDeclaredField("mQClientFactory"); - field.setAccessible(true); - field.set(pushConsumerImpl, mQClientFactory); - - field = MQClientInstance.class.getDeclaredField("mQClientAPIImpl"); - field.setAccessible(true); - field.set(mQClientFactory, mQClientAPIImpl); - - pullAPIWrapper = spy(new PullAPIWrapper(mQClientFactory, consumerGroup, false)); - field = DefaultMQPushConsumerImpl.class.getDeclaredField("pullAPIWrapper"); - field.setAccessible(true); - field.set(pushConsumerImpl, pullAPIWrapper); - - pushConsumer.getDefaultMQPushConsumerImpl().getRebalanceImpl().setmQClientFactory(mQClientFactory); - mQClientFactory.registerConsumer(consumerGroup, pushConsumerImpl); - - when(mQClientFactory.getMQClientAPIImpl().pullMessage(anyString(), any(PullMessageRequestHeader.class), + when(mQClientAPIImpl.pullMessage(anyString(), any(PullMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), nullable(PullCallback.class))) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override - public Object answer(InvocationOnMock mock) throws Throwable { + public PullResult answer(InvocationOnMock mock) throws Throwable { PullMessageRequestHeader requestHeader = mock.getArgument(1); MessageClientExt messageClientExt = new MessageClientExt(); messageClientExt.setTopic(topic); @@ -152,12 +115,42 @@ public class DefaultMQPushConsumerTest { } }); + + consumerGroup = "FooBarGroup" + System.currentTimeMillis(); + pushConsumer = new DefaultMQPushConsumer(consumerGroup); + pushConsumer.setNamesrvAddr("127.0.0.1:9876"); + pushConsumer.setPullInterval(60 * 1000); + + pushConsumer.registerMessageListener(new MessageListenerConcurrently() { + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, + ConsumeConcurrentlyContext context) { + return null; + } + }); + + DefaultMQPushConsumerImpl pushConsumerImpl = pushConsumer.getDefaultMQPushConsumerImpl(); + + // suppress updateTopicRouteInfoFromNameServer + pushConsumer.changeInstanceNameToPID(); + mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(pushConsumer, (RPCHook) FieldUtils.readDeclaredField(pushConsumerImpl, "rpcHook", true)); + FieldUtils.writeDeclaredField(mQClientFactory, "mQClientAPIImpl", mQClientAPIImpl, true); + mQClientFactory = spy(mQClientFactory); + factoryTable.put(pushConsumer.buildMQClientId(), mQClientFactory); + doReturn(false).when(mQClientFactory).updateTopicRouteInfoFromNameServer(anyString()); + doReturn(new FindBrokerResult("127.0.0.1:10911", false)).when(mQClientFactory).findBrokerAddressInSubscribe(anyString(), anyLong(), anyBoolean()); - doReturn(Collections.singletonList(mQClientFactory.getClientId())).when(mQClientFactory).findConsumerIdList(anyString(), anyString()); + + rebalanceImpl = spy(pushConsumerImpl.getRebalanceImpl()); + doReturn(123L).when(rebalanceImpl).computePullFromWhere(any(MessageQueue.class)); + FieldUtils.writeDeclaredField(pushConsumerImpl, "rebalanceImpl", rebalanceImpl, true); + Set messageQueueSet = new HashSet(); messageQueueSet.add(createPullRequest().getMessageQueue()); - pushConsumer.getDefaultMQPushConsumerImpl().updateTopicSubscribeInfo(topic, messageQueueSet); - doReturn(123L).when(rebalancePushImpl).computePullFromWhere(any(MessageQueue.class)); + pushConsumerImpl.updateTopicSubscribeInfo(topic, messageQueueSet); + + pushConsumer.subscribe(topic, "*"); + pushConsumer.start(); } @After @@ -173,12 +166,12 @@ public class DefaultMQPushConsumerTest { @Test public void testPullMessage_Success() throws InterruptedException, RemotingException, MQBrokerException { final CountDownLatch countDownLatch = new CountDownLatch(1); - final MessageExt[] messageExts = new MessageExt[1]; + final AtomicReference messageAtomic = new AtomicReference<>(); pushConsumer.getDefaultMQPushConsumerImpl().setConsumeMessageService(new ConsumeMessageConcurrentlyService(pushConsumer.getDefaultMQPushConsumerImpl(), new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { - messageExts[0] = msgs.get(0); + messageAtomic.set(msgs.get(0)); countDownLatch.countDown(); return null; } @@ -186,20 +179,22 @@ public class DefaultMQPushConsumerTest { PullMessageService pullMessageService = mQClientFactory.getPullMessageService(); pullMessageService.executePullRequestImmediately(createPullRequest()); - countDownLatch.await(); - assertThat(messageExts[0].getTopic()).isEqualTo(topic); - assertThat(messageExts[0].getBody()).isEqualTo(new byte[] {'a'}); + countDownLatch.await(10, TimeUnit.SECONDS); + MessageExt msg = messageAtomic.get(); + assertThat(msg).isNotNull(); + assertThat(msg.getTopic()).isEqualTo(topic); + assertThat(msg.getBody()).isEqualTo(new byte[] {'a'}); } @Test public void testPullMessage_SuccessWithOrderlyService() throws Exception { final CountDownLatch countDownLatch = new CountDownLatch(1); - final MessageExt[] messageExts = new MessageExt[1]; + final AtomicReference messageAtomic = new AtomicReference<>(); MessageListenerOrderly listenerOrderly = new MessageListenerOrderly() { @Override public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) { - messageExts[0] = msgs.get(0); + messageAtomic.set(msgs.get(0)); countDownLatch.countDown(); return null; } @@ -212,8 +207,10 @@ public class DefaultMQPushConsumerTest { pullMessageService.executePullRequestLater(createPullRequest(), 100); countDownLatch.await(10, TimeUnit.SECONDS); - assertThat(messageExts[0].getTopic()).isEqualTo(topic); - assertThat(messageExts[0].getBody()).isEqualTo(new byte[] {'a'}); + MessageExt msg = messageAtomic.get(); + assertThat(msg).isNotNull(); + assertThat(msg.getTopic()).isEqualTo(topic); + assertThat(msg.getBody()).isEqualTo(new byte[] {'a'}); } @Test @@ -279,7 +276,7 @@ public class DefaultMQPushConsumerTest { PullMessageService pullMessageService = mQClientFactory.getPullMessageService(); pullMessageService.executePullRequestImmediately(createPullRequest()); - countDownLatch.await(); + assertThat(countDownLatch.await(10, TimeUnit.SECONDS)).isTrue(); pushConsumer.shutdown(); assertThat(messageConsumedFlag.get()).isTrue(); diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageConcurrentlyServiceTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageConcurrentlyServiceTest.java index 921743c6be..e8feb80dd9 100644 --- a/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageConcurrentlyServiceTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/impl/consumer/ConsumeMessageConcurrentlyServiceTest.java @@ -16,7 +16,19 @@ */ package org.apache.rocketmq.client.impl.consumer; -import org.apache.rocketmq.client.consumer.*; +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.PullCallback; +import org.apache.rocketmq.client.consumer.PullResult; +import org.apache.rocketmq.client.consumer.PullStatus; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; @@ -40,23 +52,15 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import java.io.ByteArrayOutputStream; -import java.lang.reflect.Field; -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CountDownLatch; - import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -117,9 +121,9 @@ public class ConsumeMessageConcurrentlyServiceTest { when(mQClientFactory.getMQClientAPIImpl().pullMessage(anyString(), any(PullMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), nullable(PullCallback.class))) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override - public Object answer(InvocationOnMock mock) throws Throwable { + public PullResult answer(InvocationOnMock mock) throws Throwable { PullMessageRequestHeader requestHeader = mock.getArgument(1); MessageClientExt messageClientExt = new MessageClientExt(); messageClientExt.setTopic(topic); @@ -145,13 +149,13 @@ public class ConsumeMessageConcurrentlyServiceTest { @Test public void testPullMessage_ConsumeSuccess() throws InterruptedException, RemotingException, MQBrokerException, NoSuchFieldException,Exception { final CountDownLatch countDownLatch = new CountDownLatch(1); - final MessageExt[] messageExts = new MessageExt[1]; + final AtomicReference messageAtomic = new AtomicReference<>(); ConsumeMessageConcurrentlyService normalServie = new ConsumeMessageConcurrentlyService(pushConsumer.getDefaultMQPushConsumerImpl(), new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { - messageExts[0] = msgs.get(0); + messageAtomic.set(msgs.get(0)); countDownLatch.countDown(); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } @@ -175,8 +179,10 @@ public class ConsumeMessageConcurrentlyServiceTest { StatsItem item = itemSet.getAndCreateStatsItem(topic + "@" + pushConsumer.getDefaultMQPushConsumerImpl().groupName()); assertThat(item.getValue().get()).isGreaterThan(0L); - assertThat(messageExts[0].getTopic()).isEqualTo(topic); - assertThat(messageExts[0].getBody()).isEqualTo(new byte[] {'a'}); + MessageExt msg = messageAtomic.get(); + assertThat(msg).isNotNull(); + assertThat(msg.getTopic()).isEqualTo(topic); + assertThat(msg.getBody()).isEqualTo(new byte[] {'a'}); } @After diff --git a/client/src/test/java/org/apache/rocketmq/client/impl/factory/MQClientInstanceTest.java b/client/src/test/java/org/apache/rocketmq/client/impl/factory/MQClientInstanceTest.java index bb2132111c..d37844ca96 100644 --- a/client/src/test/java/org/apache/rocketmq/client/impl/factory/MQClientInstanceTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/impl/factory/MQClientInstanceTest.java @@ -19,29 +19,44 @@ package org.apache.rocketmq.client.impl.factory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.ClientConfig; import org.apache.rocketmq.client.admin.MQAdminExtInner; import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.impl.FindBrokerResult; import org.apache.rocketmq.client.impl.MQClientManager; import org.apache.rocketmq.client.impl.consumer.MQConsumerInner; import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; import org.apache.rocketmq.client.impl.producer.TopicPublishInfo; +import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo; +import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType; import org.apache.rocketmq.common.protocol.route.BrokerData; import org.apache.rocketmq.common.protocol.route.QueueData; import org.apache.rocketmq.common.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.exception.RemotingException; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class MQClientInstanceTest { private MQClientInstance mqClientInstance = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig()); private String topic = "FooBar"; private String group = "FooBarGroup"; + private ConcurrentMap> brokerAddrTable = new ConcurrentHashMap>(); + + @Before + public void init() throws Exception { + FieldUtils.writeDeclaredField(mqClientInstance, "brokerAddrTable", brokerAddrTable, true); + } @Test public void testTopicRouteData2TopicPublishInfo() { @@ -64,7 +79,7 @@ public class MQClientInstanceTest { queueData.setPerm(6); queueData.setReadQueueNums(3); queueData.setWriteQueueNums(4); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); @@ -74,6 +89,34 @@ public class MQClientInstanceTest { assertThat(topicPublishInfo.getMessageQueueList().size()).isEqualTo(4); } + @Test + public void testFindBrokerAddressInSubscribe() { + // dledger normal case + String brokerName = "BrokerA"; + HashMap addrMap = new HashMap(); + addrMap.put(0L, "127.0.0.1:10911"); + addrMap.put(1L, "127.0.0.1:10912"); + addrMap.put(2L, "127.0.0.1:10913"); + brokerAddrTable.put(brokerName, addrMap); + long brokerId = 1; + FindBrokerResult brokerResult = mqClientInstance.findBrokerAddressInSubscribe(brokerName, brokerId, false); + assertThat(brokerResult).isNotNull(); + assertThat(brokerResult.getBrokerAddr()).isEqualTo("127.0.0.1:10912"); + assertThat(brokerResult.isSlave()).isTrue(); + + // dledger case, when node n0 was voted as the leader + brokerName = "BrokerB"; + HashMap addrMapNew = new HashMap(); + addrMapNew.put(0L, "127.0.0.1:10911"); + addrMapNew.put(2L, "127.0.0.1:10912"); + addrMapNew.put(3L, "127.0.0.1:10913"); + brokerAddrTable.put(brokerName, addrMapNew); + brokerResult = mqClientInstance.findBrokerAddressInSubscribe(brokerName, brokerId, false); + assertThat(brokerResult).isNotNull(); + assertThat(brokerResult.getBrokerAddr()).isEqualTo("127.0.0.1:10912"); + assertThat(brokerResult.isSlave()).isTrue(); + } + @Test public void testRegisterProducer() { boolean flag = mqClientInstance.registerProducer(group, mock(DefaultMQProducerImpl.class)); @@ -100,6 +143,31 @@ public class MQClientInstanceTest { assertThat(flag).isTrue(); } + + @Test + public void testConsumerRunningInfoWhenConsumersIsEmptyOrNot() throws RemotingException, InterruptedException, MQBrokerException { + MQConsumerInner mockConsumerInner = mock(MQConsumerInner.class); + ConsumerRunningInfo mockConsumerRunningInfo = mock(ConsumerRunningInfo.class); + when(mockConsumerInner.consumerRunningInfo()).thenReturn(mockConsumerRunningInfo); + when(mockConsumerInner.consumeType()).thenReturn(ConsumeType.CONSUME_PASSIVELY); + Properties properties = new Properties(); + when(mockConsumerRunningInfo.getProperties()).thenReturn(properties); + mqClientInstance.unregisterConsumer(group); + + ConsumerRunningInfo runningInfo = mqClientInstance.consumerRunningInfo(group); + assertThat(runningInfo).isNull(); + boolean flag = mqClientInstance.registerConsumer(group, mockConsumerInner); + assertThat(flag).isTrue(); + + runningInfo = mqClientInstance.consumerRunningInfo(group); + assertThat(runningInfo).isNotNull(); + assertThat(mockConsumerInner.consumerRunningInfo().getProperties().get(ConsumerRunningInfo.PROP_CONSUME_TYPE)); + + mqClientInstance.unregisterConsumer(group); + flag = mqClientInstance.registerConsumer(group, mock(MQConsumerInner.class)); + assertThat(flag).isTrue(); + } + @Test public void testRegisterAdminExt() { boolean flag = mqClientInstance.registerAdminExt(group, mock(MQAdminExtInner.class)); diff --git a/client/src/test/java/org/apache/rocketmq/client/producer/DefaultMQProducerTest.java b/client/src/test/java/org/apache/rocketmq/client/producer/DefaultMQProducerTest.java index e1771b96ba..5f29fe113c 100644 --- a/client/src/test/java/org/apache/rocketmq/client/producer/DefaultMQProducerTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/producer/DefaultMQProducerTest.java @@ -481,7 +481,7 @@ public class DefaultMQProducerTest { queueData.setPerm(6); queueData.setReadQueueNums(3); queueData.setWriteQueueNums(4); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); return topicRouteData; diff --git a/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHashTest.java b/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHashTest.java index 056e910bb8..8f286ee429 100644 --- a/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHashTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueByHashTest.java @@ -44,6 +44,14 @@ public class SelectMessageQueueByHashTest { String anotherOrderId = "234"; MessageQueue selected = selector.select(messageQueues, message, orderId); assertThat(selector.select(messageQueues, message, anotherOrderId)).isNotEqualTo(selected); + + //No exception is thrown while order Id hashcode is Integer.MIN + anotherOrderId = "polygenelubricants"; + selector.select(messageQueues, message, anotherOrderId); + anotherOrderId = "GydZG_"; + selector.select(messageQueues, message, anotherOrderId); + anotherOrderId = "DESIGNING WORKHOUSES"; + selector.select(messageQueues, message, anotherOrderId); } } \ No newline at end of file diff --git a/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueRetryTest.java b/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueRetryTest.java new file mode 100644 index 0000000000..36a57c9b06 --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/producer/selector/SelectMessageQueueRetryTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.rocketmq.client.producer.selector; + +import org.apache.rocketmq.client.impl.producer.TopicPublishInfo; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageQueue; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SelectMessageQueueRetryTest { + + private String topic = "TEST"; + + @Test + public void testSelect() throws Exception { + + TopicPublishInfo topicPublishInfo = new TopicPublishInfo(); + List messageQueueList = new ArrayList(); + for (int i = 0; i < 3; i++) { + MessageQueue mq = new MessageQueue(); + mq.setBrokerName("broker-" + i); + mq.setQueueId(0); + mq.setTopic(topic); + messageQueueList.add(mq); + } + + topicPublishInfo.setMessageQueueList(messageQueueList); + + Set retryBrokerNameSet = retryBroker(topicPublishInfo); + //always in Set (broker-0,broker-1,broker-2) + assertThat(retryBroker(topicPublishInfo)).isEqualTo(retryBrokerNameSet); + } + + private Set retryBroker(TopicPublishInfo topicPublishInfo) { + MessageQueue mqTmp = null; + Set retryBrokerNameSet = new HashSet(); + for (int times = 0; times < 3; times++) { + String lastBrokerName = null == mqTmp ? null : mqTmp.getBrokerName(); + mqTmp = topicPublishInfo.selectOneMessageQueue(lastBrokerName); + retryBrokerNameSet.add(mqTmp.getBrokerName()); + } + return retryBrokerNameSet; + } + +} \ No newline at end of file diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithOpenTracingTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithOpenTracingTest.java new file mode 100644 index 0000000000..ecf72ae44c --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithOpenTracingTest.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.client.trace; + +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import io.opentracing.tag.Tags; +import java.io.ByteArrayOutputStream; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.PullCallback; +import org.apache.rocketmq.client.consumer.PullResult; +import org.apache.rocketmq.client.consumer.PullStatus; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +import org.apache.rocketmq.client.consumer.store.OffsetStore; +import org.apache.rocketmq.client.consumer.store.ReadOffsetType; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.impl.CommunicationMode; +import org.apache.rocketmq.client.impl.FindBrokerResult; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; +import org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService; +import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; +import org.apache.rocketmq.client.impl.consumer.ProcessQueue; +import org.apache.rocketmq.client.impl.consumer.PullAPIWrapper; +import org.apache.rocketmq.client.impl.consumer.PullMessageService; +import org.apache.rocketmq.client.impl.consumer.PullRequest; +import org.apache.rocketmq.client.impl.consumer.PullResultExt; +import org.apache.rocketmq.client.impl.consumer.RebalancePushImpl; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.client.trace.hook.ConsumeMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.message.MessageClientExt; +import org.apache.rocketmq.common.message.MessageDecoder; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader; +import org.apache.rocketmq.remoting.RPCHook; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.waitAtMost; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultMQConsumerWithOpenTracingTest { + private String consumerGroup; + + private String topic = "FooBar"; + private String brokerName = "BrokerA"; + private MQClientInstance mQClientFactory; + + @Mock + private MQClientAPIImpl mQClientAPIImpl; + private PullAPIWrapper pullAPIWrapper; + private RebalancePushImpl rebalancePushImpl; + private DefaultMQPushConsumer pushConsumer; + private MockTracer tracer = new MockTracer(); + + @Before + public void init() throws Exception { + ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); + factoryTable.forEach((s, instance) -> instance.shutdown()); + factoryTable.clear(); + + when(mQClientAPIImpl.pullMessage(anyString(), any(PullMessageRequestHeader.class), + anyLong(), any(CommunicationMode.class), nullable(PullCallback.class))) + .thenAnswer(new Answer() { + @Override + public PullResult answer(InvocationOnMock mock) throws Throwable { + PullMessageRequestHeader requestHeader = mock.getArgument(1); + MessageClientExt messageClientExt = new MessageClientExt(); + messageClientExt.setTopic(topic); + messageClientExt.setQueueId(0); + messageClientExt.setMsgId("123"); + messageClientExt.setBody(new byte[]{'a'}); + messageClientExt.setOffsetMsgId("234"); + messageClientExt.setBornHost(new InetSocketAddress(8080)); + messageClientExt.setStoreHost(new InetSocketAddress(8080)); + PullResult pullResult = createPullResult(requestHeader, PullStatus.FOUND, Collections.singletonList(messageClientExt)); + ((PullCallback) mock.getArgument(4)).onSuccess(pullResult); + return pullResult; + } + }); + + + consumerGroup = "FooBarGroup" + System.currentTimeMillis(); + pushConsumer = new DefaultMQPushConsumer(consumerGroup); + pushConsumer.getDefaultMQPushConsumerImpl().registerConsumeMessageHook( + new ConsumeMessageOpenTracingHookImpl(tracer)); + pushConsumer.setNamesrvAddr("127.0.0.1:9876"); + pushConsumer.setPullInterval(60 * 1000); + + OffsetStore offsetStore = Mockito.mock(OffsetStore.class); + Mockito.when(offsetStore.readOffset(any(MessageQueue.class), any(ReadOffsetType.class))).thenReturn(0L); + pushConsumer.setOffsetStore(offsetStore); + + pushConsumer.registerMessageListener(new MessageListenerConcurrently() { + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, + ConsumeConcurrentlyContext context) { + return null; + } + }); + + DefaultMQPushConsumerImpl pushConsumerImpl = pushConsumer.getDefaultMQPushConsumerImpl(); + + // suppress updateTopicRouteInfoFromNameServer + pushConsumer.changeInstanceNameToPID(); + mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(pushConsumer, (RPCHook) FieldUtils.readDeclaredField(pushConsumerImpl, "rpcHook", true)); + FieldUtils.writeDeclaredField(mQClientFactory, "mQClientAPIImpl", mQClientAPIImpl, true); + mQClientFactory = spy(mQClientFactory); + factoryTable.put(pushConsumer.buildMQClientId(), mQClientFactory); + doReturn(false).when(mQClientFactory).updateTopicRouteInfoFromNameServer(anyString()); + + doReturn(new FindBrokerResult("127.0.0.1:10911", false)).when(mQClientFactory).findBrokerAddressInSubscribe(anyString(), anyLong(), anyBoolean()); + + Set messageQueueSet = new HashSet(); + messageQueueSet.add(createPullRequest().getMessageQueue()); + pushConsumerImpl.updateTopicSubscribeInfo(topic, messageQueueSet); + + pushConsumer.subscribe(topic, "*"); + pushConsumer.start(); + } + + @After + public void terminate() { + pushConsumer.shutdown(); + } + + @Test + public void testPullMessage_WithTrace_Success() throws InterruptedException, RemotingException, MQBrokerException, MQClientException { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AtomicReference messageAtomic = new AtomicReference<>(); + pushConsumer.getDefaultMQPushConsumerImpl().setConsumeMessageService(new ConsumeMessageConcurrentlyService(pushConsumer.getDefaultMQPushConsumerImpl(), new MessageListenerConcurrently() { + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, + ConsumeConcurrentlyContext context) { + messageAtomic.set(msgs.get(0)); + countDownLatch.countDown(); + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } + })); + + PullMessageService pullMessageService = mQClientFactory.getPullMessageService(); + pullMessageService.executePullRequestImmediately(createPullRequest()); + countDownLatch.await(30, TimeUnit.SECONDS); + MessageExt msg = messageAtomic.get(); + assertThat(msg).isNotNull(); + assertThat(msg.getTopic()).isEqualTo(topic); + assertThat(msg.getBody()).isEqualTo(new byte[]{'a'}); + + // wait until consumeMessageAfter hook of tracer is done surely. + waitAtMost(1, TimeUnit.SECONDS).until(() -> tracer.finishedSpans().size() == 1); + MockSpan span = tracer.finishedSpans().get(0); + assertThat(span.tags().get(Tags.MESSAGE_BUS_DESTINATION.getKey())).isEqualTo(topic); + assertThat(span.tags().get(Tags.SPAN_KIND.getKey())).isEqualTo(Tags.SPAN_KIND_CONSUMER); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_SUCCESS)).isEqualTo(true); + } + + private PullRequest createPullRequest() { + PullRequest pullRequest = new PullRequest(); + pullRequest.setConsumerGroup(consumerGroup); + pullRequest.setNextOffset(1024); + + MessageQueue messageQueue = new MessageQueue(); + messageQueue.setBrokerName(brokerName); + messageQueue.setQueueId(0); + messageQueue.setTopic(topic); + pullRequest.setMessageQueue(messageQueue); + ProcessQueue processQueue = new ProcessQueue(); + processQueue.setLocked(true); + processQueue.setLastLockTimestamp(System.currentTimeMillis()); + pullRequest.setProcessQueue(processQueue); + + return pullRequest; + } + + private PullResultExt createPullResult(PullMessageRequestHeader requestHeader, PullStatus pullStatus, + List messageExtList) throws Exception { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + for (MessageExt messageExt : messageExtList) { + outputStream.write(MessageDecoder.encode(messageExt, false)); + } + return new PullResultExt(pullStatus, requestHeader.getQueueOffset() + messageExtList.size(), 123, 2048, messageExtList, 0, outputStream.toByteArray()); + } + +} diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithTraceTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithTraceTest.java index 6c1380b12e..aec7d2cb0e 100644 --- a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithTraceTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQConsumerWithTraceTest.java @@ -26,8 +26,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.PullCallback; import org.apache.rocketmq.client.consumer.PullResult; @@ -40,6 +43,7 @@ import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.impl.CommunicationMode; import org.apache.rocketmq.client.impl.FindBrokerResult; import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; import org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService; import org.apache.rocketmq.client.impl.consumer.DefaultMQPushConsumerImpl; import org.apache.rocketmq.client.impl.consumer.ProcessQueue; @@ -53,17 +57,16 @@ import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; -import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.message.MessageClientExt; import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader; - import org.apache.rocketmq.common.protocol.route.BrokerData; import org.apache.rocketmq.common.protocol.route.QueueData; import org.apache.rocketmq.common.protocol.route.TopicRouteData; import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.exception.RemotingException; import org.junit.After; import org.junit.Before; @@ -71,10 +74,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -86,8 +87,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -@RunWith(PowerMockRunner.class) -@PrepareForTest(DefaultMQPushConsumerImpl.class) +@RunWith(MockitoJUnitRunner.class) public class DefaultMQConsumerWithTraceTest { private String consumerGroup; private String consumerGroupNormal; @@ -114,6 +114,10 @@ public class DefaultMQConsumerWithTraceTest { @Before public void init() throws Exception { + ConcurrentMap factoryTable = (ConcurrentMap) FieldUtils.readDeclaredField(MQClientManager.getInstance(), "factoryTable", true); + factoryTable.forEach((s, instance) -> instance.shutdown()); + factoryTable.clear(); + consumerGroup = "FooBarGroup" + System.currentTimeMillis(); pushConsumer = new DefaultMQPushConsumer(consumerGroup, true, ""); consumerGroupNormal = "FooBarGroup" + System.currentTimeMillis(); @@ -133,8 +137,14 @@ public class DefaultMQConsumerWithTraceTest { } }); - PowerMockito.suppress(PowerMockito.method(DefaultMQPushConsumerImpl.class, "updateTopicSubscribeInfoWhenSubscriptionChanged")); DefaultMQPushConsumerImpl pushConsumerImpl = pushConsumer.getDefaultMQPushConsumerImpl(); + + // suppress updateTopicRouteInfoFromNameServer + pushConsumer.changeInstanceNameToPID(); + mQClientFactory = spy(MQClientManager.getInstance().getOrCreateMQClientInstance(pushConsumer, (RPCHook) FieldUtils.readDeclaredField(pushConsumerImpl, "rpcHook", true))); + factoryTable.put(pushConsumer.buildMQClientId(), mQClientFactory); + doReturn(false).when(mQClientFactory).updateTopicRouteInfoFromNameServer(anyString()); + rebalancePushImpl = spy(new RebalancePushImpl(pushConsumer.getDefaultMQPushConsumerImpl())); Field field = DefaultMQPushConsumerImpl.class.getDeclaredField("rebalanceImpl"); field.setAccessible(true); @@ -172,9 +182,9 @@ public class DefaultMQConsumerWithTraceTest { when(mQClientFactory.getMQClientAPIImpl().pullMessage(anyString(), any(PullMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), nullable(PullCallback.class))) - .thenAnswer(new Answer() { + .thenAnswer(new Answer() { @Override - public Object answer(InvocationOnMock mock) throws Throwable { + public PullResult answer(InvocationOnMock mock) throws Throwable { PullMessageRequestHeader requestHeader = mock.getArgument(1); MessageClientExt messageClientExt = new MessageClientExt(); messageClientExt.setTopic(topic); @@ -206,12 +216,12 @@ public class DefaultMQConsumerWithTraceTest { traceProducer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTraceTemp, traceProducer.getDefaultMQProducerImpl()); final CountDownLatch countDownLatch = new CountDownLatch(1); - final MessageExt[] messageExts = new MessageExt[1]; + final AtomicReference messageAtomic = new AtomicReference<>(); pushConsumer.getDefaultMQPushConsumerImpl().setConsumeMessageService(new ConsumeMessageConcurrentlyService(pushConsumer.getDefaultMQPushConsumerImpl(), new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { - messageExts[0] = msgs.get(0); + messageAtomic.set(msgs.get(0)); countDownLatch.countDown(); return null; } @@ -219,9 +229,11 @@ public class DefaultMQConsumerWithTraceTest { PullMessageService pullMessageService = mQClientFactory.getPullMessageService(); pullMessageService.executePullRequestImmediately(createPullRequest()); - countDownLatch.await(3000L, TimeUnit.MILLISECONDS); - assertThat(messageExts[0].getTopic()).isEqualTo(topic); - assertThat(messageExts[0].getBody()).isEqualTo(new byte[] {'a'}); + countDownLatch.await(30, TimeUnit.SECONDS); + MessageExt msg = messageAtomic.get(); + assertThat(msg).isNotNull(); + assertThat(msg.getTopic()).isEqualTo(topic); + assertThat(msg.getBody()).isEqualTo(new byte[] {'a'}); } private PullRequest createPullRequest() { @@ -271,7 +283,7 @@ public class DefaultMQConsumerWithTraceTest { queueData.setPerm(6); queueData.setReadQueueNums(3); queueData.setWriteQueueNums(4); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); return topicRouteData; @@ -307,7 +319,7 @@ public class DefaultMQConsumerWithTraceTest { queueData.setPerm(6); queueData.setReadQueueNums(1); queueData.setWriteQueueNums(1); - queueData.setTopicSynFlag(1); + queueData.setTopicSysFlag(1); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); return topicRouteData; diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithOpenTracingTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithOpenTracingTest.java new file mode 100644 index 0000000000..5d64a93f03 --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithOpenTracingTest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.client.trace; + +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import io.opentracing.tag.Tags; +import org.apache.rocketmq.client.ClientConfig; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.impl.CommunicationMode; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.rocketmq.client.impl.producer.TopicPublishInfo; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.client.trace.hook.SendMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageType; +import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.QueueData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class DefaultMQProducerWithOpenTracingTest { + + @Spy + private MQClientInstance mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig()); + @Mock + private MQClientAPIImpl mQClientAPIImpl; + + private DefaultMQProducer producer; + + private Message message; + private String topic = "FooBar"; + private String producerGroupPrefix = "FooBar_PID"; + private String producerGroupTemp = producerGroupPrefix + System.currentTimeMillis(); + private String producerGroupTraceTemp = TopicValidator.RMQ_SYS_TRACE_TOPIC + System.currentTimeMillis(); + private MockTracer tracer = new MockTracer(); + + @Before + public void init() throws Exception { + + producer = new DefaultMQProducer(producerGroupTemp); + producer.getDefaultMQProducerImpl().registerSendMessageHook( + new SendMessageOpenTracingHookImpl(tracer)); + producer.setNamesrvAddr("127.0.0.1:9876"); + message = new Message(topic, new byte[] {'a', 'b', 'c'}); + + producer.start(); + + Field field = DefaultMQProducerImpl.class.getDeclaredField("mQClientFactory"); + field.setAccessible(true); + field.set(producer.getDefaultMQProducerImpl(), mQClientFactory); + + field = MQClientInstance.class.getDeclaredField("mQClientAPIImpl"); + field.setAccessible(true); + field.set(mQClientFactory, mQClientAPIImpl); + + producer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTemp, producer.getDefaultMQProducerImpl()); + + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))).thenCallRealMethod(); + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendCallback.class), nullable(TopicPublishInfo.class), nullable(MQClientInstance.class), anyInt(), nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))) + .thenReturn(createSendResult(SendStatus.SEND_OK)); + + } + + @Test + public void testSendMessageSync_WithTrace_Success() throws RemotingException, InterruptedException, MQBrokerException, MQClientException { + producer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTraceTemp, producer.getDefaultMQProducerImpl()); + when(mQClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(createTopicRoute()); + producer.send(message); + assertThat(tracer.finishedSpans().size()).isEqualTo(1); + MockSpan span = tracer.finishedSpans().get(0); + assertThat(span.tags().get(Tags.MESSAGE_BUS_DESTINATION.getKey())).isEqualTo(topic); + assertThat(span.tags().get(Tags.SPAN_KIND.getKey())).isEqualTo(Tags.SPAN_KIND_PRODUCER); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_MSG_ID)).isEqualTo("123"); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_BODY_LENGTH)).isEqualTo(3); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_REGION_ID)).isEqualTo("HZ"); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_MSG_TYPE)).isEqualTo(MessageType.Normal_Msg.name()); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_SOTRE_HOST)).isEqualTo("127.0.0.1:10911"); + } + + @After + public void terminate() { + producer.shutdown(); + } + + public static TopicRouteData createTopicRoute() { + TopicRouteData topicRouteData = new TopicRouteData(); + + topicRouteData.setFilterServerTable(new HashMap>()); + List brokerDataList = new ArrayList(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName("BrokerA"); + brokerData.setCluster("DefaultCluster"); + HashMap brokerAddrs = new HashMap(); + brokerAddrs.put(0L, "127.0.0.1:10911"); + brokerData.setBrokerAddrs(brokerAddrs); + brokerDataList.add(brokerData); + topicRouteData.setBrokerDatas(brokerDataList); + + List queueDataList = new ArrayList(); + QueueData queueData = new QueueData(); + queueData.setBrokerName("BrokerA"); + queueData.setPerm(6); + queueData.setReadQueueNums(3); + queueData.setWriteQueueNums(4); + queueData.setTopicSysFlag(0); + queueDataList.add(queueData); + topicRouteData.setQueueDatas(queueDataList); + return topicRouteData; + } + + private SendResult createSendResult(SendStatus sendStatus) { + SendResult sendResult = new SendResult(); + sendResult.setMsgId("123"); + sendResult.setOffsetMsgId("123"); + sendResult.setQueueOffset(456); + sendResult.setSendStatus(sendStatus); + sendResult.setRegionId("HZ"); + return sendResult; + } + +} diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithTraceTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithTraceTest.java index da573a25fa..62b34175aa 100644 --- a/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithTraceTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/trace/DefaultMQProducerWithTraceTest.java @@ -168,7 +168,7 @@ public class DefaultMQProducerWithTraceTest { queueData.setPerm(6); queueData.setReadQueueNums(3); queueData.setWriteQueueNums(4); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); return topicRouteData; @@ -204,7 +204,7 @@ public class DefaultMQProducerWithTraceTest { queueData.setPerm(6); queueData.setReadQueueNums(1); queueData.setWriteQueueNums(1); - queueData.setTopicSynFlag(1); + queueData.setTopicSysFlag(1); queueDataList.add(queueData); topicRouteData.setQueueDatas(queueDataList); return topicRouteData; diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/TraceDataEncoderTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/TraceDataEncoderTest.java index 249a0d1f14..bac12ea0ce 100644 --- a/client/src/test/java/org/apache/rocketmq/client/trace/TraceDataEncoderTest.java +++ b/client/src/test/java/org/apache/rocketmq/client/trace/TraceDataEncoderTest.java @@ -17,6 +17,7 @@ package org.apache.rocketmq.client.trace; +import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.common.UtilAll; import org.apache.rocketmq.common.message.MessageType; import org.junit.Assert; @@ -90,4 +91,46 @@ public class TraceDataEncoderTest { Assert.assertEquals(traceTransferBean.getTransKey().size(), 2); } -} \ No newline at end of file + @Test + public void testEncoderFromContextBean_EndTransaction() { + TraceContext context = new TraceContext(); + context.setTraceType(TraceType.EndTransaction); + context.setGroupName("PID-test"); + context.setRegionId("DefaultRegion"); + context.setTimeStamp(time); + TraceBean traceBean = new TraceBean(); + traceBean.setTopic("topic-test"); + traceBean.setKeys("Keys"); + traceBean.setTags("Tags"); + traceBean.setMsgId("AC1415116D1418B4AAC217FE1B4E0000"); + traceBean.setStoreHost("127.0.0.1:10911"); + traceBean.setClientHost("127.0.0.1@41700"); + traceBean.setMsgType(MessageType.Trans_msg_Commit); + traceBean.setTransactionId("transactionId"); + traceBean.setTransactionState(LocalTransactionState.COMMIT_MESSAGE); + traceBean.setFromTransactionCheck(false); + List traceBeans = new ArrayList(); + traceBeans.add(traceBean); + context.setTraceBeans(traceBeans); + TraceTransferBean traceTransferBean = TraceDataEncoder.encoderFromContextBean(context); + + Assert.assertEquals(traceTransferBean.getTransKey().size(), 2); + String traceData = traceTransferBean.getTransData(); + TraceContext contextAfter = TraceDataEncoder.decoderFromTraceDataString(traceData).get(0); + Assert.assertEquals(context.getTraceType(), contextAfter.getTraceType()); + Assert.assertEquals(context.getTimeStamp(), contextAfter.getTimeStamp()); + Assert.assertEquals(context.getGroupName(), contextAfter.getGroupName()); + TraceBean before = context.getTraceBeans().get(0); + TraceBean after = contextAfter.getTraceBeans().get(0); + Assert.assertEquals(before.getTopic(), after.getTopic()); + Assert.assertEquals(before.getMsgId(), after.getMsgId()); + Assert.assertEquals(before.getTags(), after.getTags()); + Assert.assertEquals(before.getKeys(), after.getKeys()); + Assert.assertEquals(before.getStoreHost(), after.getStoreHost()); + Assert.assertEquals(before.getMsgType(), after.getMsgType()); + Assert.assertEquals(before.getClientHost(), after.getClientHost()); + Assert.assertEquals(before.getTransactionId(), after.getTransactionId()); + Assert.assertEquals(before.getTransactionState(), after.getTransactionState()); + Assert.assertEquals(before.isFromTransactionCheck(), after.isFromTransactionCheck()); + } +} diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithOpenTracingTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithOpenTracingTest.java new file mode 100644 index 0000000000..dd6d1083ce --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithOpenTracingTest.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.client.trace; + +import io.opentracing.mock.MockSpan; +import io.opentracing.mock.MockTracer; +import io.opentracing.tag.Tags; +import org.apache.rocketmq.client.ClientConfig; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.impl.CommunicationMode; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.rocketmq.client.impl.producer.TopicPublishInfo; +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.client.producer.TransactionListener; +import org.apache.rocketmq.client.producer.TransactionMQProducer; +import org.apache.rocketmq.client.trace.hook.EndTransactionOpenTracingHookImpl; +import org.apache.rocketmq.client.trace.hook.SendMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageDecoder; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.message.MessageType; +import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.QueueData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TransactionMQProducerWithOpenTracingTest { + + @Spy + private MQClientInstance mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig()); + @Mock + private MQClientAPIImpl mQClientAPIImpl; + + private TransactionMQProducer producer; + + private Message message; + private String topic = "FooBar"; + private String producerGroupPrefix = "FooBar_PID"; + private String producerGroupTemp = producerGroupPrefix + System.currentTimeMillis(); + private String producerGroupTraceTemp = TopicValidator.RMQ_SYS_TRACE_TOPIC + System.currentTimeMillis(); + private MockTracer tracer = new MockTracer(); + @Before + public void init() throws Exception { + TransactionListener transactionListener = new TransactionListener() { + @Override + public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + + @Override + public LocalTransactionState checkLocalTransaction(MessageExt msg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + }; + producer = new TransactionMQProducer(producerGroupTemp); + producer.getDefaultMQProducerImpl().registerSendMessageHook(new SendMessageOpenTracingHookImpl(tracer)); + producer.getDefaultMQProducerImpl().registerEndTransactionHook(new EndTransactionOpenTracingHookImpl(tracer)); + producer.setTransactionListener(transactionListener); + + producer.setNamesrvAddr("127.0.0.1:9876"); + message = new Message(topic, new byte[] {'a', 'b', 'c'}); + + producer.start(); + + Field field = DefaultMQProducerImpl.class.getDeclaredField("mQClientFactory"); + field.setAccessible(true); + field.set(producer.getDefaultMQProducerImpl(), mQClientFactory); + + field = MQClientInstance.class.getDeclaredField("mQClientAPIImpl"); + field.setAccessible(true); + field.set(mQClientFactory, mQClientAPIImpl); + + producer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTemp, producer.getDefaultMQProducerImpl()); + + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))).thenCallRealMethod(); + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendCallback.class), nullable(TopicPublishInfo.class), nullable(MQClientInstance.class), anyInt(), nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))) + .thenReturn(createSendResult(SendStatus.SEND_OK)); + + } + + @Test + public void testSendMessageSync_WithTrace_Success() throws RemotingException, InterruptedException, MQBrokerException, MQClientException { + producer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTraceTemp, producer.getDefaultMQProducerImpl()); + when(mQClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(createTopicRoute()); + producer.sendMessageInTransaction(message, null); + + assertThat(tracer.finishedSpans().size()).isEqualTo(2); + MockSpan span = tracer.finishedSpans().get(1); + assertThat(span.tags().get(Tags.MESSAGE_BUS_DESTINATION.getKey())).isEqualTo(topic); + assertThat(span.tags().get(Tags.SPAN_KIND.getKey())).isEqualTo(Tags.SPAN_KIND_PRODUCER); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_MSG_ID)).isEqualTo("123"); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_MSG_TYPE)).isEqualTo(MessageType.Trans_msg_Commit.name()); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_TRANSACTION_STATE)).isEqualTo(LocalTransactionState.COMMIT_MESSAGE.name()); + assertThat(span.tags().get(TraceConstants.ROCKETMQ_IS_FROM_TRANSACTION_CHECK)).isEqualTo(false); + } + + @After + public void terminate() { + producer.shutdown(); + } + + public static TopicRouteData createTopicRoute() { + TopicRouteData topicRouteData = new TopicRouteData(); + + topicRouteData.setFilterServerTable(new HashMap>()); + List brokerDataList = new ArrayList(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName("BrokerA"); + brokerData.setCluster("DefaultCluster"); + HashMap brokerAddrs = new HashMap(); + brokerAddrs.put(0L, "127.0.0.1:10911"); + brokerData.setBrokerAddrs(brokerAddrs); + brokerDataList.add(brokerData); + topicRouteData.setBrokerDatas(brokerDataList); + + List queueDataList = new ArrayList(); + QueueData queueData = new QueueData(); + queueData.setBrokerName("BrokerA"); + queueData.setPerm(6); + queueData.setReadQueueNums(3); + queueData.setWriteQueueNums(4); + queueData.setTopicSysFlag(0); + queueDataList.add(queueData); + topicRouteData.setQueueDatas(queueDataList); + return topicRouteData; + } + + private SendResult createSendResult(SendStatus sendStatus) { + SendResult sendResult = new SendResult(); + sendResult.setMsgId("123"); + sendResult.setOffsetMsgId(MessageDecoder.createMessageId(new InetSocketAddress("127.0.0.1", 12), 1)); + sendResult.setQueueOffset(456); + sendResult.setSendStatus(sendStatus); + sendResult.setRegionId("HZ"); + sendResult.setMessageQueue(new MessageQueue(topic, "broker-trace", 0)); + return sendResult; + } + +} diff --git a/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithTraceTest.java b/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithTraceTest.java new file mode 100644 index 0000000000..f838817bf9 --- /dev/null +++ b/client/src/test/java/org/apache/rocketmq/client/trace/TransactionMQProducerWithTraceTest.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.client.trace; + +import org.apache.rocketmq.client.ClientConfig; +import org.apache.rocketmq.client.exception.MQBrokerException; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.hook.EndTransactionContext; +import org.apache.rocketmq.client.hook.EndTransactionHook; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.impl.CommunicationMode; +import org.apache.rocketmq.client.impl.MQClientAPIImpl; +import org.apache.rocketmq.client.impl.MQClientManager; +import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl; +import org.apache.rocketmq.client.impl.producer.TopicPublishInfo; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.client.producer.SendCallback; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.client.producer.TransactionListener; +import org.apache.rocketmq.client.producer.TransactionMQProducer; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageDecoder; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageQueue; +import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.QueueData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; +import org.apache.rocketmq.common.topic.TopicValidator; +import org.apache.rocketmq.remoting.exception.RemotingException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TransactionMQProducerWithTraceTest { + + @Spy + private MQClientInstance mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(new ClientConfig()); + @Mock + private MQClientAPIImpl mQClientAPIImpl; + @Mock + private EndTransactionHook endTransactionHook; + + private AsyncTraceDispatcher asyncTraceDispatcher; + + private TransactionMQProducer producer; + private DefaultMQProducer traceProducer; + + private Message message; + private String topic = "FooBar"; + private String producerGroupPrefix = "FooBar_PID"; + private String producerGroupTemp = producerGroupPrefix + System.currentTimeMillis(); + private String producerGroupTraceTemp = TopicValidator.RMQ_SYS_TRACE_TOPIC + System.currentTimeMillis(); + private String customerTraceTopic = "rmq_trace_topic_12345"; + + @Before + public void init() throws Exception { + TransactionListener transactionListener = new TransactionListener() { + @Override + public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + + @Override + public LocalTransactionState checkLocalTransaction(MessageExt msg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + }; + producer = new TransactionMQProducer(null, producerGroupTemp, null, true, null); + producer.setTransactionListener(transactionListener); + + producer.setNamesrvAddr("127.0.0.1:9876"); + message = new Message(topic, new byte[] {'a', 'b', 'c'}); + asyncTraceDispatcher = (AsyncTraceDispatcher) producer.getTraceDispatcher(); + traceProducer = asyncTraceDispatcher.getTraceProducer(); + + producer.start(); + + Field field = DefaultMQProducerImpl.class.getDeclaredField("mQClientFactory"); + field.setAccessible(true); + field.set(producer.getDefaultMQProducerImpl(), mQClientFactory); + + Field fieldTrace = DefaultMQProducerImpl.class.getDeclaredField("mQClientFactory"); + fieldTrace.setAccessible(true); + fieldTrace.set(traceProducer.getDefaultMQProducerImpl(), mQClientFactory); + + field = MQClientInstance.class.getDeclaredField("mQClientAPIImpl"); + field.setAccessible(true); + field.set(mQClientFactory, mQClientAPIImpl); + + producer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTemp, producer.getDefaultMQProducerImpl()); + + Field fieldHooks = DefaultMQProducerImpl.class.getDeclaredField("endTransactionHookList"); + fieldHooks.setAccessible(true); + Listhooks = new ArrayList<>(); + hooks.add(endTransactionHook); + fieldHooks.set(producer.getDefaultMQProducerImpl(), hooks); + + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))).thenCallRealMethod(); + when(mQClientAPIImpl.sendMessage(anyString(), anyString(), any(Message.class), any(SendMessageRequestHeader.class), anyLong(), any(CommunicationMode.class), + nullable(SendCallback.class), nullable(TopicPublishInfo.class), nullable(MQClientInstance.class), anyInt(), nullable(SendMessageContext.class), any(DefaultMQProducerImpl.class))) + .thenReturn(createSendResult(SendStatus.SEND_OK)); + + } + + @Test + public void testSendMessageSync_WithTrace_Success() throws RemotingException, InterruptedException, MQBrokerException, MQClientException { + traceProducer.getDefaultMQProducerImpl().getmQClientFactory().registerProducer(producerGroupTraceTemp, traceProducer.getDefaultMQProducerImpl()); + when(mQClientAPIImpl.getTopicRouteInfoFromNameServer(anyString(), anyLong())).thenReturn(createTopicRoute()); + AtomicReference context = new AtomicReference<>(); + doAnswer(mock -> { + context.set(mock.getArgument(0)); + return null; + }).when(endTransactionHook).endTransaction(any()); + producer.sendMessageInTransaction(message, null); + + EndTransactionContext ctx = context.get(); + assertThat(ctx.getProducerGroup()).isEqualTo(producerGroupTemp); + assertThat(ctx.getMsgId()).isEqualTo("123"); + assertThat(ctx.isFromTransactionCheck()).isFalse(); + assertThat(new String(ctx.getMessage().getBody())).isEqualTo(new String(message.getBody())); + assertThat(ctx.getMessage().getTopic()).isEqualTo(topic); + } + + @After + public void terminate() { + producer.shutdown(); + } + + public static TopicRouteData createTopicRoute() { + TopicRouteData topicRouteData = new TopicRouteData(); + + topicRouteData.setFilterServerTable(new HashMap>()); + List brokerDataList = new ArrayList(); + BrokerData brokerData = new BrokerData(); + brokerData.setBrokerName("BrokerA"); + brokerData.setCluster("DefaultCluster"); + HashMap brokerAddrs = new HashMap(); + brokerAddrs.put(0L, "127.0.0.1:10911"); + brokerData.setBrokerAddrs(brokerAddrs); + brokerDataList.add(brokerData); + topicRouteData.setBrokerDatas(brokerDataList); + + List queueDataList = new ArrayList(); + QueueData queueData = new QueueData(); + queueData.setBrokerName("BrokerA"); + queueData.setPerm(6); + queueData.setReadQueueNums(3); + queueData.setWriteQueueNums(4); + queueData.setTopicSysFlag(0); + queueDataList.add(queueData); + topicRouteData.setQueueDatas(queueDataList); + return topicRouteData; + } + + private SendResult createSendResult(SendStatus sendStatus) { + SendResult sendResult = new SendResult(); + sendResult.setMsgId("123"); + sendResult.setOffsetMsgId(MessageDecoder.createMessageId(new InetSocketAddress("127.0.0.1", 12), 1)); + sendResult.setQueueOffset(456); + sendResult.setSendStatus(sendStatus); + sendResult.setRegionId("HZ"); + sendResult.setMessageQueue(new MessageQueue(topic, "broker-trace", 0)); + return sendResult; + } + +} diff --git a/client/src/test/resources/log4j2.xml b/client/src/test/resources/log4j2.xml new file mode 100644 index 0000000000..52cf2a8869 --- /dev/null +++ b/client/src/test/resources/log4j2.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/pom.xml b/common/pom.xml index ad1a180101..068de26dcb 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/common/src/main/java/org/apache/rocketmq/common/MQVersion.java b/common/src/main/java/org/apache/rocketmq/common/MQVersion.java index 139581e5f7..0d4aff0b0f 100644 --- a/common/src/main/java/org/apache/rocketmq/common/MQVersion.java +++ b/common/src/main/java/org/apache/rocketmq/common/MQVersion.java @@ -18,7 +18,7 @@ package org.apache.rocketmq.common; public class MQVersion { - public static final int CURRENT_VERSION = Version.V4_7_1.ordinal(); + public static final int CURRENT_VERSION = Version.V4_8_0.ordinal(); public static String getVersionDesc(int value) { int length = Version.values().length; diff --git a/common/src/main/java/org/apache/rocketmq/common/UtilAll.java b/common/src/main/java/org/apache/rocketmq/common/UtilAll.java index 639a200955..aa6920817f 100644 --- a/common/src/main/java/org/apache/rocketmq/common/UtilAll.java +++ b/common/src/main/java/org/apache/rocketmq/common/UtilAll.java @@ -461,33 +461,9 @@ public class UtilAll { if (ip.length != 4) { throw new RuntimeException("illegal ipv4 bytes"); } - - if (ip[0] >= (byte) 1 && ip[0] <= (byte) 126) { - if (ip[1] == (byte) 1 && ip[2] == (byte) 1 && ip[3] == (byte) 1) { - return false; - } - if (ip[1] == (byte) 0 && ip[2] == (byte) 0 && ip[3] == (byte) 0) { - return false; - } - return true; - } else if (ip[0] >= (byte) 128 && ip[0] <= (byte) 191) { - if (ip[2] == (byte) 1 && ip[3] == (byte) 1) { - return false; - } - if (ip[2] == (byte) 0 && ip[3] == (byte) 0) { - return false; - } - return true; - } else if (ip[0] >= (byte) 192 && ip[0] <= (byte) 223) { - if (ip[3] == (byte) 1) { - return false; - } - if (ip[3] == (byte) 0) { - return false; - } - return true; - } - return false; + + InetAddressValidator validator = InetAddressValidator.getInstance(); + return validator.isValidInet4Address(ipToIPv4Str(ip)); } private static boolean ipV6Check(byte[] ip) { diff --git a/common/src/main/java/org/apache/rocketmq/common/constant/PermName.java b/common/src/main/java/org/apache/rocketmq/common/constant/PermName.java index 200dec2481..1fe9aa106e 100644 --- a/common/src/main/java/org/apache/rocketmq/common/constant/PermName.java +++ b/common/src/main/java/org/apache/rocketmq/common/constant/PermName.java @@ -20,10 +20,10 @@ public class PermName { public static final int PERM_PRIORITY = 0x1 << 3; public static final int PERM_READ = 0x1 << 2; public static final int PERM_WRITE = 0x1 << 1; - public static final int PERM_INHERIT = 0x1 << 0; + public static final int PERM_INHERIT = 0x1; public static String perm2String(final int perm) { - final StringBuffer sb = new StringBuffer("---"); + final StringBuilder sb = new StringBuilder("---"); if (isReadable(perm)) { sb.replace(0, 1, "R"); } diff --git a/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java b/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java index 9268a6ef45..c5b51b1aff 100644 --- a/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java +++ b/common/src/main/java/org/apache/rocketmq/common/filter/FilterAPI.java @@ -36,8 +36,7 @@ public class FilterAPI { return simple; } - public static SubscriptionData buildSubscriptionData(final String consumerGroup, String topic, - String subString) throws Exception { + public static SubscriptionData buildSubscriptionData(String topic, String subString) throws Exception { SubscriptionData subscriptionData = new SubscriptionData(); subscriptionData.setTopic(topic); subscriptionData.setSubString(subString); @@ -67,7 +66,7 @@ public class FilterAPI { public static SubscriptionData build(final String topic, final String subString, final String type) throws Exception { if (ExpressionType.TAG.equals(type) || type == null) { - return buildSubscriptionData(null, topic, subString); + return buildSubscriptionData(topic, subString); } if (subString == null || subString.length() < 1) { diff --git a/common/src/main/java/org/apache/rocketmq/common/hook/FilterCheckHook.java b/common/src/main/java/org/apache/rocketmq/common/hook/FilterCheckHook.java index e72fb82601..f57df26d6f 100644 --- a/common/src/main/java/org/apache/rocketmq/common/hook/FilterCheckHook.java +++ b/common/src/main/java/org/apache/rocketmq/common/hook/FilterCheckHook.java @@ -20,7 +20,7 @@ package org.apache.rocketmq.common.hook; import java.nio.ByteBuffer; public interface FilterCheckHook { - public String hookName(); + String hookName(); - public boolean isFilterMatched(final boolean isUnitMode, final ByteBuffer byteBuffer); + boolean isFilterMatched(final boolean isUnitMode, final ByteBuffer byteBuffer); } diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java index d048dde2b9..2936c18eac 100644 --- a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java +++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java @@ -42,6 +42,7 @@ public class MessageDecoder { public static final char NAME_VALUE_SEPARATOR = 1; public static final char PROPERTY_SEPARATOR = 2; public static final int PHY_POS_POSITION = 4 + 4 + 4 + 4 + 4 + 8; + public static final int QUEUE_OFFSET_POSITION = 4 + 4 + 4 + 4 + 4; public static final int SYSFLAG_POSITION = 4 + 4 + 4 + 4 + 4 + 8 + 8; // public static final int BODY_SIZE_POSITION = 4 // 1 TOTALSIZE // + 4 // 2 MAGICCODE @@ -104,7 +105,7 @@ public class MessageDecoder { * * @param byteBuffer msg commit log buffer. */ - public static Map decodeProperties(java.nio.ByteBuffer byteBuffer) { + public static Map decodeProperties(ByteBuffer byteBuffer) { int sysFlag = byteBuffer.getInt(SYSFLAG_POSITION); int bornhostLength = (sysFlag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 8 : 20; int storehostAddressLength = (sysFlag & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 8 : 20; @@ -140,15 +141,15 @@ public class MessageDecoder { return null; } - public static MessageExt decode(java.nio.ByteBuffer byteBuffer) { + public static MessageExt decode(ByteBuffer byteBuffer) { return decode(byteBuffer, true, true, false); } - public static MessageExt clientDecode(java.nio.ByteBuffer byteBuffer, final boolean readBody) { + public static MessageExt clientDecode(ByteBuffer byteBuffer, final boolean readBody) { return decode(byteBuffer, readBody, true, true); } - public static MessageExt decode(java.nio.ByteBuffer byteBuffer, final boolean readBody) { + public static MessageExt decode(ByteBuffer byteBuffer, final boolean readBody) { return decode(byteBuffer, readBody, true, false); } @@ -263,12 +264,12 @@ public class MessageDecoder { } public static MessageExt decode( - java.nio.ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody) { + ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody) { return decode(byteBuffer, readBody, deCompressBody, false); } public static MessageExt decode( - java.nio.ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient) { + ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient) { try { MessageExt msgExt; @@ -390,11 +391,11 @@ public class MessageDecoder { return null; } - public static List decodes(java.nio.ByteBuffer byteBuffer) { + public static List decodes(ByteBuffer byteBuffer) { return decodes(byteBuffer, true); } - public static List decodes(java.nio.ByteBuffer byteBuffer, final boolean readBody) { + public static List decodes(ByteBuffer byteBuffer, final boolean readBody) { List msgExts = new ArrayList(); while (byteBuffer.hasRemaining()) { MessageExt msgExt = clientDecode(byteBuffer, readBody); diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java index dff9e2f35c..6591d7780c 100644 --- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java +++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java @@ -25,6 +25,8 @@ public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader @CFNotNull private String groupName; + private boolean removeOffset; + @Override public void checkFields() throws RemotingCommandException { } @@ -36,4 +38,12 @@ public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader public void setGroupName(String groupName) { this.groupName = groupName; } + + public boolean isRemoveOffset() { + return removeOffset; + } + + public void setRemoveOffset(boolean removeOffset) { + this.removeOffset = removeOffset; + } } diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/route/QueueData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/route/QueueData.java index 72a21f384c..2dbb2902c9 100644 --- a/common/src/main/java/org/apache/rocketmq/common/protocol/route/QueueData.java +++ b/common/src/main/java/org/apache/rocketmq/common/protocol/route/QueueData.java @@ -25,7 +25,7 @@ public class QueueData implements Comparable { private int readQueueNums; private int writeQueueNums; private int perm; - private int topicSynFlag; + private int topicSysFlag; public int getReadQueueNums() { return readQueueNums; @@ -51,12 +51,12 @@ public class QueueData implements Comparable { this.perm = perm; } - public int getTopicSynFlag() { - return topicSynFlag; + public int getTopicSysFlag() { + return topicSysFlag; } - public void setTopicSynFlag(int topicSynFlag) { - this.topicSynFlag = topicSynFlag; + public void setTopicSysFlag(int topicSysFlag) { + this.topicSysFlag = topicSysFlag; } @Override @@ -67,7 +67,7 @@ public class QueueData implements Comparable { result = prime * result + perm; result = prime * result + readQueueNums; result = prime * result + writeQueueNums; - result = prime * result + topicSynFlag; + result = prime * result + topicSysFlag; return result; } @@ -91,7 +91,7 @@ public class QueueData implements Comparable { return false; if (writeQueueNums != other.writeQueueNums) return false; - if (topicSynFlag != other.topicSynFlag) + if (topicSysFlag != other.topicSysFlag) return false; return true; } @@ -99,7 +99,7 @@ public class QueueData implements Comparable { @Override public String toString() { return "QueueData [brokerName=" + brokerName + ", readQueueNums=" + readQueueNums - + ", writeQueueNums=" + writeQueueNums + ", perm=" + perm + ", topicSynFlag=" + topicSynFlag + + ", writeQueueNums=" + writeQueueNums + ", perm=" + perm + ", topicSysFlag=" + topicSysFlag + "]"; } diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/NameServerAddressUtils.java b/common/src/main/java/org/apache/rocketmq/common/utils/NameServerAddressUtils.java index 6aaf3a28d9..85dc95feb4 100644 --- a/common/src/main/java/org/apache/rocketmq/common/utils/NameServerAddressUtils.java +++ b/common/src/main/java/org/apache/rocketmq/common/utils/NameServerAddressUtils.java @@ -19,8 +19,7 @@ import org.apache.rocketmq.common.MixAll; public class NameServerAddressUtils { public static final String INSTANCE_PREFIX = "MQ_INST_"; public static final String INSTANCE_REGEX = INSTANCE_PREFIX + "\\w+_\\w+"; - public static final String ENDPOINT_PREFIX = "http://"; - public static final Pattern NAMESRV_ENDPOINT_PATTERN = Pattern.compile("^" + ENDPOINT_PREFIX + ".*"); + public static final String ENDPOINT_PREFIX = "(\\w+://|)"; public static final Pattern INST_ENDPOINT_PATTERN = Pattern.compile("^" + ENDPOINT_PREFIX + INSTANCE_REGEX + "\\..*"); public static String getNameServerAddresses() { @@ -35,6 +34,13 @@ public class NameServerAddressUtils { if (StringUtils.isEmpty(endpoint)) { return null; } - return endpoint.substring(ENDPOINT_PREFIX.length(), endpoint.indexOf('.')); + return endpoint.substring(endpoint.lastIndexOf("/") + 1, endpoint.indexOf('.')); + } + + public static String getNameSrvAddrFromNamesrvEndpoint(String nameSrvEndpoint) { + if (StringUtils.isEmpty(nameSrvEndpoint)) { + return null; + } + return nameSrvEndpoint.substring(nameSrvEndpoint.lastIndexOf('/') + 1); } } diff --git a/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java b/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java index 73ab09e61c..5190f88f47 100644 --- a/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java +++ b/common/src/test/java/org/apache/rocketmq/common/filter/FilterAPITest.java @@ -33,7 +33,7 @@ public class FilterAPITest { @Test public void testBuildSubscriptionData() throws Exception { SubscriptionData subscriptionData = - FilterAPI.buildSubscriptionData(group, topic, subString); + FilterAPI.buildSubscriptionData(topic, subString); assertThat(subscriptionData.getTopic()).isEqualTo(topic); assertThat(subscriptionData.getSubString()).isEqualTo(subString); String[] tags = subString.split("\\|\\|"); diff --git a/common/src/test/java/org/apache/rocketmq/common/message/MessageClientIDSetterTest.java b/common/src/test/java/org/apache/rocketmq/common/message/MessageClientIDSetterTest.java index 6d8c20383a..0a17c36b57 100644 --- a/common/src/test/java/org/apache/rocketmq/common/message/MessageClientIDSetterTest.java +++ b/common/src/test/java/org/apache/rocketmq/common/message/MessageClientIDSetterTest.java @@ -35,12 +35,14 @@ public class MessageClientIDSetterTest { assertThat(ipStr).isEqualTo(ipStrFromID); } + @Test public void testGetPidFromID() { - int pid = UtilAll.getPid(); + // Temporary fix on MacOS + short pid = (short) UtilAll.getPid(); String uniqID = MessageClientIDSetter.createUniqID(); - int pidFromID = MessageClientIDSetter.getPidFromID(uniqID); + short pidFromID = (short) MessageClientIDSetter.getPidFromID(uniqID); assertThat(pid).isEqualTo(pidFromID); } diff --git a/common/src/test/java/org/apache/rocketmq/common/protocol/route/TopicRouteDataTest.java b/common/src/test/java/org/apache/rocketmq/common/protocol/route/TopicRouteDataTest.java index 764225d6a8..5c7c6d1d1f 100644 --- a/common/src/test/java/org/apache/rocketmq/common/protocol/route/TopicRouteDataTest.java +++ b/common/src/test/java/org/apache/rocketmq/common/protocol/route/TopicRouteDataTest.java @@ -43,7 +43,7 @@ public class TopicRouteDataTest { queueData.setPerm(6); queueData.setReadQueueNums(8); queueData.setWriteQueueNums(8); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); List queueDataList = new ArrayList(); queueDataList.add(queueData); @@ -78,7 +78,7 @@ public class TopicRouteDataTest { queueData.setPerm(6); queueData.setReadQueueNums(8); queueData.setWriteQueueNums(8); - queueData.setTopicSynFlag(0); + queueData.setTopicSysFlag(0); List queueDataList = new ArrayList(); queueDataList.add(queueData); diff --git a/common/src/test/java/org/apache/rocketmq/common/utils/NameServerAddressUtilsTest.java b/common/src/test/java/org/apache/rocketmq/common/utils/NameServerAddressUtilsTest.java new file mode 100644 index 0000000000..38cffdba9d --- /dev/null +++ b/common/src/test/java/org/apache/rocketmq/common/utils/NameServerAddressUtilsTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.common.utils; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NameServerAddressUtilsTest { + + private static String endpoint1 = "http://127.0.0.1:9876"; + private static String endpoint2 = "127.0.0.1:9876"; + private static String endpoint3 + = "http://MQ_INST_123456789_BXXUzaee.xxx:80"; + private static String endpoint4 = "MQ_INST_123456789_BXXUzaee.xxx:80"; + + @Test + public void testValidateInstanceEndpoint() { + assertThat(NameServerAddressUtils.validateInstanceEndpoint(endpoint1)).isEqualTo(false); + assertThat(NameServerAddressUtils.validateInstanceEndpoint(endpoint2)).isEqualTo(false); + assertThat(NameServerAddressUtils.validateInstanceEndpoint(endpoint3)).isEqualTo(true); + assertThat(NameServerAddressUtils.validateInstanceEndpoint(endpoint4)).isEqualTo(true); + } + + @Test + public void testParseInstanceIdFromEndpoint() { + assertThat(NameServerAddressUtils.parseInstanceIdFromEndpoint(endpoint3)).isEqualTo( + "MQ_INST_123456789_BXXUzaee"); + assertThat(NameServerAddressUtils.parseInstanceIdFromEndpoint(endpoint4)).isEqualTo( + "MQ_INST_123456789_BXXUzaee"); + } + + @Test + public void testGetNameSrvAddrFromNamesrvEndpoint() { + assertThat(NameServerAddressUtils.getNameSrvAddrFromNamesrvEndpoint(endpoint1)) + .isEqualTo("127.0.0.1:9876"); + assertThat(NameServerAddressUtils.getNameSrvAddrFromNamesrvEndpoint(endpoint2)) + .isEqualTo("127.0.0.1:9876"); + assertThat(NameServerAddressUtils.getNameSrvAddrFromNamesrvEndpoint(endpoint3)) + .isEqualTo("MQ_INST_123456789_BXXUzaee.xxx:80"); + assertThat(NameServerAddressUtils.getNameSrvAddrFromNamesrvEndpoint(endpoint4)) + .isEqualTo("MQ_INST_123456789_BXXUzaee.xxx:80"); + } +} diff --git a/distribution/LICENSE-BIN b/distribution/LICENSE-BIN index 372617233b..bd431bfdea 100644 --- a/distribution/LICENSE-BIN +++ b/distribution/LICENSE-BIN @@ -15,7 +15,7 @@ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, - "control" means (properties) the power, direct or indirect, to cause the + "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. diff --git a/distribution/NOTICE-BIN b/distribution/NOTICE-BIN index c2f511fafe..b7bba8daab 100644 --- a/distribution/NOTICE-BIN +++ b/distribution/NOTICE-BIN @@ -1,5 +1,5 @@ Apache RocketMQ -Copyright 2016-2019 The Apache Software Foundation +Copyright 2016-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/distribution/benchmark/shutdown.sh b/distribution/benchmark/shutdown.sh new file mode 100644 index 0000000000..9ecd32600e --- /dev/null +++ b/distribution/benchmark/shutdown.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +case $1 in + producer) + + pid=`ps ax | grep -i 'org.apache.rocketmq.example.benchmark.Producer' |grep java | grep -v grep | awk '{print $1}'` + if [ -z "$pid" ] ; then + echo "No benchmark producer running." + exit -1; + fi + + echo "The benchmkar producer(${pid}) is running..." + + kill ${pid} + + echo "Send shutdown request to benchmark producer(${pid}) OK" + ;; + consumer) + + pid=`ps ax | grep -i 'org.apache.rocketmq.example.benchmark.Consumer' |grep java | grep -v grep | awk '{print $1}'` + if [ -z "$pid" ] ; then + echo "No benchmark consumer running." + exit -1; + fi + + echo "The benchmark consumer(${pid}) is running..." + + kill ${pid} + + echo "Send shutdown request to benchmark consumer(${pid}) OK" + ;; + tproducer) + + pid=`ps ax | grep -i 'org.apache.rocketmq.example.benchmark.TransactionProducer' |grep java | grep -v grep | awk '{print $1}'` + if [ -z "$pid" ] ; then + echo "No benchmark transaction producer running." + exit -1; + fi + + echo "The benchmkar transaction producer(${pid}) is running..." + + kill ${pid} + + echo "Send shutdown request to benchmark transaction producer(${pid}) OK" + ;; + *) + echo "Useage: shutdown producer | consumer | tproducer" +esac diff --git a/distribution/benchmark/tproducer.sh b/distribution/benchmark/tproducer.sh index ac4bbf3ee6..9ced41f832 100644 --- a/distribution/benchmark/tproducer.sh +++ b/distribution/benchmark/tproducer.sh @@ -15,4 +15,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -sh ./runclass.sh org.apache.rocketmq.example.benchmark.TransactionProducer $@ +sh ./runclass.sh org.apache.rocketmq.example.benchmark.TransactionProducer $@ & diff --git a/distribution/bin/runbroker.cmd b/distribution/bin/runbroker.cmd index eab7e30f9d..753a11e9a4 100644 --- a/distribution/bin/runbroker.cmd +++ b/distribution/bin/runbroker.cmd @@ -36,7 +36,7 @@ set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow" set "JAVA_OPT=%JAVA_OPT% -XX:+AlwaysPreTouch" set "JAVA_OPT=%JAVA_OPT% -XX:MaxDirectMemorySize=15g" set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages -XX:-UseBiasedLocking" -set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib" +set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib;%JAVA_HOME%\jre\lib\ext" set "JAVA_OPT=%JAVA_OPT% -cp %CLASSPATH%" "%JAVA%" %JAVA_OPT% %* \ No newline at end of file diff --git a/distribution/bin/runserver.cmd b/distribution/bin/runserver.cmd index 48e32bf2d7..76865f7a16 100644 --- a/distribution/bin/runserver.cmd +++ b/distribution/bin/runserver.cmd @@ -31,7 +31,7 @@ set "JAVA_OPT=%JAVA_OPT% -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollect set "JAVA_OPT=%JAVA_OPT% -verbose:gc -Xloggc:"%USERPROFILE%\rmq_srv_gc.log" -XX:+PrintGCDetails" set "JAVA_OPT=%JAVA_OPT% -XX:-OmitStackTraceInFastThrow" set "JAVA_OPT=%JAVA_OPT% -XX:-UseLargePages" -set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib" +set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib;%JAVA_HOME%\jre\lib\ext" set "JAVA_OPT=%JAVA_OPT% -cp "%CLASSPATH%"" "%JAVA%" %JAVA_OPT% %* \ No newline at end of file diff --git a/distribution/bin/runserver.sh b/distribution/bin/runserver.sh index 68fd1b4619..59f322085d 100644 --- a/distribution/bin/runserver.sh +++ b/distribution/bin/runserver.sh @@ -66,8 +66,8 @@ choose_gc_options() { # Example of JAVA_MAJOR_VERSION value : '1', '9', '10', '11', ... # '1' means releases befor Java 9 - JAVA_MAJOR_VERSION=$("$JAVA" -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p') - if [[ "$JAVA_MAJOR_VERSION" -lt "9" ]] ; then + JAVA_MAJOR_VERSION=$("$JAVA" -version 2>&1 | sed -r -n 's/.* version "([0-9]*).*$/\1/p') + if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "9" ] ; then JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC" JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails" JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m" diff --git a/distribution/pom.xml b/distribution/pom.xml index 11effc370c..d5ab667229 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -20,7 +20,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT rocketmq-distribution rocketmq-distribution ${project.version} diff --git a/docs/cn/README b/docs/cn/README deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/cn/RocketMQ_Example.md b/docs/cn/RocketMQ_Example.md index 4a22b3be5b..d924ce1d7d 100644 --- a/docs/cn/RocketMQ_Example.md +++ b/docs/cn/RocketMQ_Example.md @@ -1,4 +1,43 @@ # 样例 +----- + * [目录](#样例) + * [1 基本样例](#1-基本样例) + * [1.1 加入依赖:](#11-加入依赖) + * [1.2 消息发送](#12-消息发送) + * [1、Producer端发送同步消息](#1producer端发送同步消息) + * [2、发送异步消息](#2发送异步消息) + * [3、单向发送消息](#3单向发送消息) + * [1.3 消费消息](#13-消费消息) + * [2 顺序消息样例](#2-顺序消息样例) + * [2.1 顺序消息生产](#21-顺序消息生产) + * [2.2 顺序消费消息](#22-顺序消费消息) + * [3 延时消息样例](#3-延时消息样例) + * [3.1 启动消费者等待传入订阅消息](#31-启动消费者等待传入订阅消息) + * [3.2 发送延时消息](#32-发送延时消息) + * [3.3 验证](#33-验证) + * [3.4 延时消息的使用场景](#34-延时消息的使用场景) + * [3.5 延时消息的使用限制](#35-延时消息的使用限制) + * [4 批量消息样例](#4-批量消息样例) + * [4.1 发送批量消息](#41-发送批量消息) + * [4.2 消息列表分割](#42-消息列表分割) + * [5 过滤消息样例](#5-过滤消息样例) + * [5.1 基本语法](#51-基本语法) + * [5.2 使用样例](#52-使用样例) + * [1、生产者样例](#1生产者样例) + * [2、消费者样例](#2消费者样例) + * [6 消息事务样例](#6-消息事务样例) + * [6.1 发送事务消息样例](#61-发送事务消息样例) + * [1、创建事务性生产者](#1创建事务性生产者) + * [2、实现事务的监听接口](#2实现事务的监听接口) + * [6.2 事务消息使用上的限制](#62-事务消息使用上的限制) + * [7 Logappender样例](#7-logappender样例) + * [7.1 log4j样例](#71-log4j样例) + * [7.2 log4j2样例](#72-log4j2样例) + * [7.3 logback样例](#73-logback样例) + * [8 OpenMessaging样例](#8-openmessaging样例) + * [8.1 OMSProducer样例](#81-omsproducer样例) + * [8.2 OMSPullConsumer](#82-omspullconsumer) + * [8.3 OMSPushConsumer](#83-omspushconsumer) ----- ## 1 基本样例 @@ -407,7 +446,7 @@ public class ScheduledMessageConsumer { public ConsumeConcurrentlyStatus consumeMessage(List messages, ConsumeConcurrentlyContext context) { for (MessageExt message : messages) { // Print approximate delay time period - System.out.println("Receive message[msgId=" + message.getMsgId() + "] " + (System.currentTimeMillis() - message.getStoreTimestamp()) + "ms later"); + System.out.println("Receive message[msgId=" + message.getMsgId() + "] " + (System.currentTimeMillis() - message.getBornTimestamp()) + "ms later"); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } diff --git a/docs/cn/architecture.md b/docs/cn/architecture.md index 7b41edaea7..42df9f07b2 100644 --- a/docs/cn/architecture.md +++ b/docs/cn/architecture.md @@ -12,11 +12,11 @@ RocketMQ架构上主要分为四部分,如上图所示: - NameServer:NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息。 - BrokerServer:Broker主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker包含了以下几个重要子模块。 -1. Remoting Module:整个Broker的实体,负责处理来自clients端的请求。 -2. Client Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息 -3. Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。 -4. HA Service:高可用服务,提供Master Broker 和 Slave Broker之间的数据同步功能。 -5. Index Service:根据特定的Message key对投递到Broker的消息进行索引服务,以提供消息的快速查询。 + 1. Remoting Module:整个Broker的实体,负责处理来自clients端的请求。 + 2. Client Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息 + 3. Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。 + 4. HA Service:高可用服务,提供Master Broker 和 Slave Broker之间的数据同步功能。 + 5. Index Service:根据特定的Message key对投递到Broker的消息进行索引服务,以提供消息的快速查询。 ![](image/rocketmq_architecture_2.png) diff --git a/docs/cn/best_practice.md b/docs/cn/best_practice.md index e4b04e0d56..4ad6bb23fe 100755 --- a/docs/cn/best_practice.md +++ b/docs/cn/best_practice.md @@ -45,8 +45,8 @@ Producer的send方法本身支持内部重试,重试逻辑如下: -- 至多重试2次(同步发送为2次,异步发送为0次)。 -- 如果发送失败,则轮转到下一个Broker。这个方法的总耗时时间不超过sendMsgTimeout设置的值,默认10s。 +- 至多重试2次。 +- 如果同步模式发送失败,则轮转到下一个Broker,如果异步模式发送失败,则只会在当前Broker进行重试。这个方法的总耗时时间不超过sendMsgTimeout设置的值,默认10s。 - 如果本身向broker发送消息产生超时异常,就不会再重试。 以上策略也是在一定程度上保证了消息可以发送成功。如果业务对消息可靠性要求比较高,建议应用增加相应的重试逻辑:比如调用send同步方法发送失败时,则尝试将消息存储到db,然后由后台线程定时重试,确保消息一定到达Broker。 diff --git a/docs/cn/concept.md b/docs/cn/concept.md index 8ad0be6da8..75532e89da 100644 --- a/docs/cn/concept.md +++ b/docs/cn/concept.md @@ -38,7 +38,7 @@ RocketMQ主要由 Producer、Broker、Consumer 三部分组成,其中Producer 广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。 ## 13 普通顺序消息(Normal Ordered Message) -普通顺序消费模式下,消费者通过同一个消费队列收到的消息是有顺序的,不同消息队列收到的消息则可能是无顺序的。 +普通顺序消费模式下,消费者通过同一个消息队列( Topic 分区,称作 Message Queue) 收到的消息是有顺序的,不同消息队列收到的消息则可能是无顺序的。 ## 14 严格顺序消息(Strictly Ordered Message) 严格顺序消息模式下,消费者收到的所有消息均是有顺序的。 diff --git a/docs/cn/design.md b/docs/cn/design.md index dcd39026f2..1c32521ed5 100644 --- a/docs/cn/design.md +++ b/docs/cn/design.md @@ -58,7 +58,7 @@ code |int | 请求操作码,应答方根据不同的请求码进行不同的 language | LanguageCode | 请求方实现的语言 | 应答方实现的语言 version | int | 请求方程序的版本 | 应答方程序的版本 opaque | int |相当于requestId,在同一个连接上的不同请求标识码,与响应消息中的相对应 | 应答不做修改直接返回 -flag | int | 区分是普通RPC还是onewayRPC得标志 | 区分是普通RPC还是onewayRPC得标志 +flag | int | 区分是普通RPC还是onewayRPC的标志 | 区分是普通RPC还是onewayRPC的标志 remark | String | 传输自定义文本信息 | 传输自定义文本信息 extFields | HashMap | 请求自定义扩展信息 | 响应自定义扩展信息 diff --git a/docs/cn/image/rocketmq_architecture_1.png b/docs/cn/image/rocketmq_architecture_1.png index b8120b2722..1d3e82b354 100644 Binary files a/docs/cn/image/rocketmq_architecture_1.png and b/docs/cn/image/rocketmq_architecture_1.png differ diff --git a/docs/cn/image/rocketmq_architecture_3.png b/docs/cn/image/rocketmq_architecture_3.png index aa74ed7ac1..6460f8e1c3 100644 Binary files a/docs/cn/image/rocketmq_architecture_3.png and b/docs/cn/image/rocketmq_architecture_3.png differ diff --git a/docs/cn/rpc_request.md b/docs/cn/rpc_request.md index a8fe802a15..ffd2bb87e3 100644 --- a/docs/cn/rpc_request.md +++ b/docs/cn/rpc_request.md @@ -4,7 +4,7 @@ ## 1 使用场景 随着服务规模的扩大,单机服务无法满足性能和容量的要求,此时需要将服务拆分为更小粒度的服务或者部署多个服务实例构成集群来提供服务。在分布式场景下,RPC是最常用的联机调用的方式。 -在构建分布式应用时,有些领域,例如金融服务领域,常常使用消息队列来构建服务总线,实现联机调用的目的。消息队列的主要场景是解耦、削峰填谷,在联机调用的场景下,需要将服务的调用抽象成基于消息的交互,并增强同步调用的这种交互逻辑。为了更好地支持消息队列在联机调用场景下的应用,rocketmq-4.7.0推出了“Request-Reply”特性来支持RPC调用。 +在构建分布式应用时,有些领域,例如金融服务领域,常常使用消息队列来构建服务总线,实现联机调用的目的。消息队列的主要场景是解耦、削峰填谷,在联机调用的场景下,需要将服务的调用抽象成基于消息的交互,并增强同步调用的这种交互逻辑。为了更好地支持消息队列在联机调用场景下的应用,rocketmq-4.6.0推出了“Request-Reply”特性来支持RPC调用。 ## 2 设计思路 在rocketmq中,整个同步调用主要包括两个过程: diff --git a/docs/en/Design_LoadBlancing.md b/docs/en/Design_LoadBlancing.md index e6bb66f8ac..971e8b5dd4 100644 --- a/docs/en/Design_LoadBlancing.md +++ b/docs/en/Design_LoadBlancing.md @@ -1,22 +1,24 @@ ## Load Balancing Load balancing in RocketMQ is accomplished on Client side. Specifically, it can be divided into load balancing at Producer side when sending messages and load balancing at Constumer side when subscribing messages. -### 1 Producer Load Balancing +### Producer Load Balancing When the Producer sends a message, it will first find the specified TopicPublishInfo according to Topic. After getting the routing information of TopicPublishInfo, the RocketMQ client will select a queue (MessageQueue) from the messageQueue List in TopicPublishInfo to send the message by default.Specific fault-tolerant strategies are defined in the MQFaultStrategy class. Here is a sendLatencyFaultEnable switch variable, which, if turned on, filters out the Broker agent of not available on the basis of randomly gradually increasing modular arithmetic selection. The so-called "latencyFault Tolerance" refers to a certain period of time to avoid previous failures. For example, if the latency of the last request exceeds 550 Lms, it will evade 3000 Lms; if it exceeds 1000L, it will evade 60000 L; if it is closed, it will choose a queue (MessageQueue) to send messages by randomly gradually increasing modular arithmetic, and the latencyFault Tolerance mechanism is the key to achieve high availability of message sending. -### 2 Consumer Load Balancing +### Consumer Load Balancing In RocketMQ, the two consumption modes (Push/Pull) on the Consumer side are both based on the pull mode to get the message, while in the Push mode it is only a kind of encapsulation of the pull mode, which is essentially implemented as the message pulling thread after pulling a batch of messages from the server. After submitting to the message consuming thread pool, it continues to try again to pull the message to the server. If the message is not pulled, the pull is delayed and continues. In both pull mode based consumption patterns (Push/Pull), the Consumer needs to know which message queue - queue from the Broker side to get the message. Therefore, it is necessary to do load balancing on the Consumer side, that is, which Consumer consumption is allocated to the same ConsumerGroup by more than one MessageQueue on the Broker side. - 1, Heartbeat Packet Sending on Consumer side +#### 1 Heartbeat Packet Sending on Consumer side After Consumer is started, it continuously sends heartbeat packets to all Broker instances in the RocketMQ cluster via timing task (which contains the message consumption group name, subscription relationship collection,Message communication mode and the value of the client id,etc). After receiving the heartbeat message from Consumer, Broker side maintains it in Consumer Manager's local caching variable—consumerTable, At the same time, the encapsulated client network channel information is stored in the local caching variable—channelInfoTable, which can provide metadata information for the later load balancing of Consumer. -2,Core Class for Load Balancing on Consumer side—RebalanceImpl +#### 2 Core Class for Load Balancing on Consumer side—RebalanceImpl Starting the MQClientInstance instance in the startup process of the Consumer instance will complete the start of the load balancing service thread-RebalanceService (executed every 20 s). By looking at the source code, we can find that the run () method of the RebalanceService thread calls the rebalanceByTopic () method of the RebalanceImpl class, which is the core of the Consumer end load balancing. Here, rebalanceByTopic () method will do different logical processing depending on whether the consumer communication type is "broadcast mode" or "cluster mode". Here we mainly look at the main processing flow in cluster mode: -(1) Get the message consumption queue set (mqSet) under the Topic from the local cache variable—topicSubscribeInfoTable of the rebalanceImpl instance. -(2) Call mQClientFactory. findConsumerIdList () method to send a RPC communication request to Broker side to obtain the consumer Id list under the consumer group based on the parameters of topic and consumer group (consumer table constructed by Broker side based on the heartbeat data reported by the front consumer side responds and returns, business request code: GET_CONSUMER_LIST_BY_GROUP); -(3) First, the message consumption queue and the consumer Id under Topic are sorted, then the message queue to be pulled is calculated by using the message queue allocation strategy algorithm (default: the average allocation algorithm of the message queue). The average allocation algorithm here is similar to the paging algorithm. It ranks all MessageQueues like records. It ranks all consumers like pages. It calculates the average size of each page and the range of each page record. Finally, it traverses the whole range and calculates the records that the current consumer should allocate to (MessageQueue here). +##### 1) Get the message consumption queue set (mqSet) under the Topic from the local cache variable—topicSubscribeInfoTable of the rebalanceImpl instance. +##### 2) Call mQClientFactory. findConsumerIdList () method to send a RPC communication request to Broker side to obtain the consumer Id list under the consumer group based on the parameters of topic and consumer group (consumer table constructed by Broker side based on the heartbeat data reported by the front consumer side responds and returns, business request code: GET_CONSUMER_LIST_BY_GROUP); +##### 3) First, the message consumption queue and the consumer Id under Topic are sorted, then the message queue to be pulled is calculated by using the message queue allocation strategy algorithm (default: the average allocation algorithm of the message queue). The average allocation algorithm here is similar to the paging algorithm. It ranks all MessageQueues like records. It ranks all consumers like pages. It calculates the average size of each page and the range of each page record. Finally, it traverses the whole range and calculates the records that the current consumer should allocate to (MessageQueue here). ![Image text](https://github.com/apache/rocketmq/raw/develop/docs/cn/image/rocketmq_design_8.png) -(4) Then, the updateProcessQueueTableInRebalance () method is invoked, which first compares the allocated message queue set (mqSet) with processQueueTable for filtering. + + +##### 4) Then, the updateProcessQueueTableInRebalance () method is invoked, which first compares the allocated message queue set (mqSet) with processQueueTable for filtering. ![Image text](https://github.com/apache/rocketmq/raw/develop/docs/cn/image/rocketmq_design_9.png) - The red part of the processQueueTable annotation in the figure above @@ -37,6 +39,6 @@ Starting the MQClientInstance instance in the startup process of the Consumer in removeUnnecessaryMessageQueue () method to try to remove Entry as above; -Finally, a ProcessQueue object is created for each MessageQueue in the filtered message queue set (mqSet) and stored in the processQueueTable queue of RebalanceImpl (where the computePullFromWhere (MessageQueue mq) method of the RebalanceImpl instance is invoked to obtain the next progress consumption value offset of the MessageQueue object, which is then populated into the attribute of pullRequest object to be created next time.), and create pull request object—pullRequest to add to pull list—pullRequestList, and finally execute dispatchPullRequest () method. PullRequest object of Pull message is put into the blocking queue pullRequestQueue of PullMessageService service thread in turn, and the request of Pull message is sent to Broker end after the service thread takes out. Among them, we can focus on the contrast, RebalancePushImpl and RebalancePullImpl two implementation classes dispatchPullRequest () method is different, the method in RebalancePullImpl class is empty, thus answering the last question in the previous article. +Finally, a ProcessQueue object is created for each MessageQueue in the filtered message queue set (mqSet) and stored in the processQueueTable queue of RebalanceImpl (where the computePullFromWhere (MessageQueue mq) method of the RebalanceImpl instance is invoked to obtain the next progress consumption value offset of the MessageQueue object, which is then populated into the attribute of pullRequest object to be created next time.), and create pull request object—pullRequest to add to pull list—pullRequestList, and finally execute dispatchPullRequest () method. PullRequest object of Pull message is put into the blocking queue pullRequestQueue of PullMessageService service thread in turn, and the request of Pull message is sent to Broker end after the service thread takes out. The core design idea of message consumption queue is that a message consumption queue can only be consumed by one consumer in the same consumer group at the same time, and a message consumer can consume multiple message queues at the same time. diff --git a/docs/en/image/rocketmq_architecture_1.png b/docs/en/image/rocketmq_architecture_1.png index 548c8752a8..1d3e82b354 100644 Binary files a/docs/en/image/rocketmq_architecture_1.png and b/docs/en/image/rocketmq_architecture_1.png differ diff --git a/docs/en/image/rocketmq_architecture_3.png b/docs/en/image/rocketmq_architecture_3.png index aa74ed7ac1..6460f8e1c3 100644 Binary files a/docs/en/image/rocketmq_architecture_3.png and b/docs/en/image/rocketmq_architecture_3.png differ diff --git a/example/pom.xml b/example/pom.xml index 4a427d5733..568dd80295 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -19,7 +19,7 @@ rocketmq-all org.apache.rocketmq - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 @@ -52,5 +52,15 @@ org.javassist javassist + + io.jaegertracing + jaeger-core + 1.6.0 + + + io.jaegertracing + jaeger-client + 1.6.0 + diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java index c0a2a8b53a..8a6429b2aa 100644 --- a/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java +++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/Consumer.java @@ -52,8 +52,9 @@ public class Consumer { } final String topic = commandLine.hasOption('t') ? commandLine.getOptionValue('t').trim() : "BenchmarkTest"; + final int threadCount = commandLine.hasOption('w') ? Integer.parseInt(commandLine.getOptionValue('w')) : 20; final String groupPrefix = commandLine.hasOption('g') ? commandLine.getOptionValue('g').trim() : "benchmark_consumer"; - final String isSuffixEnable = commandLine.hasOption('p') ? commandLine.getOptionValue('p').trim() : "true"; + final String isSuffixEnable = commandLine.hasOption('p') ? commandLine.getOptionValue('p').trim() : "false"; final String filterType = commandLine.hasOption('f') ? commandLine.getOptionValue('f').trim() : null; final String expression = commandLine.hasOption('e') ? commandLine.getOptionValue('e').trim() : null; final double failRate = commandLine.hasOption('r') ? Double.parseDouble(commandLine.getOptionValue('r').trim()) : 0.0; @@ -65,8 +66,8 @@ public class Consumer { group = groupPrefix + "_" + (System.currentTimeMillis() % 100); } - System.out.printf("topic: %s, group: %s, suffix: %s, filterType: %s, expression: %s, msgTraceEnable: %s, aclEnable: %s%n", - topic, group, isSuffixEnable, filterType, expression, msgTraceEnable, aclEnable); + System.out.printf("topic: %s, threadCount %d, group: %s, suffix: %s, filterType: %s, expression: %s, msgTraceEnable: %s, aclEnable: %s%n", + topic, threadCount, group, isSuffixEnable, filterType, expression, msgTraceEnable, aclEnable); final StatsBenchmarkConsumer statsBenchmarkConsumer = new StatsBenchmarkConsumer(); @@ -101,8 +102,8 @@ public class Consumer { statsBenchmarkConsumer.getBorn2ConsumerMaxRT().set(0); statsBenchmarkConsumer.getStore2ConsumerMaxRT().set(0); - System.out.printf("TPS: %d FAIL: %d AVG(B2C) RT: %7.3f AVG(S2C) RT: %7.3f MAX(B2C) RT: %d MAX(S2C) RT: %d%n", - consumeTps, failCount, averageB2CRT, averageS2CRT, b2cMax, s2cMax + System.out.printf("Current Time: %s TPS: %d FAIL: %d AVG(B2C) RT(ms): %7.3f AVG(S2C) RT(ms): %7.3f MAX(B2C) RT(ms): %d MAX(S2C) RT(ms): %d%n", + System.currentTimeMillis(), consumeTps, failCount, averageB2CRT, averageS2CRT, b2cMax, s2cMax ); } } @@ -123,6 +124,8 @@ public class Consumer { String ns = commandLine.getOptionValue('n'); consumer.setNamesrvAddr(ns); } + consumer.setConsumeThreadMin(threadCount); + consumer.setConsumeThreadMax(threadCount); consumer.setInstanceName(Long.toString(System.currentTimeMillis())); if (filterType == null || expression == null) { @@ -179,11 +182,15 @@ public class Consumer { opt.setRequired(false); options.addOption(opt); + opt = new Option("w", "threadCount", true, "Thread count, Default: 20"); + opt.setRequired(false); + options.addOption(opt); + opt = new Option("g", "group", true, "Consumer group name, Default: benchmark_consumer"); opt.setRequired(false); options.addOption(opt); - opt = new Option("p", "group prefix enable", true, "Consumer group name, Default: false"); + opt = new Option("p", "group suffix enable", true, "Consumer group suffix enable, Default: false"); opt.setRequired(false); options.addOption(opt); diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java index dbad169221..6975ab58b0 100644 --- a/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java +++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/Producer.java @@ -17,12 +17,14 @@ package org.apache.rocketmq.example.benchmark; import java.io.UnsupportedEncodingException; +import java.util.Arrays; import java.util.LinkedList; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.cli.CommandLine; @@ -41,6 +43,7 @@ import org.apache.rocketmq.remoting.exception.RemotingException; import org.apache.rocketmq.srvutil.ServerUtil; public class Producer { + public static void main(String[] args) throws MQClientException, UnsupportedEncodingException { Options options = ServerUtil.buildCommandlineOptions(new Options()); @@ -54,11 +57,13 @@ public class Producer { final int messageSize = commandLine.hasOption('s') ? Integer.parseInt(commandLine.getOptionValue('s')) : 128; final boolean keyEnable = commandLine.hasOption('k') && Boolean.parseBoolean(commandLine.getOptionValue('k')); final int propertySize = commandLine.hasOption('p') ? Integer.parseInt(commandLine.getOptionValue('p')) : 0; + final int tagCount = commandLine.hasOption('l') ? Integer.parseInt(commandLine.getOptionValue('l')) : 0; final boolean msgTraceEnable = commandLine.hasOption('m') && Boolean.parseBoolean(commandLine.getOptionValue('m')); final boolean aclEnable = commandLine.hasOption('a') && Boolean.parseBoolean(commandLine.getOptionValue('a')); + final long messageNum = commandLine.hasOption('q') ? Long.parseLong(commandLine.getOptionValue('q')) : 0; - System.out.printf("topic %s threadCount %d messageSize %d keyEnable %s traceEnable %s aclEnable %s%n", - topic, threadCount, messageSize, keyEnable, msgTraceEnable, aclEnable); + System.out.printf("topic: %s threadCount: %d messageSize: %d keyEnable: %s propertySize: %d tagCount: %d traceEnable: %s aclEnable: %s messageQuantity: %d%n", + topic, threadCount, messageSize, keyEnable, propertySize, tagCount, msgTraceEnable, aclEnable, messageNum); final InternalLogger log = ClientLogger.getLog(); @@ -70,6 +75,16 @@ public class Producer { final LinkedList snapshotList = new LinkedList(); + final long[] msgNums = new long[threadCount]; + + if (messageNum > 0) { + Arrays.fill(msgNums, messageNum / threadCount); + long mod = messageNum % threadCount; + if (mod > 0) { + msgNums[0] += mod; + } + } + timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { @@ -83,14 +98,7 @@ public class Producer { timer.scheduleAtFixedRate(new TimerTask() { private void printStats() { if (snapshotList.size() >= 10) { - Long[] begin = snapshotList.getFirst(); - Long[] end = snapshotList.getLast(); - - final long sendTps = (long) (((end[3] - begin[3]) / (double) (end[0] - begin[0])) * 1000L); - final double averageRT = (end[5] - begin[5]) / (double) (end[3] - begin[3]); - - System.out.printf("Send TPS: %d Max RT: %d Average RT: %7.3f Send Failed: %d Response Failed: %d%n", - sendTps, statsBenchmark.getSendMessageMaxRT().get(), averageRT, end[2], end[4]); + doPrintStats(snapshotList, statsBenchmark, false); } } @@ -118,9 +126,14 @@ public class Producer { producer.start(); for (int i = 0; i < threadCount; i++) { + final long msgNumLimit = msgNums[i]; + if (messageNum > 0 && msgNumLimit == 0) { + break; + } sendThreadPool.execute(new Runnable() { @Override public void run() { + int num = 0; while (true) { try { final Message msg; @@ -134,6 +147,10 @@ public class Producer { if (keyEnable) { msg.setKeys(String.valueOf(beginTimestamp / 1000)); } + if (tagCount > 0) { + long sendSucCount = statsBenchmark.getReceiveResponseSuccessCount().get(); + msg.setTags(String.format("tag%d", sendSucCount % tagCount)); + } if (propertySize > 0) { if (msg.getProperties() != null) { msg.getProperties().clear(); @@ -192,10 +209,28 @@ public class Producer { } catch (InterruptedException ignored) { } } + if (messageNum > 0 && ++num >= msgNumLimit) { + break; + } } } }); } + try { + sendThreadPool.shutdown(); + sendThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); + timer.cancel(); + if (snapshotList.size() > 1) { + doPrintStats(snapshotList, statsBenchmark, true); + } else { + System.out.printf("[Complete] Send Total: %d Send Failed: %d Response Failed: %d%n", + statsBenchmark.getSendRequestSuccessCount().get() + statsBenchmark.getSendRequestFailedCount().get(), + statsBenchmark.getSendRequestFailedCount().get(), statsBenchmark.getReceiveResponseFailedCount().get()); + } + producer.shutdown(); + } catch (InterruptedException e) { + log.error("[Exit] Thread Interrupted Exception", e); + } } public static Options buildCommandlineOptions(final Options options) { @@ -215,6 +250,10 @@ public class Producer { opt.setRequired(false); options.addOption(opt); + opt = new Option("l", "tagCount", true, "Tag count, Default: 0"); + opt.setRequired(false); + options.addOption(opt); + opt = new Option("m", "msgTraceEnable", true, "Message Trace Enable, Default: false"); opt.setRequired(false); options.addOption(opt); @@ -223,6 +262,10 @@ public class Producer { opt.setRequired(false); options.addOption(opt); + opt = new Option("q", "messageQuantity", true, "Send message quantity, Default: 0, running forever"); + opt.setRequired(false); + options.addOption(opt); + return options; } @@ -239,6 +282,23 @@ public class Producer { return msg; } + + private static void doPrintStats(final LinkedList snapshotList, final StatsBenchmarkProducer statsBenchmark, boolean done) { + Long[] begin = snapshotList.getFirst(); + Long[] end = snapshotList.getLast(); + + final long sendTps = (long) (((end[3] - begin[3]) / (double) (end[0] - begin[0])) * 1000L); + final double averageRT = (end[5] - begin[5]) / (double) (end[3] - begin[3]); + + if (done) { + System.out.printf("[Complete] Send Total: %d Send TPS: %d Max RT(ms): %d Average RT(ms): %7.3f Send Failed: %d Response Failed: %d%n", + statsBenchmark.getSendRequestSuccessCount().get() + statsBenchmark.getSendRequestFailedCount().get(), + sendTps, statsBenchmark.getSendMessageMaxRT().get(), averageRT, end[2], end[4]); + } else { + System.out.printf("Current Time: %s Send TPS: %d Max RT(ms): %d Average RT(ms): %7.3f Send Failed: %d Response Failed: %d%n", + System.currentTimeMillis(), sendTps, statsBenchmark.getSendMessageMaxRT().get(), averageRT, end[2], end[4]); + } + } } class StatsBenchmarkProducer { diff --git a/example/src/main/java/org/apache/rocketmq/example/benchmark/TransactionProducer.java b/example/src/main/java/org/apache/rocketmq/example/benchmark/TransactionProducer.java index 951b718d1e..85af04eabb 100644 --- a/example/src/main/java/org/apache/rocketmq/example/benchmark/TransactionProducer.java +++ b/example/src/main/java/org/apache/rocketmq/example/benchmark/TransactionProducer.java @@ -105,8 +105,8 @@ public class TransactionProducer { final long dupCheck = end.duplicatedCheck - begin.duplicatedCheck; System.out.printf( - "Send TPS:%5d Max RT:%5d AVG RT:%3.1f Send Failed: %d check: %d unexpectedCheck: %d duplicatedCheck: %d %n", - sendTps, statsBenchmark.getSendMessageMaxRT().get(), averageRT, failCount, checkCount, + "Current Time: %s Send TPS:%5d Max RT(ms):%5d AVG RT(ms):%3.1f Send Failed: %d check: %d unexpectedCheck: %d duplicatedCheck: %d %n", + System.currentTimeMillis(), sendTps, statsBenchmark.getSendMessageMaxRT().get(), averageRT, failCount, checkCount, unexpectedCheck, dupCheck); statsBenchmark.getSendMessageMaxRT().set(0); } diff --git a/example/src/main/java/org/apache/rocketmq/example/ordermessage/Producer.java b/example/src/main/java/org/apache/rocketmq/example/ordermessage/Producer.java index 6a6bdc7d71..c5d864fa3f 100644 --- a/example/src/main/java/org/apache/rocketmq/example/ordermessage/Producer.java +++ b/example/src/main/java/org/apache/rocketmq/example/ordermessage/Producer.java @@ -16,12 +16,9 @@ */ package org.apache.rocketmq.example.ordermessage; -import java.io.UnsupportedEncodingException; -import java.util.List; import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.MQProducer; import org.apache.rocketmq.client.producer.MessageQueueSelector; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; @@ -29,10 +26,13 @@ import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.remoting.common.RemotingHelper; import org.apache.rocketmq.remoting.exception.RemotingException; +import java.io.UnsupportedEncodingException; +import java.util.List; + public class Producer { public static void main(String[] args) throws UnsupportedEncodingException { try { - MQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); + DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name"); producer.start(); String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"}; diff --git a/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingProducer.java b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingProducer.java new file mode 100644 index 0000000000..cd9ae2792b --- /dev/null +++ b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingProducer.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.example.tracemessage; + +import io.jaegertracing.Configuration; +import io.jaegertracing.internal.samplers.ConstSampler; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.trace.hook.SendMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.remoting.common.RemotingHelper; + +public class OpenTracingProducer { + public static void main(String[] args) throws MQClientException { + + Tracer tracer = initTracer(); + + DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); + producer.getDefaultMQProducerImpl().registerSendMessageHook(new SendMessageOpenTracingHookImpl(tracer)); + producer.start(); + + try { + Message msg = new Message("TopicTest", + "TagA", + "OrderID188", + "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); + SendResult sendResult = producer.send(msg); + System.out.printf("%s%n", sendResult); + + } catch (Exception e) { + e.printStackTrace(); + } + + producer.shutdown(); + } + + private static Tracer initTracer() { + Configuration.SamplerConfiguration samplerConfig = Configuration.SamplerConfiguration.fromEnv() + .withType(ConstSampler.TYPE) + .withParam(1); + Configuration.ReporterConfiguration reporterConfig = Configuration.ReporterConfiguration.fromEnv() + .withLogSpans(true); + + Configuration config = new Configuration("rocketmq") + .withSampler(samplerConfig) + .withReporter(reporterConfig); + GlobalTracer.registerIfAbsent(config.getTracer()); + return config.getTracer(); + } +} diff --git a/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingPushConsumer.java b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingPushConsumer.java new file mode 100644 index 0000000000..1d5d8a273b --- /dev/null +++ b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingPushConsumer.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.example.tracemessage; + +import io.jaegertracing.Configuration; +import io.jaegertracing.internal.samplers.ConstSampler; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.trace.hook.ConsumeMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.consumer.ConsumeFromWhere; +import org.apache.rocketmq.common.message.MessageExt; + +import java.util.List; + +public class OpenTracingPushConsumer { + public static void main(String[] args) throws InterruptedException, MQClientException { + Tracer tracer = initTracer(); + + DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1"); + consumer.getDefaultMQPushConsumerImpl().registerConsumeMessageHook(new ConsumeMessageOpenTracingHookImpl(tracer)); + + consumer.subscribe("TopicTest", "*"); + consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); + + consumer.setConsumeTimestamp("20181109221800"); + consumer.registerMessageListener(new MessageListenerConcurrently() { + + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { + System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs); + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + } + }); + consumer.start(); + System.out.printf("Consumer Started.%n"); + } + + private static Tracer initTracer() { + Configuration.SamplerConfiguration samplerConfig = Configuration.SamplerConfiguration.fromEnv() + .withType(ConstSampler.TYPE) + .withParam(1); + Configuration.ReporterConfiguration reporterConfig = Configuration.ReporterConfiguration.fromEnv() + .withLogSpans(true); + + Configuration config = new Configuration("rocketmq") + .withSampler(samplerConfig) + .withReporter(reporterConfig); + GlobalTracer.registerIfAbsent(config.getTracer()); + return config.getTracer(); + } +} diff --git a/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingTransactionProducer.java b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingTransactionProducer.java new file mode 100644 index 0000000000..514f3ceb7f --- /dev/null +++ b/example/src/main/java/org/apache/rocketmq/example/tracemessage/OpenTracingTransactionProducer.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.rocketmq.example.tracemessage; + +import io.jaegertracing.Configuration; +import io.jaegertracing.internal.samplers.ConstSampler; +import io.opentracing.Tracer; +import io.opentracing.util.GlobalTracer; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.LocalTransactionState; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.TransactionListener; +import org.apache.rocketmq.client.producer.TransactionMQProducer; +import org.apache.rocketmq.client.trace.hook.EndTransactionOpenTracingHookImpl; +import org.apache.rocketmq.client.trace.hook.SendMessageOpenTracingHookImpl; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.remoting.common.RemotingHelper; + +import java.io.UnsupportedEncodingException; + +public class OpenTracingTransactionProducer { + public static void main(String[] args) throws MQClientException, InterruptedException { + Tracer tracer = initTracer(); + + TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name"); + producer.getDefaultMQProducerImpl().registerSendMessageHook(new SendMessageOpenTracingHookImpl(tracer)); + producer.getDefaultMQProducerImpl().registerEndTransactionHook(new EndTransactionOpenTracingHookImpl(tracer)); + + producer.setTransactionListener(new TransactionListener() { + @Override + public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + + @Override + public LocalTransactionState checkLocalTransaction(MessageExt msg) { + return LocalTransactionState.COMMIT_MESSAGE; + } + }); + producer.start(); + + try { + Message msg = new Message("TopicTest", "Tag", "KEY", + "Hello RocketMQ".getBytes(RemotingHelper.DEFAULT_CHARSET)); + SendResult sendResult = producer.sendMessageInTransaction(msg, null); + System.out.printf("%s%n", sendResult); + } catch (MQClientException | UnsupportedEncodingException e) { + e.printStackTrace(); + } + + for (int i = 0; i < 100000; i++) { + Thread.sleep(1000); + } + producer.shutdown(); + } + + private static Tracer initTracer() { + Configuration.SamplerConfiguration samplerConfig = Configuration.SamplerConfiguration.fromEnv() + .withType(ConstSampler.TYPE) + .withParam(1); + Configuration.ReporterConfiguration reporterConfig = Configuration.ReporterConfiguration.fromEnv() + .withLogSpans(true); + + Configuration config = new Configuration("rocketmq") + .withSampler(samplerConfig) + .withReporter(reporterConfig); + GlobalTracer.registerIfAbsent(config.getTracer()); + return config.getTracer(); + } +} diff --git a/filter/pom.xml b/filter/pom.xml index 94bfd16b8c..c12dd1fb28 100644 --- a/filter/pom.xml +++ b/filter/pom.xml @@ -20,7 +20,7 @@ rocketmq-all org.apache.rocketmq - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java b/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java index 53f7e1c297..c10250e3bb 100644 --- a/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java +++ b/filter/src/main/java/org/apache/rocketmq/filter/parser/SimpleCharStream.java @@ -36,8 +36,8 @@ public class SimpleCharStream { * Position in buffer. */ public int bufpos = -1; - protected int bufline[]; - protected int bufcolumn[]; + protected int[] bufline; + protected int[] bufcolumn; protected int column = 0; protected int line = 1; @@ -62,8 +62,8 @@ public class SimpleCharStream { protected void ExpandBuff(boolean wrapAround) { char[] newbuffer = new char[bufsize + 2048]; - int newbufline[] = new int[bufsize + 2048]; - int newbufcolumn[] = new int[bufsize + 2048]; + int[] newbufline = new int[bufsize + 2048]; + int[] newbufcolumn = new int[bufsize + 2048]; try { if (wrapAround) { diff --git a/logappender/pom.xml b/logappender/pom.xml index b67d6f705f..a6e979a83e 100644 --- a/logappender/pom.xml +++ b/logappender/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 rocketmq-logappender diff --git a/logging/pom.xml b/logging/pom.xml index 917e37a9e6..77d0f29a97 100644 --- a/logging/pom.xml +++ b/logging/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java index 487682cd44..0dc81d74e6 100755 --- a/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java +++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/Level.java @@ -116,7 +116,6 @@ public class Level implements Serializable { if (s.equals(OFF_NAME)) { return Level.OFF; } - if (s.equals(INFO_NAME)) { return Level.INFO; } diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java index f457a69567..3ec440bc88 100644 --- a/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java +++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/LoggingBuilder.java @@ -1201,7 +1201,7 @@ public class LoggingBuilder { public String format(LoggingEvent event) { StringBuilder sb = new StringBuilder(); - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,sss"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS"); String format = simpleDateFormat.format(new Date(event.timeStamp)); sb.append(format); sb.append(" "); diff --git a/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java b/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java index b6d1049778..aaba4d6e89 100755 --- a/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java +++ b/logging/src/main/java/org/apache/rocketmq/logging/inner/SysLogger.java @@ -33,13 +33,13 @@ public class SysLogger { public static void debug(String msg) { if (debugEnabled && !quietMode) { - System.out.printf("%s", PREFIX + msg); + System.err.println(PREFIX + msg); } } public static void debug(String msg, Throwable t) { if (debugEnabled && !quietMode) { - System.out.printf("%s", PREFIX + msg); + System.err.println(PREFIX + msg); if (t != null) { t.printStackTrace(System.out); } diff --git a/namesrv/pom.xml b/namesrv/pom.xml index 9a7eb35047..c5d6d05bc2 100644 --- a/namesrv/pom.xml +++ b/namesrv/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java index ecd057a29a..edef87ce2d 100644 --- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java +++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/RouteInfoManager.java @@ -220,7 +220,7 @@ public class RouteInfoManager { queueData.setWriteQueueNums(topicConfig.getWriteQueueNums()); queueData.setReadQueueNums(topicConfig.getReadQueueNums()); queueData.setPerm(topicConfig.getPerm()); - queueData.setTopicSynFlag(topicConfig.getTopicSysFlag()); + queueData.setTopicSysFlag(topicConfig.getTopicSysFlag()); List queueDataList = this.topicQueueTable.get(topicConfig.getTopicName()); if (null == queueDataList) { @@ -684,7 +684,7 @@ public class RouteInfoManager { String topic = topicEntry.getKey(); List queueDatas = topicEntry.getValue(); if (queueDatas != null && queueDatas.size() > 0 - && TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSynFlag())) { + && TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSysFlag())) { topicList.getTopicList().add(topic); } } @@ -710,7 +710,7 @@ public class RouteInfoManager { String topic = topicEntry.getKey(); List queueDatas = topicEntry.getValue(); if (queueDatas != null && queueDatas.size() > 0 - && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSynFlag())) { + && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSysFlag())) { topicList.getTopicList().add(topic); } } @@ -736,8 +736,8 @@ public class RouteInfoManager { String topic = topicEntry.getKey(); List queueDatas = topicEntry.getValue(); if (queueDatas != null && queueDatas.size() > 0 - && !TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSynFlag()) - && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSynFlag())) { + && !TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSysFlag()) + && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSysFlag())) { topicList.getTopicList().add(topic); } } diff --git a/openmessaging/pom.xml b/openmessaging/pom.xml index 8a0db607d9..7f0e937996 100644 --- a/openmessaging/pom.xml +++ b/openmessaging/pom.xml @@ -20,7 +20,7 @@ rocketmq-all org.apache.rocketmq - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index 9370ecfe57..39f368538e 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 2012 org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT pom Apache RocketMQ ${project.version} http://rocketmq.apache.org/ @@ -102,7 +102,6 @@ ${project.basedir}/../test/target/jacoco-it.exec file:**/generated-sources/**,**/test/** - 2.0.2 @@ -425,7 +424,7 @@ junit junit - 4.11 + 4.13.2 test @@ -437,19 +436,13 @@ org.mockito mockito-core - 2.23.0 + 3.10.0 test - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito2 - ${powermock.version} + org.awaitility + awaitility + 4.1.0 test @@ -549,7 +542,7 @@ com.alibaba fastjson - 1.2.70 + 1.2.76 org.javassist @@ -604,7 +597,7 @@ commons-validator commons-validator - 1.6 + 1.7 diff --git a/remoting/pom.xml b/remoting/pom.xml index 27f44eab31..d9dab9efa6 100644 --- a/remoting/pom.xml +++ b/remoting/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 @@ -40,10 +40,5 @@ ${project.groupId} rocketmq-logging - - io.netty - netty-tcnative-boringssl-static - 1.1.33.Fork26 - diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/common/RemotingUtil.java b/remoting/src/main/java/org/apache/rocketmq/remoting/common/RemotingUtil.java index a16940e561..d936c3bf69 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/common/RemotingUtil.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/common/RemotingUtil.java @@ -19,6 +19,7 @@ package org.apache.rocketmq.remoting.common; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; +import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.Inet6Address; @@ -98,6 +99,10 @@ public class RemotingUtil { ArrayList ipv6Result = new ArrayList(); while (enumeration.hasMoreElements()) { final NetworkInterface networkInterface = enumeration.nextElement(); + if (isBridge(networkInterface)) { + continue; + } + final Enumeration en = networkInterface.getInetAddresses(); while (en.hasMoreElements()) { final InetAddress address = en.nextElement(); @@ -160,6 +165,19 @@ public class RemotingUtil { return sb.toString(); } + private static boolean isBridge(NetworkInterface networkInterface) { + try { + if (isLinuxPlatform()) { + String interfaceName = networkInterface.getName(); + File file = new File("/sys/class/net/" + interfaceName + "/bridge"); + return file.exists(); + } + } catch (SecurityException e) { + //Ignore + } + return false; + } + public static SocketChannel connect(SocketAddress remote) { return connect(remote, 1000 * 5); } diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java index d836c8e0bf..5ba6cfab0a 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingClient.java @@ -84,7 +84,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti private final AtomicReference> namesrvAddrList = new AtomicReference>(); private final AtomicReference namesrvAddrChoosed = new AtomicReference(); private final AtomicInteger namesrvIndex = new AtomicInteger(initValueIndex()); - private final Lock lockNamesrvChannel = new ReentrantLock(); + private final Lock namesrvChannelLock = new ReentrantLock(); private final ExecutorService publicExecutor; @@ -418,7 +418,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti } final List addrList = this.namesrvAddrList.get(); - if (this.lockNamesrvChannel.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + if (this.namesrvChannelLock.tryLock(LOCK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { try { addr = this.namesrvAddrChoosed.get(); if (addr != null) { @@ -445,7 +445,7 @@ public class NettyRemotingClient extends NettyRemotingAbstract implements Remoti throw new RemotingConnectException(addrList.toString()); } } finally { - this.lockNamesrvChannel.unlock(); + this.namesrvChannelLock.unlock(); } } else { log.warn("getAndCreateNameserverChannel: try to lock name server, but timeout, {}ms", LOCK_TIMEOUT_MILLIS); diff --git a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java index efbdd52c07..5003ce39ef 100644 --- a/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java +++ b/remoting/src/main/java/org/apache/rocketmq/remoting/netty/TlsHelper.java @@ -133,7 +133,7 @@ public class TlsHelper { SelfSignedCertificate selfSignedCertificate = new SelfSignedCertificate(); return SslContextBuilder .forServer(selfSignedCertificate.certificate(), selfSignedCertificate.privateKey()) - .sslProvider(SslProvider.JDK) + .sslProvider(provider) .clientAuth(ClientAuth.OPTIONAL) .build(); } else { diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstractTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstractTest.java index 5330c90060..a272d21da4 100644 --- a/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstractTest.java +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingAbstractTest.java @@ -27,8 +27,6 @@ import org.mockito.junit.MockitoJUnitRunner; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -98,7 +96,7 @@ public class NettyRemotingAbstractTest { public void testScanResponseTable() { int dummyId = 1; // mock timeout - ResponseFuture responseFuture = new ResponseFuture(null,dummyId, -1000, new InvokeCallback() { + ResponseFuture responseFuture = new ResponseFuture(null, dummyId, -1000, new InvokeCallback() { @Override public void operationComplete(final ResponseFuture responseFuture) { } diff --git a/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingClientTest.java b/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingClientTest.java index 6b5633df1d..4b38ce9524 100644 --- a/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingClientTest.java +++ b/remoting/src/test/java/org/apache/rocketmq/remoting/netty/NettyRemotingClientTest.java @@ -16,7 +16,6 @@ */ package org.apache.rocketmq.remoting.netty; -import java.lang.reflect.Field; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; diff --git a/srvutil/pom.xml b/srvutil/pom.xml index 36fe9c03a5..b95808b7c2 100644 --- a/srvutil/pom.xml +++ b/srvutil/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/store/pom.xml b/store/pom.xml index 8f4b44a288..ed54db1b24 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 @@ -31,7 +31,7 @@ io.openmessaging.storage dledger - 0.2.0 + 0.2.2 org.apache.rocketmq diff --git a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java index b6d17daa4e..cce6481b8d 100644 --- a/store/src/main/java/org/apache/rocketmq/store/CommitLog.java +++ b/store/src/main/java/org/apache/rocketmq/store/CommitLog.java @@ -661,14 +661,18 @@ public class CommitLog { storeStatsService.getSinglePutMessageTopicTimesTotal(msg.getTopic()).incrementAndGet(); storeStatsService.getSinglePutMessageTopicSizeTotal(topic).addAndGet(result.getWroteBytes()); - CompletableFuture flushResultFuture = submitFlushRequest(result, putMessageResult, msg); - CompletableFuture replicaResultFuture = submitReplicaRequest(result, putMessageResult, msg); + CompletableFuture flushResultFuture = submitFlushRequest(result, msg); + CompletableFuture replicaResultFuture = submitReplicaRequest(result, msg); return flushResultFuture.thenCombine(replicaResultFuture, (flushStatus, replicaStatus) -> { if (flushStatus != PutMessageStatus.PUT_OK) { - putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT); + putMessageResult.setPutMessageStatus(flushStatus); } if (replicaStatus != PutMessageStatus.PUT_OK) { putMessageResult.setPutMessageStatus(replicaStatus); + if (replicaStatus == PutMessageStatus.FLUSH_SLAVE_TIMEOUT) { + log.error("do sync transfer other node, wait return, but failed, topic: {} tags: {} client address: {}", + msg.getTopic(), msg.getTags(), msg.getBornHostNameString()); + } } return putMessageResult; }); @@ -762,15 +766,18 @@ public class CommitLog { storeStatsService.getSinglePutMessageTopicTimesTotal(messageExtBatch.getTopic()).addAndGet(result.getMsgNum()); storeStatsService.getSinglePutMessageTopicSizeTotal(messageExtBatch.getTopic()).addAndGet(result.getWroteBytes()); - CompletableFuture flushOKFuture = submitFlushRequest(result, putMessageResult, messageExtBatch); - CompletableFuture replicaOKFuture = submitReplicaRequest(result, putMessageResult, messageExtBatch); + CompletableFuture flushOKFuture = submitFlushRequest(result, messageExtBatch); + CompletableFuture replicaOKFuture = submitReplicaRequest(result, messageExtBatch); return flushOKFuture.thenCombine(replicaOKFuture, (flushStatus, replicaStatus) -> { if (flushStatus != PutMessageStatus.PUT_OK) { - putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT); + putMessageResult.setPutMessageStatus(flushStatus); } - if (replicaStatus != PutMessageStatus.PUT_OK) { putMessageResult.setPutMessageStatus(replicaStatus); + if (replicaStatus == PutMessageStatus.FLUSH_SLAVE_TIMEOUT) { + log.error("do sync transfer other node, wait return, but failed, topic: {} client address: {}", + messageExtBatch.getTopic(), messageExtBatch.getBornHostNameString()); + } } return putMessageResult; }); @@ -900,8 +907,7 @@ public class CommitLog { return putMessageResult; } - public CompletableFuture submitFlushRequest(AppendMessageResult result, PutMessageResult putMessageResult, - MessageExt messageExt) { + public CompletableFuture submitFlushRequest(AppendMessageResult result, MessageExt messageExt) { // Synchronization flush if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) { final GroupCommitService service = (GroupCommitService) this.flushCommitLogService; @@ -926,8 +932,7 @@ public class CommitLog { } } - public CompletableFuture submitReplicaRequest(AppendMessageResult result, PutMessageResult putMessageResult, - MessageExt messageExt) { + public CompletableFuture submitReplicaRequest(AppendMessageResult result, MessageExt messageExt) { if (BrokerRole.SYNC_MASTER == this.defaultMessageStore.getMessageStoreConfig().getBrokerRole()) { HAService service = this.defaultMessageStore.getHaService(); if (messageExt.isWaitStoreMsgOK()) { @@ -1420,13 +1425,10 @@ public class CommitLog { for (GroupCommitRequest req : this.requestsRead) { // There may be a message in the next file, so a maximum of // two times the flush - boolean flushOK = false; + boolean flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset(); for (int i = 0; i < 2 && !flushOK; i++) { + CommitLog.this.mappedFileQueue.flush(0); flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset(); - - if (!flushOK) { - CommitLog.this.mappedFileQueue.flush(0); - } } req.wakeupCustomer(flushOK ? PutMessageStatus.PUT_OK : PutMessageStatus.FLUSH_DISK_TIMEOUT); @@ -1795,6 +1797,11 @@ public class CommitLog { ByteBuffer bornHostHolder = ByteBuffer.allocate(bornHostLength); ByteBuffer storeHostHolder = ByteBuffer.allocate(storeHostLength); + // properties from MessageExtBatch + String batchPropStr = MessageDecoder.messageProperties2String(messageExtBatch.getProperties()); + final byte[] batchPropData = batchPropStr.getBytes(MessageDecoder.CHARSET_UTF8); + final short batchPropLen = (short) batchPropData.length; + while (messagesByteBuff.hasRemaining()) { // 1 TOTALSIZE messagesByteBuff.getInt(); @@ -1818,7 +1825,8 @@ public class CommitLog { final int topicLength = topicData.length; - final int msgLen = calMsgLength(messageExtBatch.getSysFlag(), bodyLen, topicLength, propertiesLen); + final int msgLen = calMsgLength(messageExtBatch.getSysFlag(), bodyLen, topicLength, + propertiesLen + batchPropLen); // Exceeds the maximum message if (msgLen > this.maxMessageSize) { @@ -1871,9 +1879,13 @@ public class CommitLog { this.msgBatchMemory.put((byte) topicLength); this.msgBatchMemory.put(topicData); // 17 PROPERTIES - this.msgBatchMemory.putShort(propertiesLen); - if (propertiesLen > 0) + this.msgBatchMemory.putShort((short) (propertiesLen + batchPropLen)); + if (propertiesLen > 0) { this.msgBatchMemory.put(messagesByteBuff.array(), propertiesPos, propertiesLen); + } + if (batchPropLen > 0) { + this.msgBatchMemory.put(batchPropData, 0, batchPropLen); + } } msgBatchMemory.flip(); return msgBatchMemory; diff --git a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java index 5462e76bfb..b8ecdee8cc 100644 --- a/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java +++ b/store/src/main/java/org/apache/rocketmq/store/DefaultMessageStore.java @@ -392,7 +392,7 @@ public class DefaultMessageStore implements MessageStore { if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) { long value = this.printTimes.getAndIncrement(); if ((value % 50000) == 0) { - log.warn("message store has shutdown, so putMessage is forbidden"); + log.warn("broke role is slave, so putMessage is forbidden"); } return PutMessageStatus.SERVICE_NOT_AVAILABLE; } @@ -400,7 +400,8 @@ public class DefaultMessageStore implements MessageStore { if (!this.runningFlags.isWriteable()) { long value = this.printTimes.getAndIncrement(); if ((value % 50000) == 0) { - log.warn("message store has shutdown, so putMessage is forbidden"); + log.warn("the message store is not writable. It may be caused by one of the following reasons: " + + "the broker's disk is full, write to logic queue error, write to index file error, etc"); } return PutMessageStatus.SERVICE_NOT_AVAILABLE; } else { @@ -1029,7 +1030,8 @@ public class DefaultMessageStore implements MessageStore { Entry> next = it.next(); String topic = next.getKey(); - if (!topics.contains(topic) && !topic.equals(TopicValidator.RMQ_SYS_SCHEDULE_TOPIC)) { + if (!topics.contains(topic) && !topic.equals(TopicValidator.RMQ_SYS_SCHEDULE_TOPIC) + && !topic.equals(TopicValidator.RMQ_SYS_TRANS_OP_HALF_TOPIC)) { ConcurrentMap queueTable = next.getValue(); for (ConsumeQueue cq : queueTable.values()) { cq.destroy(); @@ -1745,8 +1747,7 @@ public class DefaultMessageStore implements MessageStore { this.manualDeleteFileSeveralTimes = manualDeleteFileSeveralTimes; } public boolean isSpaceFull() { - String storePathPhysic = DefaultMessageStore.this.getMessageStoreConfig().getStorePathCommitLog(); - double physicRatio = UtilAll.getDiskPartitionSpaceUsedPercent(storePathPhysic); + double physicRatio = UtilAll.getDiskPartitionSpaceUsedPercent(getStorePathPhysic()); double ratio = DefaultMessageStore.this.getMessageStoreConfig().getDiskMaxUsedSpaceRatio() / 100.0; if (physicRatio > ratio) { DefaultMessageStore.log.info("physic disk of commitLog used: " + physicRatio); @@ -1946,7 +1947,8 @@ public class DefaultMessageStore implements MessageStore { DefaultMessageStore.this.doDispatch(dispatchRequest); if (BrokerRole.SLAVE != DefaultMessageStore.this.getMessageStoreConfig().getBrokerRole() - && DefaultMessageStore.this.brokerConfig.isLongPollingEnable()) { + && DefaultMessageStore.this.brokerConfig.isLongPollingEnable() + && DefaultMessageStore.this.messageArrivingListener != null) { DefaultMessageStore.this.messageArrivingListener.arriving(dispatchRequest.getTopic(), dispatchRequest.getQueueId(), dispatchRequest.getConsumeQueueOffset() + 1, dispatchRequest.getTagsCode(), dispatchRequest.getStoreTimestamp(), diff --git a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java index 9185d21d87..b5bdc7766b 100644 --- a/store/src/main/java/org/apache/rocketmq/store/MappedFile.java +++ b/store/src/main/java/org/apache/rocketmq/store/MappedFile.java @@ -321,7 +321,7 @@ public class MappedFile extends ReferenceResource { int writePos = this.wrotePosition.get(); int lastCommittedPosition = this.committedPosition.get(); - if (writePos - this.committedPosition.get() > 0) { + if (writePos - lastCommittedPosition > commitLeastPages) { try { ByteBuffer byteBuffer = writeBuffer.slice(); byteBuffer.position(lastCommittedPosition); diff --git a/store/src/main/java/org/apache/rocketmq/store/dledger/DLedgerCommitLog.java b/store/src/main/java/org/apache/rocketmq/store/dledger/DLedgerCommitLog.java index 400ad786db..9241ffe423 100644 --- a/store/src/main/java/org/apache/rocketmq/store/dledger/DLedgerCommitLog.java +++ b/store/src/main/java/org/apache/rocketmq/store/dledger/DLedgerCommitLog.java @@ -17,19 +17,25 @@ package org.apache.rocketmq.store.dledger; import io.openmessaging.storage.dledger.AppendFuture; +import io.openmessaging.storage.dledger.BatchAppendFuture; import io.openmessaging.storage.dledger.DLedgerConfig; import io.openmessaging.storage.dledger.DLedgerServer; import io.openmessaging.storage.dledger.entry.DLedgerEntry; import io.openmessaging.storage.dledger.protocol.AppendEntryRequest; import io.openmessaging.storage.dledger.protocol.AppendEntryResponse; +import io.openmessaging.storage.dledger.protocol.BatchAppendEntryRequest; import io.openmessaging.storage.dledger.protocol.DLedgerResponseCode; import io.openmessaging.storage.dledger.store.file.DLedgerMmapFileStore; import io.openmessaging.storage.dledger.store.file.MmapFile; import io.openmessaging.storage.dledger.store.file.MmapFileList; import io.openmessaging.storage.dledger.store.file.SelectMmapBufferResult; import io.openmessaging.storage.dledger.utils.DLedgerUtils; +import java.net.Inet6Address; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.apache.rocketmq.common.UtilAll; @@ -72,6 +78,8 @@ public class DLedgerCommitLog extends CommitLog { private boolean isInrecoveringOldCommitlog = false; + private final StringBuilder msgIdBuilder = new StringBuilder(); + public DLedgerCommitLog(final DefaultMessageStore defaultMessageStore) { super(defaultMessageStore); dLedgerConfig = new DLedgerConfig(); @@ -364,21 +372,14 @@ public class DLedgerCommitLog extends CommitLog { return beginTimeInDledgerLock; } - @Override - public PutMessageResult putMessage(final MessageExtBrokerInner msg) { + private void setMessageInfo(MessageExtBrokerInner msg, int tranType) { // Set the storage time msg.setStoreTimestamp(System.currentTimeMillis()); // Set the message body BODY CRC (consider the most appropriate setting // on the client) msg.setBodyCRC(UtilAll.crc32(msg.getBody())); - StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); - - String topic = msg.getTopic(); - int queueId = msg.getQueueId(); - //should be consistent with the old version - final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag()); if (tranType == MessageSysFlag.TRANSACTION_NOT_TYPE || tranType == MessageSysFlag.TRANSACTION_COMMIT_TYPE) { // Delay Delivery @@ -387,8 +388,9 @@ public class DLedgerCommitLog extends CommitLog { msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()); } - topic = TopicValidator.RMQ_SYS_SCHEDULE_TOPIC; - queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel()); + + String topic = TopicValidator.RMQ_SYS_SCHEDULE_TOPIC; + int queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel()); // Backup real topic, queueId MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic()); @@ -400,25 +402,46 @@ public class DLedgerCommitLog extends CommitLog { } } + InetSocketAddress bornSocketAddress = (InetSocketAddress) msg.getBornHost(); + if (bornSocketAddress.getAddress() instanceof Inet6Address) { + msg.setBornHostV6Flag(); + } + + InetSocketAddress storeSocketAddress = (InetSocketAddress) msg.getStoreHost(); + if (storeSocketAddress.getAddress() instanceof Inet6Address) { + msg.setStoreHostAddressV6Flag(); + } + } + + @Override + public PutMessageResult putMessage(final MessageExtBrokerInner msg) { + + StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); + final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag()); + String topic = msg.getTopic(); + setMessageInfo(msg,tranType); + // Back to Results AppendMessageResult appendResult; AppendFuture dledgerFuture; EncodeResult encodeResult; + encodeResult = this.messageSerializer.serialize(msg); + if (encodeResult.status != AppendMessageStatus.PUT_OK) { + return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(encodeResult.status)); + } + putMessageLock.lock(); //spin or ReentrantLock ,depending on store config long elapsedTimeInLock; long queueOffset; try { beginTimeInDledgerLock = this.defaultMessageStore.getSystemClock().now(); - encodeResult = this.messageSerializer.serialize(msg); - queueOffset = topicQueueTable.get(encodeResult.queueOffsetKey); - if (encodeResult.status != AppendMessageStatus.PUT_OK) { - return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(encodeResult.status)); - } + queueOffset = getQueueOffsetByKey(encodeResult.queueOffsetKey, tranType); + encodeResult.setQueueOffsetKey(queueOffset, false); AppendEntryRequest request = new AppendEntryRequest(); request.setGroup(dLedgerConfig.getGroup()); request.setRemoteId(dLedgerServer.getMemberState().getSelfId()); - request.setBody(encodeResult.data); + request.setBody(encodeResult.getData()); dledgerFuture = (AppendFuture) dLedgerServer.handleAppend(request); if (dledgerFuture.getPos() == -1) { return new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR)); @@ -430,7 +453,7 @@ public class DLedgerCommitLog extends CommitLog { String msgId = MessageDecoder.createMessageId(buffer, msg.getStoreHostBytes(), wroteOffset); elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginTimeInDledgerLock; - appendResult = new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, encodeResult.data.length, msgId, System.currentTimeMillis(), queueOffset, elapsedTimeInLock); + appendResult = new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, encodeResult.getData().length, msgId, System.currentTimeMillis(), queueOffset, elapsedTimeInLock); switch (tranType) { case MessageSysFlag.TRANSACTION_PREPARED_TYPE: case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: @@ -491,17 +514,352 @@ public class DLedgerCommitLog extends CommitLog { @Override public PutMessageResult putMessages(final MessageExtBatch messageExtBatch) { - return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null); + final int tranType = MessageSysFlag.getTransactionValue(messageExtBatch.getSysFlag()); + + if (tranType != MessageSysFlag.TRANSACTION_NOT_TYPE) { + return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null); + } + if (messageExtBatch.getDelayTimeLevel() > 0) { + return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null); + } + + // Set the storage time + messageExtBatch.setStoreTimestamp(System.currentTimeMillis()); + + StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); + + InetSocketAddress bornSocketAddress = (InetSocketAddress) messageExtBatch.getBornHost(); + if (bornSocketAddress.getAddress() instanceof Inet6Address) { + messageExtBatch.setBornHostV6Flag(); + } + + InetSocketAddress storeSocketAddress = (InetSocketAddress) messageExtBatch.getStoreHost(); + if (storeSocketAddress.getAddress() instanceof Inet6Address) { + messageExtBatch.setStoreHostAddressV6Flag(); + } + + // Back to Results + AppendMessageResult appendResult; + BatchAppendFuture dledgerFuture; + EncodeResult encodeResult; + + encodeResult = this.messageSerializer.serialize(messageExtBatch); + if (encodeResult.status != AppendMessageStatus.PUT_OK) { + return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(encodeResult + .status)); + } + + putMessageLock.lock(); //spin or ReentrantLock ,depending on store config + msgIdBuilder.setLength(0); + long elapsedTimeInLock; + long queueOffset; + long msgNum = 0; + try { + beginTimeInDledgerLock = this.defaultMessageStore.getSystemClock().now(); + queueOffset = getQueueOffsetByKey(encodeResult.queueOffsetKey, tranType); + encodeResult.setQueueOffsetKey(queueOffset, true); + BatchAppendEntryRequest request = new BatchAppendEntryRequest(); + request.setGroup(dLedgerConfig.getGroup()); + request.setRemoteId(dLedgerServer.getMemberState().getSelfId()); + request.setBatchMsgs(encodeResult.batchData); + dledgerFuture = (BatchAppendFuture) dLedgerServer.handleAppend(request); + if (dledgerFuture.getPos() == -1) { + log.warn("HandleAppend return false due to error code {}", dledgerFuture.get().getCode()); + return new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR)); + } + long wroteOffset = 0; + + int msgIdLength = (messageExtBatch.getSysFlag() & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 + 4 + 8 : 16 + 4 + 8; + ByteBuffer buffer = ByteBuffer.allocate(msgIdLength); + + boolean isFirstOffset = true; + long firstWroteOffset = 0; + for (long pos : dledgerFuture.getPositions()) { + wroteOffset = pos + DLedgerEntry.BODY_OFFSET; + if (isFirstOffset) { + firstWroteOffset = wroteOffset; + isFirstOffset = false; + } + String msgId = MessageDecoder.createMessageId(buffer, messageExtBatch.getStoreHostBytes(), wroteOffset); + if (msgIdBuilder.length() > 0) { + msgIdBuilder.append(',').append(msgId); + } else { + msgIdBuilder.append(msgId); + } + msgNum++; + } + + elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginTimeInDledgerLock; + appendResult = new AppendMessageResult(AppendMessageStatus.PUT_OK, firstWroteOffset, encodeResult.totalMsgLen, + msgIdBuilder.toString(), System.currentTimeMillis(), queueOffset, elapsedTimeInLock); + DLedgerCommitLog.this.topicQueueTable.put(encodeResult.queueOffsetKey, queueOffset + msgNum); + } catch (Exception e) { + log.error("Put message error", e); + return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, new AppendMessageResult(AppendMessageStatus + .UNKNOWN_ERROR)); + } finally { + beginTimeInDledgerLock = 0; + putMessageLock.unlock(); + } + + if (elapsedTimeInLock > 500) { + log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", + elapsedTimeInLock, messageExtBatch.getBody().length, appendResult); + } + + PutMessageStatus putMessageStatus = PutMessageStatus.UNKNOWN_ERROR; + try { + AppendEntryResponse appendEntryResponse = dledgerFuture.get(3, TimeUnit.SECONDS); + switch (DLedgerResponseCode.valueOf(appendEntryResponse.getCode())) { + case SUCCESS: + putMessageStatus = PutMessageStatus.PUT_OK; + break; + case INCONSISTENT_LEADER: + case NOT_LEADER: + case LEADER_NOT_READY: + case DISK_FULL: + putMessageStatus = PutMessageStatus.SERVICE_NOT_AVAILABLE; + break; + case WAIT_QUORUM_ACK_TIMEOUT: + //Do not return flush_slave_timeout to the client, for the ons client will ignore it. + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + case LEADER_PENDING_FULL: + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + } + } catch (Throwable t) { + log.error("Failed to get dledger append result", t); + } + + PutMessageResult putMessageResult = new PutMessageResult(putMessageStatus, appendResult); + if (putMessageStatus == PutMessageStatus.PUT_OK) { + // Statistics + storeStatsService.getSinglePutMessageTopicTimesTotal(messageExtBatch.getTopic()).addAndGet(msgNum); + storeStatsService.getSinglePutMessageTopicSizeTotal(messageExtBatch.getTopic()).addAndGet(encodeResult.totalMsgLen); + } + return putMessageResult; } @Override public CompletableFuture asyncPutMessage(MessageExtBrokerInner msg) { - return CompletableFuture.completedFuture(this.putMessage(msg)); + + StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); + + final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag()); + + setMessageInfo(msg, tranType); + + final String finalTopic = msg.getTopic(); + + // Back to Results + AppendMessageResult appendResult; + AppendFuture dledgerFuture; + EncodeResult encodeResult; + + encodeResult = this.messageSerializer.serialize(msg); + if (encodeResult.status != AppendMessageStatus.PUT_OK) { + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(encodeResult.status))); + } + putMessageLock.lock(); //spin or ReentrantLock ,depending on store config + long elapsedTimeInLock; + long queueOffset; + try { + beginTimeInDledgerLock = this.defaultMessageStore.getSystemClock().now(); + queueOffset = getQueueOffsetByKey(encodeResult.queueOffsetKey, tranType); + encodeResult.setQueueOffsetKey(queueOffset, false); + AppendEntryRequest request = new AppendEntryRequest(); + request.setGroup(dLedgerConfig.getGroup()); + request.setRemoteId(dLedgerServer.getMemberState().getSelfId()); + request.setBody(encodeResult.getData()); + dledgerFuture = (AppendFuture) dLedgerServer.handleAppend(request); + if (dledgerFuture.getPos() == -1) { + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR))); + } + long wroteOffset = dledgerFuture.getPos() + DLedgerEntry.BODY_OFFSET; + + int msgIdLength = (msg.getSysFlag() & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 + 4 + 8 : 16 + 4 + 8; + ByteBuffer buffer = ByteBuffer.allocate(msgIdLength); + + String msgId = MessageDecoder.createMessageId(buffer, msg.getStoreHostBytes(), wroteOffset); + elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginTimeInDledgerLock; + appendResult = new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, encodeResult.getData().length, msgId, System.currentTimeMillis(), queueOffset, elapsedTimeInLock); + switch (tranType) { + case MessageSysFlag.TRANSACTION_PREPARED_TYPE: + case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: + break; + case MessageSysFlag.TRANSACTION_NOT_TYPE: + case MessageSysFlag.TRANSACTION_COMMIT_TYPE: + // The next update ConsumeQueue information + DLedgerCommitLog.this.topicQueueTable.put(encodeResult.queueOffsetKey, queueOffset + 1); + break; + default: + break; + } + } catch (Exception e) { + log.error("Put message error", e); + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR))); + } finally { + beginTimeInDledgerLock = 0; + putMessageLock.unlock(); + } + + if (elapsedTimeInLock > 500) { + log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", elapsedTimeInLock, msg.getBody().length, appendResult); + } + + return dledgerFuture.thenApply(appendEntryResponse -> { + PutMessageStatus putMessageStatus = PutMessageStatus.UNKNOWN_ERROR; + switch (DLedgerResponseCode.valueOf(appendEntryResponse.getCode())) { + case SUCCESS: + putMessageStatus = PutMessageStatus.PUT_OK; + break; + case INCONSISTENT_LEADER: + case NOT_LEADER: + case LEADER_NOT_READY: + case DISK_FULL: + putMessageStatus = PutMessageStatus.SERVICE_NOT_AVAILABLE; + break; + case WAIT_QUORUM_ACK_TIMEOUT: + //Do not return flush_slave_timeout to the client, for the ons client will ignore it. + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + case LEADER_PENDING_FULL: + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + } + PutMessageResult putMessageResult = new PutMessageResult(putMessageStatus, appendResult); + if (putMessageStatus == PutMessageStatus.PUT_OK) { + // Statistics + storeStatsService.getSinglePutMessageTopicTimesTotal(finalTopic).incrementAndGet(); + storeStatsService.getSinglePutMessageTopicSizeTotal(msg.getTopic()).addAndGet(appendResult.getWroteBytes()); + } + return putMessageResult; + }); } @Override public CompletableFuture asyncPutMessages(MessageExtBatch messageExtBatch) { - return CompletableFuture.completedFuture(putMessages(messageExtBatch)); + final int tranType = MessageSysFlag.getTransactionValue(messageExtBatch.getSysFlag()); + + if (tranType != MessageSysFlag.TRANSACTION_NOT_TYPE) { + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null)); + } + if (messageExtBatch.getDelayTimeLevel() > 0) { + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null)); + } + + // Set the storage time + messageExtBatch.setStoreTimestamp(System.currentTimeMillis()); + + StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService(); + + InetSocketAddress bornSocketAddress = (InetSocketAddress) messageExtBatch.getBornHost(); + if (bornSocketAddress.getAddress() instanceof Inet6Address) { + messageExtBatch.setBornHostV6Flag(); + } + + InetSocketAddress storeSocketAddress = (InetSocketAddress) messageExtBatch.getStoreHost(); + if (storeSocketAddress.getAddress() instanceof Inet6Address) { + messageExtBatch.setStoreHostAddressV6Flag(); + } + + // Back to Results + AppendMessageResult appendResult; + BatchAppendFuture dledgerFuture; + EncodeResult encodeResult; + + encodeResult = this.messageSerializer.serialize(messageExtBatch); + if (encodeResult.status != AppendMessageStatus.PUT_OK) { + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, new AppendMessageResult(encodeResult + .status))); + } + + putMessageLock.lock(); //spin or ReentrantLock ,depending on store config + msgIdBuilder.setLength(0); + long elapsedTimeInLock; + long queueOffset; + long msgNum = 0; + try { + beginTimeInDledgerLock = this.defaultMessageStore.getSystemClock().now(); + queueOffset = getQueueOffsetByKey(encodeResult.queueOffsetKey, tranType); + encodeResult.setQueueOffsetKey(queueOffset, true); + BatchAppendEntryRequest request = new BatchAppendEntryRequest(); + request.setGroup(dLedgerConfig.getGroup()); + request.setRemoteId(dLedgerServer.getMemberState().getSelfId()); + request.setBatchMsgs(encodeResult.batchData); + dledgerFuture = (BatchAppendFuture) dLedgerServer.handleAppend(request); + if (dledgerFuture.getPos() == -1) { + log.warn("HandleAppend return false due to error code {}", dledgerFuture.get().getCode()); + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR))); + } + long wroteOffset = 0; + + int msgIdLength = (messageExtBatch.getSysFlag() & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 + 4 + 8 : 16 + 4 + 8; + ByteBuffer buffer = ByteBuffer.allocate(msgIdLength); + + boolean isFirstOffset = true; + long firstWroteOffset = 0; + for (long pos : dledgerFuture.getPositions()) { + wroteOffset = pos + DLedgerEntry.BODY_OFFSET; + if (isFirstOffset) { + firstWroteOffset = wroteOffset; + isFirstOffset = false; + } + String msgId = MessageDecoder.createMessageId(buffer, messageExtBatch.getStoreHostBytes(), wroteOffset); + if (msgIdBuilder.length() > 0) { + msgIdBuilder.append(',').append(msgId); + } else { + msgIdBuilder.append(msgId); + } + msgNum++; + } + + elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginTimeInDledgerLock; + appendResult = new AppendMessageResult(AppendMessageStatus.PUT_OK, firstWroteOffset, encodeResult.totalMsgLen, + msgIdBuilder.toString(), System.currentTimeMillis(), queueOffset, elapsedTimeInLock); + DLedgerCommitLog.this.topicQueueTable.put(encodeResult.queueOffsetKey, queueOffset + msgNum); + } catch (Exception e) { + log.error("Put message error", e); + return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR))); + } finally { + beginTimeInDledgerLock = 0; + putMessageLock.unlock(); + } + + if (elapsedTimeInLock > 500) { + log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", + elapsedTimeInLock, messageExtBatch.getBody().length, appendResult); + } + + return dledgerFuture.thenApply(appendEntryResponse -> { + PutMessageStatus putMessageStatus = PutMessageStatus.UNKNOWN_ERROR; + switch (DLedgerResponseCode.valueOf(appendEntryResponse.getCode())) { + case SUCCESS: + putMessageStatus = PutMessageStatus.PUT_OK; + break; + case INCONSISTENT_LEADER: + case NOT_LEADER: + case LEADER_NOT_READY: + case DISK_FULL: + putMessageStatus = PutMessageStatus.SERVICE_NOT_AVAILABLE; + break; + case WAIT_QUORUM_ACK_TIMEOUT: + //Do not return flush_slave_timeout to the client, for the ons client will ignore it. + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + case LEADER_PENDING_FULL: + putMessageStatus = PutMessageStatus.OS_PAGECACHE_BUSY; + break; + } + PutMessageResult putMessageResult = new PutMessageResult(putMessageStatus, appendResult); + if (putMessageStatus == PutMessageStatus.PUT_OK) { + // Statistics + storeStatsService.getSinglePutMessageTopicTimesTotal(messageExtBatch.getTopic()).incrementAndGet(); + storeStatsService.getSinglePutMessageTopicSizeTotal(messageExtBatch.getTopic()).addAndGet(appendResult.getWroteBytes()); + } + return putMessageResult; + }); } @Override @@ -566,51 +924,83 @@ public class DLedgerCommitLog extends CommitLog { return diff; } + private long getQueueOffsetByKey(String key, int tranType) { + Long queueOffset = DLedgerCommitLog.this.topicQueueTable.get(key); + if (null == queueOffset) { + queueOffset = 0L; + DLedgerCommitLog.this.topicQueueTable.put(key, queueOffset); + } + + // Transaction messages that require special handling + switch (tranType) { + // Prepared and Rollback message is not consumed, will not enter the + // consumer queuec + case MessageSysFlag.TRANSACTION_PREPARED_TYPE: + case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: + queueOffset = 0L; + break; + case MessageSysFlag.TRANSACTION_NOT_TYPE: + case MessageSysFlag.TRANSACTION_COMMIT_TYPE: + default: + break; + } + return queueOffset; + } + + class EncodeResult { private String queueOffsetKey; - private byte[] data; + private ByteBuffer data; + private List batchData; private AppendMessageStatus status; + private int totalMsgLen; - public EncodeResult(AppendMessageStatus status, byte[] data, String queueOffsetKey) { + public EncodeResult(AppendMessageStatus status, ByteBuffer data, String queueOffsetKey) { this.data = data; this.status = status; this.queueOffsetKey = queueOffsetKey; } + + public void setQueueOffsetKey(long offset, boolean isBatch) { + if (!isBatch) { + this.data.putLong(MessageDecoder.QUEUE_OFFSET_POSITION, offset); + return; + } + + for (byte[] data : batchData) { + ByteBuffer.wrap(data).putLong(MessageDecoder.QUEUE_OFFSET_POSITION, offset++); + } + } + + public byte[] getData() { + return data.array(); + } + + public EncodeResult(AppendMessageStatus status, String queueOffsetKey, List batchData, int totalMsgLen) { + this.batchData = batchData; + this.status = status; + this.queueOffsetKey = queueOffsetKey; + this.totalMsgLen = totalMsgLen; + } } class MessageSerializer { - // File at the end of the minimum fixed length empty - private static final int END_FILE_MIN_BLANK_LENGTH = 4 + 4; - private final ByteBuffer msgIdMemory; - private final ByteBuffer msgIdV6Memory; - // Store the message content - private final ByteBuffer msgStoreItemMemory; + // The maximum length of the message private final int maxMessageSize; - // Build Message Key - private final StringBuilder keyBuilder = new StringBuilder(); - - private final StringBuilder msgIdBuilder = new StringBuilder(); - -// private final ByteBuffer hostHolder = ByteBuffer.allocate(8); MessageSerializer(final int size) { - this.msgIdMemory = ByteBuffer.allocate(4 + 4 + 8); - this.msgIdV6Memory = ByteBuffer.allocate(16 + 4 + 8); - this.msgStoreItemMemory = ByteBuffer.allocate(size + END_FILE_MIN_BLANK_LENGTH); this.maxMessageSize = size; } - public ByteBuffer getMsgStoreItemMemory() { - return msgStoreItemMemory; - } - public EncodeResult serialize(final MessageExtBrokerInner msgInner) { // STORETIMESTAMP + STOREHOSTADDRESS + OFFSET
// PHY OFFSET long wroteOffset = 0; + long queueOffset = 0; + int sysflag = msgInner.getSysFlag(); int bornHostLength = (sysflag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 4 + 4 : 16 + 4; @@ -618,33 +1008,7 @@ public class DLedgerCommitLog extends CommitLog { ByteBuffer bornHostHolder = ByteBuffer.allocate(bornHostLength); ByteBuffer storeHostHolder = ByteBuffer.allocate(storeHostLength); - // Record ConsumeQueue information - keyBuilder.setLength(0); - keyBuilder.append(msgInner.getTopic()); - keyBuilder.append('-'); - keyBuilder.append(msgInner.getQueueId()); - String key = keyBuilder.toString(); - - Long queueOffset = DLedgerCommitLog.this.topicQueueTable.get(key); - if (null == queueOffset) { - queueOffset = 0L; - DLedgerCommitLog.this.topicQueueTable.put(key, queueOffset); - } - - // Transaction messages that require special handling - final int tranType = MessageSysFlag.getTransactionValue(msgInner.getSysFlag()); - switch (tranType) { - // Prepared and Rollback message is not consumed, will not enter the - // consumer queuec - case MessageSysFlag.TRANSACTION_PREPARED_TYPE: - case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: - queueOffset = 0L; - break; - case MessageSysFlag.TRANSACTION_NOT_TYPE: - case MessageSysFlag.TRANSACTION_COMMIT_TYPE: - default: - break; - } + String key = msgInner.getTopic() + "-" + msgInner.getQueueId(); /** * Serialize message @@ -666,6 +1030,8 @@ public class DLedgerCommitLog extends CommitLog { final int msgLen = calMsgLength(msgInner.getSysFlag(), bodyLength, topicLength, propertiesLength); + ByteBuffer msgStoreItemMemory = ByteBuffer.allocate(msgLen); + // Exceeds the maximum message if (msgLen > this.maxMessageSize) { DLedgerCommitLog.log.warn("message size exceeded, msg total size: " + msgLen + ", msg body size: " + bodyLength @@ -675,60 +1041,163 @@ public class DLedgerCommitLog extends CommitLog { // Initialization of storage space this.resetByteBuffer(msgStoreItemMemory, msgLen); // 1 TOTALSIZE - this.msgStoreItemMemory.putInt(msgLen); + msgStoreItemMemory.putInt(msgLen); // 2 MAGICCODE - this.msgStoreItemMemory.putInt(DLedgerCommitLog.MESSAGE_MAGIC_CODE); + msgStoreItemMemory.putInt(DLedgerCommitLog.MESSAGE_MAGIC_CODE); // 3 BODYCRC - this.msgStoreItemMemory.putInt(msgInner.getBodyCRC()); + msgStoreItemMemory.putInt(msgInner.getBodyCRC()); // 4 QUEUEID - this.msgStoreItemMemory.putInt(msgInner.getQueueId()); + msgStoreItemMemory.putInt(msgInner.getQueueId()); // 5 FLAG - this.msgStoreItemMemory.putInt(msgInner.getFlag()); + msgStoreItemMemory.putInt(msgInner.getFlag()); // 6 QUEUEOFFSET - this.msgStoreItemMemory.putLong(queueOffset); + msgStoreItemMemory.putLong(queueOffset); // 7 PHYSICALOFFSET - this.msgStoreItemMemory.putLong(wroteOffset); + msgStoreItemMemory.putLong(wroteOffset); // 8 SYSFLAG - this.msgStoreItemMemory.putInt(msgInner.getSysFlag()); + msgStoreItemMemory.putInt(msgInner.getSysFlag()); // 9 BORNTIMESTAMP - this.msgStoreItemMemory.putLong(msgInner.getBornTimestamp()); + msgStoreItemMemory.putLong(msgInner.getBornTimestamp()); // 10 BORNHOST - this.resetByteBuffer(bornHostHolder, bornHostLength); - this.msgStoreItemMemory.put(msgInner.getBornHostBytes(bornHostHolder)); + resetByteBuffer(bornHostHolder, bornHostLength); + msgStoreItemMemory.put(msgInner.getBornHostBytes(bornHostHolder)); // 11 STORETIMESTAMP - this.msgStoreItemMemory.putLong(msgInner.getStoreTimestamp()); + msgStoreItemMemory.putLong(msgInner.getStoreTimestamp()); // 12 STOREHOSTADDRESS - this.resetByteBuffer(storeHostHolder, storeHostLength); - this.msgStoreItemMemory.put(msgInner.getStoreHostBytes(storeHostHolder)); + resetByteBuffer(storeHostHolder, storeHostLength); + msgStoreItemMemory.put(msgInner.getStoreHostBytes(storeHostHolder)); //this.msgBatchMemory.put(msgInner.getStoreHostBytes()); // 13 RECONSUMETIMES - this.msgStoreItemMemory.putInt(msgInner.getReconsumeTimes()); + msgStoreItemMemory.putInt(msgInner.getReconsumeTimes()); // 14 Prepared Transaction Offset - this.msgStoreItemMemory.putLong(msgInner.getPreparedTransactionOffset()); + msgStoreItemMemory.putLong(msgInner.getPreparedTransactionOffset()); // 15 BODY - this.msgStoreItemMemory.putInt(bodyLength); + msgStoreItemMemory.putInt(bodyLength); if (bodyLength > 0) { - this.msgStoreItemMemory.put(msgInner.getBody()); + msgStoreItemMemory.put(msgInner.getBody()); } // 16 TOPIC - this.msgStoreItemMemory.put((byte) topicLength); - this.msgStoreItemMemory.put(topicData); + msgStoreItemMemory.put((byte) topicLength); + msgStoreItemMemory.put(topicData); // 17 PROPERTIES - this.msgStoreItemMemory.putShort((short) propertiesLength); + msgStoreItemMemory.putShort((short) propertiesLength); if (propertiesLength > 0) { - this.msgStoreItemMemory.put(propertiesData); + msgStoreItemMemory.put(propertiesData); } - byte[] data = new byte[msgLen]; - this.msgStoreItemMemory.clear(); - this.msgStoreItemMemory.get(data); - return new EncodeResult(AppendMessageStatus.PUT_OK, data, key); + return new EncodeResult(AppendMessageStatus.PUT_OK, msgStoreItemMemory, key); + } + + public EncodeResult serialize(final MessageExtBatch messageExtBatch) { + String key = messageExtBatch.getTopic() + "-" + messageExtBatch.getQueueId(); + + int totalMsgLen = 0; + ByteBuffer messagesByteBuff = messageExtBatch.wrap(); + List batchBody = new LinkedList<>(); + + int sysFlag = messageExtBatch.getSysFlag(); + int bornHostLength = (sysFlag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 4 + 4 : 16 + 4; + int storeHostLength = (sysFlag & MessageSysFlag.STOREHOSTADDRESS_V6_FLAG) == 0 ? 4 + 4 : 16 + 4; + ByteBuffer bornHostHolder = ByteBuffer.allocate(bornHostLength); + ByteBuffer storeHostHolder = ByteBuffer.allocate(storeHostLength); + + while (messagesByteBuff.hasRemaining()) { + // 1 TOTALSIZE + messagesByteBuff.getInt(); + // 2 MAGICCODE + messagesByteBuff.getInt(); + // 3 BODYCRC + messagesByteBuff.getInt(); + // 4 FLAG + int flag = messagesByteBuff.getInt(); + // 5 BODY + int bodyLen = messagesByteBuff.getInt(); + int bodyPos = messagesByteBuff.position(); + int bodyCrc = UtilAll.crc32(messagesByteBuff.array(), bodyPos, bodyLen); + messagesByteBuff.position(bodyPos + bodyLen); + // 6 properties + short propertiesLen = messagesByteBuff.getShort(); + int propertiesPos = messagesByteBuff.position(); + messagesByteBuff.position(propertiesPos + propertiesLen); + + final byte[] topicData = messageExtBatch.getTopic().getBytes(MessageDecoder.CHARSET_UTF8); + + final int topicLength = topicData.length; + + final int msgLen = calMsgLength(messageExtBatch.getSysFlag(), bodyLen, topicLength, propertiesLen); + ByteBuffer msgStoreItemMemory = ByteBuffer.allocate(msgLen); + + // Exceeds the maximum message + if (msgLen > this.maxMessageSize) { + CommitLog.log.warn("message size exceeded, msg total size: " + msgLen + ", msg body size: " + + bodyLen + + ", maxMessageSize: " + this.maxMessageSize); + throw new RuntimeException("message size exceeded"); + } + + totalMsgLen += msgLen; + // Determines whether there is sufficient free space + if (totalMsgLen > maxMessageSize) { + throw new RuntimeException("message size exceeded"); + } + + // Initialization of storage space + this.resetByteBuffer(msgStoreItemMemory, msgLen); + // 1 TOTALSIZE + msgStoreItemMemory.putInt(msgLen); + // 2 MAGICCODE + msgStoreItemMemory.putInt(DLedgerCommitLog.MESSAGE_MAGIC_CODE); + // 3 BODYCRC + msgStoreItemMemory.putInt(bodyCrc); + // 4 QUEUEID + msgStoreItemMemory.putInt(messageExtBatch.getQueueId()); + // 5 FLAG + msgStoreItemMemory.putInt(flag); + // 6 QUEUEOFFSET + msgStoreItemMemory.putLong(0L); + // 7 PHYSICALOFFSET + msgStoreItemMemory.putLong(0); + // 8 SYSFLAG + msgStoreItemMemory.putInt(messageExtBatch.getSysFlag()); + // 9 BORNTIMESTAMP + msgStoreItemMemory.putLong(messageExtBatch.getBornTimestamp()); + // 10 BORNHOST + resetByteBuffer(bornHostHolder, bornHostLength); + msgStoreItemMemory.put(messageExtBatch.getBornHostBytes(bornHostHolder)); + // 11 STORETIMESTAMP + msgStoreItemMemory.putLong(messageExtBatch.getStoreTimestamp()); + // 12 STOREHOSTADDRESS + resetByteBuffer(storeHostHolder, storeHostLength); + msgStoreItemMemory.put(messageExtBatch.getStoreHostBytes(storeHostHolder)); + // 13 RECONSUMETIMES + msgStoreItemMemory.putInt(messageExtBatch.getReconsumeTimes()); + // 14 Prepared Transaction Offset + msgStoreItemMemory.putLong(0); + // 15 BODY + msgStoreItemMemory.putInt(bodyLen); + if (bodyLen > 0) { + msgStoreItemMemory.put(messagesByteBuff.array(), bodyPos, bodyLen); + } + // 16 TOPIC + msgStoreItemMemory.put((byte) topicLength); + msgStoreItemMemory.put(topicData); + // 17 PROPERTIES + msgStoreItemMemory.putShort(propertiesLen); + if (propertiesLen > 0) { + msgStoreItemMemory.put(messagesByteBuff.array(), propertiesPos, propertiesLen); + } + byte[] data = new byte[msgLen]; + msgStoreItemMemory.clear(); + msgStoreItemMemory.get(data); + batchBody.add(data); + } + + return new EncodeResult(AppendMessageStatus.PUT_OK, key, batchBody, totalMsgLen); } private void resetByteBuffer(final ByteBuffer byteBuffer, final int limit) { byteBuffer.flip(); byteBuffer.limit(limit); } - } public static class DLedgerSelectMappedBufferResult extends SelectMappedBufferResult { @@ -740,6 +1209,7 @@ public class DLedgerCommitLog extends CommitLog { this.sbr = sbr; } + @Override public synchronized void release() { super.release(); if (sbr != null) { diff --git a/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java b/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java index a0e77fa3dc..34c51eb9e2 100644 --- a/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java +++ b/store/src/main/java/org/apache/rocketmq/store/ha/HAService.java @@ -590,6 +590,13 @@ public class HAService { log.info(this.getServiceName() + " service end"); } + + @Override + public void shutdown() { + super.shutdown(); + closeMaster(); + } + // private void disableWriteFlag() { // if (this.socketChannel != null) { // SelectionKey sk = this.socketChannel.keyFor(this.selector); diff --git a/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java b/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java index 8618dbb0e5..b3a7c196f9 100644 --- a/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java @@ -35,6 +35,7 @@ import java.io.File; import java.net.InetSocketAddress; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -80,6 +81,12 @@ public class BatchPutMessageTest { @Test public void testPutMessages() throws Exception { + String batchPropK = "extraKey"; + String batchPropV = "extraValue"; + Map batchProp = new HashMap<>(1); + batchProp.put(batchPropK, batchPropV); + short batchPropLen = (short) messageProperties2String(batchProp).getBytes(MessageDecoder.CHARSET_UTF8).length; + List messages = new ArrayList<>(); String topic = "batch-write-topic"; int queue = 0; @@ -98,7 +105,7 @@ public class BatchPutMessageTest { short propertiesLength = (short) propertiesBytes.length; final byte[] topicData = msg.getTopic().getBytes(MessageDecoder.CHARSET_UTF8); final int topicLength = topicData.length; - msgLengthArr[j] = calMsgLength(msg.getBody().length, topicLength, propertiesLength) + msgLengthArr[j - 1]; + msgLengthArr[j] = calMsgLength(msg.getBody().length, topicLength, propertiesLength+batchPropLen) + msgLengthArr[j - 1]; j++; } byte[] batchMessageBody = MessageDecoder.encodeMessages(messages); @@ -106,6 +113,7 @@ public class BatchPutMessageTest { messageExtBatch.setTopic(topic); messageExtBatch.setQueueId(queue); messageExtBatch.setBody(batchMessageBody); + messageExtBatch.putUserProperty(batchPropK,batchPropV); messageExtBatch.setBornTimestamp(System.currentTimeMillis()); messageExtBatch.setStoreHost(new InetSocketAddress("127.0.0.1", 125)); messageExtBatch.setBornHost(new InetSocketAddress("127.0.0.1", 126)); diff --git a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreShutDownTest.java b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreShutDownTest.java index e43bb4bc3d..db7d367e02 100644 --- a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreShutDownTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreShutDownTest.java @@ -40,10 +40,11 @@ public class DefaultMessageStoreShutDownTest { @Before public void init() throws Exception { - messageStore = spy(buildMessageStore()); - boolean load = messageStore.load(); + DefaultMessageStore store = buildMessageStore(); + boolean load = store.load(); assertTrue(load); - messageStore.start(); + store.start(); + messageStore = spy(store); when(messageStore.dispatchBehindBytes()).thenReturn(100L); } diff --git a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java index fd13e71a42..440f914631 100644 --- a/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/DefaultMessageStoreTest.java @@ -133,7 +133,7 @@ public class DefaultMessageStoreTest { StoreTestUtil.waitCommitLogReput((DefaultMessageStore) messageStore); for (long i = 0; i < totalMsgs; i++) { - GetMessageResult result = messageStore.getMessage("GROUP_A", "TOPIC_A", 0, i, 1024 * 1024, null); + GetMessageResult result = messageStore.getMessage("GROUP_A", "FooBar", 0, i, 1024 * 1024, null); assertThat(result).isNotNull(); result.release(); } @@ -471,7 +471,7 @@ public class DefaultMessageStoreTest { StoreTestUtil.waitCommitLogReput((DefaultMessageStore) messageStore); for (long i = 0; i < totalMsgs; i++) { - GetMessageResult result = master.getMessage("GROUP_A", "TOPIC_A", 0, i, 1024 * 1024, null); + GetMessageResult result = master.getMessage("GROUP_A", "FooBar", 0, i, 1024 * 1024, null); assertThat(result).isNotNull(); result.release(); diff --git a/store/src/test/java/org/apache/rocketmq/store/StoreTestBase.java b/store/src/test/java/org/apache/rocketmq/store/StoreTestBase.java index a736754de4..5660de1363 100644 --- a/store/src/test/java/org/apache/rocketmq/store/StoreTestBase.java +++ b/store/src/test/java/org/apache/rocketmq/store/StoreTestBase.java @@ -16,17 +16,19 @@ */ package org.apache.rocketmq.store; +import org.apache.rocketmq.common.UtilAll; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.common.message.MessageDecoder; +import org.apache.rocketmq.common.message.MessageExtBatch; +import org.junit.After; + import java.io.File; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.rocketmq.common.UtilAll; -import org.junit.After; public class StoreTestBase { @@ -44,6 +46,28 @@ public class StoreTestBase { return port.addAndGet(5); } + protected MessageExtBatch buildBatchMessage(int size) { + MessageExtBatch messageExtBatch = new MessageExtBatch(); + messageExtBatch.setTopic("StoreTest"); + messageExtBatch.setTags("TAG1"); + messageExtBatch.setKeys("Hello"); + messageExtBatch.setQueueId(Math.abs(QueueId.getAndIncrement()) % QUEUE_TOTAL); + messageExtBatch.setSysFlag(0); + + messageExtBatch.setBornTimestamp(System.currentTimeMillis()); + messageExtBatch.setBornHost(BornHost); + messageExtBatch.setStoreHost(StoreHost); + + List messageList = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + messageList.add(buildMessage()); + } + + messageExtBatch.setBody(MessageDecoder.encodeMessages(messageList)); + + return messageExtBatch; + } + protected MessageExtBrokerInner buildMessage() { MessageExtBrokerInner msg = new MessageExtBrokerInner(); msg.setTopic("StoreTest"); @@ -59,6 +83,40 @@ public class StoreTestBase { return msg; } + protected MessageExtBatch buildIPv6HostBatchMessage(int size) { + MessageExtBatch messageExtBatch = new MessageExtBatch(); + messageExtBatch.setTopic("StoreTest"); + messageExtBatch.setTags("TAG1"); + messageExtBatch.setKeys("Hello"); + messageExtBatch.setBody(MessageBody); + messageExtBatch.setMsgId("24084004018081003FAA1DDE2B3F898A00002A9F0000000000000CA0"); + messageExtBatch.setKeys(String.valueOf(System.currentTimeMillis())); + messageExtBatch.setQueueId(Math.abs(QueueId.getAndIncrement()) % QUEUE_TOTAL); + messageExtBatch.setSysFlag(0); + messageExtBatch.setBornHostV6Flag(); + messageExtBatch.setStoreHostAddressV6Flag(); + messageExtBatch.setBornTimestamp(System.currentTimeMillis()); + try { + messageExtBatch.setBornHost(new InetSocketAddress(InetAddress.getByName("1050:0000:0000:0000:0005:0600:300c:326b"), 8123)); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + + try { + messageExtBatch.setStoreHost(new InetSocketAddress(InetAddress.getByName("::1"), 8123)); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + + List messageList = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + messageList.add(buildIPv6HostMessage()); + } + + messageExtBatch.setBody(MessageDecoder.encodeMessages(messageList)); + return messageExtBatch; + } + protected MessageExtBrokerInner buildIPv6HostMessage() { MessageExtBrokerInner msg = new MessageExtBrokerInner(); msg.setTopic("StoreTest"); diff --git a/store/src/test/java/org/apache/rocketmq/store/dledger/DLedgerCommitlogTest.java b/store/src/test/java/org/apache/rocketmq/store/dledger/DLedgerCommitlogTest.java index f0b9205302..8ab8a23b47 100644 --- a/store/src/test/java/org/apache/rocketmq/store/dledger/DLedgerCommitlogTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/dledger/DLedgerCommitlogTest.java @@ -19,12 +19,17 @@ package org.apache.rocketmq.store.dledger; import io.openmessaging.storage.dledger.DLedgerServer; import io.openmessaging.storage.dledger.store.file.DLedgerMmapFileStore; import io.openmessaging.storage.dledger.store.file.MmapFileList; + import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.common.message.MessageExtBatch; import org.apache.rocketmq.store.DefaultMessageStore; import org.apache.rocketmq.store.GetMessageResult; import org.apache.rocketmq.store.GetMessageStatus; @@ -39,7 +44,7 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { @Test public void testTruncateCQ() throws Exception { - String base = createBaseDir(); + String base = createBaseDir(); String peers = String.format("n0-localhost:%d", nextPort()); String group = UUID.randomUUID().toString(); String topic = UUID.randomUUID().toString(); @@ -92,10 +97,9 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { } - @Test public void testRecover() throws Exception { - String base = createBaseDir(); + String base = createBaseDir(); String peers = String.format("n0-localhost:%d", nextPort()); String group = UUID.randomUUID().toString(); String topic = UUID.randomUUID().toString(); @@ -133,10 +137,9 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { } - @Test public void testPutAndGetMessage() throws Exception { - String base = createBaseDir(); + String base = createBaseDir(); String peers = String.format("n0-localhost:%d", nextPort()); String group = UUID.randomUUID().toString(); DefaultMessageStore messageStore = createDledgerMessageStore(base, group, "n0", peers, null, false, 0); @@ -146,7 +149,7 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { List results = new ArrayList<>(); for (int i = 0; i < 10; i++) { MessageExtBrokerInner msgInner = - i < 5 ? buildMessage() : buildIPv6HostMessage(); + i < 5 ? buildMessage() : buildIPv6HostMessage(); msgInner.setTopic(topic); msgInner.setQueueId(0); PutMessageResult putMessageResult = messageStore.putMessage(msgInner); @@ -158,7 +161,7 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { Assert.assertEquals(0, messageStore.getMinOffsetInQueue(topic, 0)); Assert.assertEquals(10, messageStore.getMaxOffsetInQueue(topic, 0)); Assert.assertEquals(0, messageStore.dispatchBehindBytes()); - GetMessageResult getMessageResult = messageStore.getMessage("group", topic, 0, 0, 32, null); + GetMessageResult getMessageResult = messageStore.getMessage("group", topic, 0, 0, 32, null); Assert.assertEquals(GetMessageStatus.FOUND, getMessageResult.getStatus()); Assert.assertEquals(10, getMessageResult.getMessageBufferList().size()); @@ -175,15 +178,146 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { messageStore.shutdown(); } + @Test + public void testBatchPutAndGetMessage() throws Exception { + String base = createBaseDir(); + String peers = String.format("n0-localhost:%d", nextPort()); + String group = UUID.randomUUID().toString(); + DefaultMessageStore messageStore = createDledgerMessageStore(base, group, "n0", peers, null, false, 0); + Thread.sleep(1000); + String topic = UUID.randomUUID().toString(); + // should be less than 4 + int batchMessageSize = 2; + int repeat = 10; + List results = new ArrayList<>(); + for (int i = 0; i < repeat; i++) { + MessageExtBatch messageExtBatch = + i < repeat / 10 ? buildBatchMessage(batchMessageSize) : buildIPv6HostBatchMessage(batchMessageSize); + messageExtBatch.setTopic(topic); + messageExtBatch.setQueueId(0); + PutMessageResult putMessageResult = messageStore.putMessages(messageExtBatch); + results.add(putMessageResult); + Assert.assertEquals(PutMessageStatus.PUT_OK, putMessageResult.getPutMessageStatus()); + Assert.assertEquals(i * batchMessageSize, putMessageResult.getAppendMessageResult().getLogicsOffset()); + } + Thread.sleep(100); + Assert.assertEquals(0, messageStore.getMinOffsetInQueue(topic, 0)); + Assert.assertEquals(repeat * batchMessageSize, messageStore.getMaxOffsetInQueue(topic, 0)); + Assert.assertEquals(0, messageStore.dispatchBehindBytes()); + GetMessageResult getMessageResult = messageStore.getMessage("group", topic, 0, 0, 100, null); + Assert.assertEquals(GetMessageStatus.FOUND, getMessageResult.getStatus()); + + Assert.assertEquals(repeat * batchMessageSize > 32 ? 32 : repeat * batchMessageSize, getMessageResult.getMessageBufferList().size()); + Assert.assertEquals(repeat * batchMessageSize > 32 ? 32 : repeat * batchMessageSize, getMessageResult.getMessageMapedList().size()); + Assert.assertEquals(repeat * batchMessageSize, getMessageResult.getMaxOffset()); + + for (int i = 0; i < results.size(); i++) { + ByteBuffer buffer = getMessageResult.getMessageBufferList().get(i * batchMessageSize); + MessageExt messageExt = MessageDecoder.decode(buffer); + Assert.assertEquals(i * batchMessageSize, messageExt.getQueueOffset()); + Assert.assertEquals(results.get(i).getAppendMessageResult().getMsgId().split(",").length, batchMessageSize); + Assert.assertEquals(results.get(i).getAppendMessageResult().getWroteOffset(), messageExt.getCommitLogOffset()); + } + messageStore.destroy(); + messageStore.shutdown(); + } + + @Test + public void testAsyncPutAndGetMessage() throws Exception { + String base = createBaseDir(); + String peers = String.format("n0-localhost:%d", nextPort()); + String group = UUID.randomUUID().toString(); + DefaultMessageStore messageStore = createDledgerMessageStore(base, group, "n0", peers, null, false, 0); + Thread.sleep(1000); + String topic = UUID.randomUUID().toString(); + + List results = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + MessageExtBrokerInner msgInner = + i < 5 ? buildMessage() : buildIPv6HostMessage(); + msgInner.setTopic(topic); + msgInner.setQueueId(0); + CompletableFuture futureResult = messageStore.asyncPutMessage(msgInner); + PutMessageResult putMessageResult = futureResult.get(3000, TimeUnit.MILLISECONDS); + results.add(putMessageResult); + Assert.assertEquals(PutMessageStatus.PUT_OK, putMessageResult.getPutMessageStatus()); + Assert.assertEquals(i, putMessageResult.getAppendMessageResult().getLogicsOffset()); + } + Thread.sleep(100); + Assert.assertEquals(0, messageStore.getMinOffsetInQueue(topic, 0)); + Assert.assertEquals(10, messageStore.getMaxOffsetInQueue(topic, 0)); + Assert.assertEquals(0, messageStore.dispatchBehindBytes()); + GetMessageResult getMessageResult = messageStore.getMessage("group", topic, 0, 0, 32, null); + Assert.assertEquals(GetMessageStatus.FOUND, getMessageResult.getStatus()); + + Assert.assertEquals(10, getMessageResult.getMessageBufferList().size()); + Assert.assertEquals(10, getMessageResult.getMessageMapedList().size()); + + for (int i = 0; i < results.size(); i++) { + ByteBuffer buffer = getMessageResult.getMessageBufferList().get(i); + MessageExt messageExt = MessageDecoder.decode(buffer); + Assert.assertEquals(i, messageExt.getQueueOffset()); + Assert.assertEquals(results.get(i).getAppendMessageResult().getMsgId(), messageExt.getMsgId()); + Assert.assertEquals(results.get(i).getAppendMessageResult().getWroteOffset(), messageExt.getCommitLogOffset()); + } + messageStore.destroy(); + messageStore.shutdown(); + } + + @Test + public void testAsyncBatchPutAndGetMessage() throws Exception { + String base = createBaseDir(); + String peers = String.format("n0-localhost:%d", nextPort()); + String group = UUID.randomUUID().toString(); + DefaultMessageStore messageStore = createDledgerMessageStore(base, group, "n0", peers, null, false, 0); + Thread.sleep(1000); + String topic = UUID.randomUUID().toString(); + // should be less than 4 + int batchMessageSize = 2; + int repeat = 10; + + List results = new ArrayList<>(); + for (int i = 0; i < repeat; i++) { + MessageExtBatch messageExtBatch = + i < 5 ? buildBatchMessage(batchMessageSize) : buildIPv6HostBatchMessage(batchMessageSize); + messageExtBatch.setTopic(topic); + messageExtBatch.setQueueId(0); + CompletableFuture futureResult = messageStore.asyncPutMessages(messageExtBatch); + PutMessageResult putMessageResult = futureResult.get(3000, TimeUnit.MILLISECONDS); + results.add(putMessageResult); + Assert.assertEquals(PutMessageStatus.PUT_OK, putMessageResult.getPutMessageStatus()); + Assert.assertEquals(i * batchMessageSize, putMessageResult.getAppendMessageResult().getLogicsOffset()); + } + Thread.sleep(100); + Assert.assertEquals(0, messageStore.getMinOffsetInQueue(topic, 0)); + Assert.assertEquals(repeat * batchMessageSize, messageStore.getMaxOffsetInQueue(topic, 0)); + Assert.assertEquals(0, messageStore.dispatchBehindBytes()); + GetMessageResult getMessageResult = messageStore.getMessage("group", topic, 0, 0, 32, null); + Assert.assertEquals(GetMessageStatus.FOUND, getMessageResult.getStatus()); + + Assert.assertEquals(repeat * batchMessageSize > 32 ? 32 : repeat * batchMessageSize, getMessageResult.getMessageBufferList().size()); + Assert.assertEquals(repeat * batchMessageSize > 32 ? 32 : repeat * batchMessageSize, getMessageResult.getMessageMapedList().size()); + Assert.assertEquals(repeat * batchMessageSize, getMessageResult.getMaxOffset()); + + for (int i = 0; i < results.size(); i++) { + ByteBuffer buffer = getMessageResult.getMessageBufferList().get(i * batchMessageSize); + MessageExt messageExt = MessageDecoder.decode(buffer); + Assert.assertEquals(i * batchMessageSize, messageExt.getQueueOffset()); + Assert.assertEquals(results.get(i).getAppendMessageResult().getMsgId().split(",").length, batchMessageSize); + Assert.assertEquals(results.get(i).getAppendMessageResult().getWroteOffset(), messageExt.getCommitLogOffset()); + } + messageStore.destroy(); + messageStore.shutdown(); + } @Test public void testCommittedPos() throws Exception { String peers = String.format("n0-localhost:%d;n1-localhost:%d", nextPort(), nextPort()); String group = UUID.randomUUID().toString(); - DefaultMessageStore leaderStore = createDledgerMessageStore(createBaseDir(), group,"n0", peers, "n0", false, 0); + DefaultMessageStore leaderStore = createDledgerMessageStore(createBaseDir(), group, "n0", peers, "n0", false, 0); String topic = UUID.randomUUID().toString(); - MessageExtBrokerInner msgInner = buildMessage(); + MessageExtBrokerInner msgInner = buildMessage(); msgInner.setTopic(topic); msgInner.setQueueId(0); PutMessageResult putMessageResult = leaderStore.putMessage(msgInner); @@ -195,7 +329,7 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { Assert.assertEquals(0, leaderStore.getMaxOffsetInQueue(topic, 0)); - DefaultMessageStore followerStore = createDledgerMessageStore(createBaseDir(), group,"n1", peers, "n0", false, 0); + DefaultMessageStore followerStore = createDledgerMessageStore(createBaseDir(), group, "n1", peers, "n0", false, 0); Thread.sleep(2000); Assert.assertEquals(1, leaderStore.getMaxOffsetInQueue(topic, 0)); @@ -214,10 +348,10 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { public void testIPv6HostMsgCommittedPos() throws Exception { String peers = String.format("n0-localhost:%d;n1-localhost:%d", nextPort(), nextPort()); String group = UUID.randomUUID().toString(); - DefaultMessageStore leaderStore = createDledgerMessageStore(createBaseDir(), group,"n0", peers, "n0", false, 0); + DefaultMessageStore leaderStore = createDledgerMessageStore(createBaseDir(), group, "n0", peers, "n0", false, 0); String topic = UUID.randomUUID().toString(); - MessageExtBrokerInner msgInner = buildIPv6HostMessage(); + MessageExtBrokerInner msgInner = buildIPv6HostMessage(); msgInner.setTopic(topic); msgInner.setQueueId(0); PutMessageResult putMessageResult = leaderStore.putMessage(msgInner); @@ -229,7 +363,7 @@ public class DLedgerCommitlogTest extends MessageStoreTestBase { Assert.assertEquals(0, leaderStore.getMaxOffsetInQueue(topic, 0)); - DefaultMessageStore followerStore = createDledgerMessageStore(createBaseDir(), group,"n1", peers, "n0", false, 0); + DefaultMessageStore followerStore = createDledgerMessageStore(createBaseDir(), group, "n1", peers, "n0", false, 0); Thread.sleep(2000); Assert.assertEquals(1, leaderStore.getMaxOffsetInQueue(topic, 0)); diff --git a/test/pom.xml b/test/pom.xml index 8fc5a71218..851d9ab9aa 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -20,7 +20,7 @@ rocketmq-all org.apache.rocketmq - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/test/src/main/java/org/apache/rocketmq/test/util/FileUtil.java b/test/src/main/java/org/apache/rocketmq/test/util/FileUtil.java index 44db782b5c..e08967eb91 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/FileUtil.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/FileUtil.java @@ -33,7 +33,7 @@ public class FileUtil { this.fileName = fileName; } - public static void main(String args[]) { + public static void main(String[] args) { String filePath = FileUtil.class.getResource("/").getPath(); String fileName = "test.txt"; FileUtil fileUtil = new FileUtil(filePath, fileName); diff --git a/test/src/main/java/org/apache/rocketmq/test/util/MQWait.java b/test/src/main/java/org/apache/rocketmq/test/util/MQWait.java index 6edeecadb2..0c24427d92 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/MQWait.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/MQWait.java @@ -76,7 +76,7 @@ public class MQWait { } } - public static void main(String args[]) { + public static void main(String[] args) { long start = System.currentTimeMillis(); MQWait.setCondition(new Condition() { diff --git a/test/src/main/java/org/apache/rocketmq/test/util/RandomUtil.java b/test/src/main/java/org/apache/rocketmq/test/util/RandomUtil.java index 1c2bdac305..41cacb684c 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/RandomUtil.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/RandomUtil.java @@ -100,14 +100,14 @@ public final class RandomUtil { return n + res % (m - n); } - private static char getChar(int arg[]) { + private static char getChar(int[] arg) { int size = arg.length; int c = rd.nextInt(size / 2); c = c * 2; return (char) (getIntegerBetween(arg[c], arg[c + 1])); } - private static String getString(int n, int arg[]) { + private static String getString(int n, int[] arg) { StringBuilder res = new StringBuilder(); for (int i = 0; i < n; i++) { res.append(getChar(arg)); @@ -116,17 +116,17 @@ public final class RandomUtil { } public static String getStringWithCharacter(int n) { - int arg[] = new int[] {'a', 'z' + 1, 'A', 'Z' + 1}; + int[] arg = new int[] {'a', 'z' + 1, 'A', 'Z' + 1}; return getString(n, arg); } public static String getStringWithNumber(int n) { - int arg[] = new int[] {'0', '9' + 1}; + int[] arg = new int[] {'0', '9' + 1}; return getString(n, arg); } public static String getStringWithNumAndCha(int n) { - int arg[] = new int[] {'a', 'z' + 1, 'A', 'Z' + 1, '0', '9' + 1}; + int[] arg = new int[] {'a', 'z' + 1, 'A', 'Z' + 1, '0', '9' + 1}; return getString(n, arg); } diff --git a/test/src/main/java/org/apache/rocketmq/test/util/RandomUtils.java b/test/src/main/java/org/apache/rocketmq/test/util/RandomUtils.java index 9eca28bbe7..3f71176d6f 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/RandomUtils.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/RandomUtils.java @@ -45,16 +45,16 @@ public class RandomUtils { } public static String getStringWithNumber(int n) { - int arg[] = new int[] {'0', '9' + 1}; + int[] arg = new int[] {'0', '9' + 1}; return getString(n, arg); } public static String getStringWithCharacter(int n) { - int arg[] = new int[] {'a', 'z' + 1, 'A', 'Z' + 1}; + int[] arg = new int[] {'a', 'z' + 1, 'A', 'Z' + 1}; return getString(n, arg); } - private static String getString(int n, int arg[]) { + private static String getString(int n, int[] arg) { StringBuilder res = new StringBuilder(); for (int i = 0; i < n; i++) { res.append(getChar(arg)); @@ -62,7 +62,7 @@ public class RandomUtils { return res.toString(); } - private static char getChar(int arg[]) { + private static char getChar(int[] arg) { int size = arg.length; int c = rd.nextInt(size / 2); c = c * 2; diff --git a/test/src/main/java/org/apache/rocketmq/test/util/VerifyUtils.java b/test/src/main/java/org/apache/rocketmq/test/util/VerifyUtils.java index 965d2ee670..69bfd8f7d1 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/VerifyUtils.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/VerifyUtils.java @@ -140,7 +140,7 @@ public class VerifyUtils { return rtExpect; } - public static void main(String args[]) { + public static void main(String[] args) { verifyBalance(400, 0.1f, 230, 190); } } diff --git a/test/src/main/java/org/apache/rocketmq/test/util/data/collect/impl/ListDataCollectorImpl.java b/test/src/main/java/org/apache/rocketmq/test/util/data/collect/impl/ListDataCollectorImpl.java index 82ab461aa5..bdd991a335 100644 --- a/test/src/main/java/org/apache/rocketmq/test/util/data/collect/impl/ListDataCollectorImpl.java +++ b/test/src/main/java/org/apache/rocketmq/test/util/data/collect/impl/ListDataCollectorImpl.java @@ -43,7 +43,7 @@ public class ListDataCollectorImpl implements DataCollector { return datas; } - public void resetData() { + public synchronized void resetData() { datas.clear(); unlockIncrement(); } @@ -67,7 +67,7 @@ public class ListDataCollectorImpl implements DataCollector { return Collections.frequency(datas, data) == 1; } - public Collection getAllDataWithoutDuplicate() { + public synchronized Collection getAllDataWithoutDuplicate() { return new HashSet(datas); } @@ -81,7 +81,7 @@ public class ListDataCollectorImpl implements DataCollector { return res; } - public void removeData(Object data) { + public synchronized void removeData(Object data) { datas.remove(data); } diff --git a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/MulTagSubIT.java b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/MulTagSubIT.java index 564da5ceaf..0edbdbe2e3 100644 --- a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/MulTagSubIT.java +++ b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/MulTagSubIT.java @@ -92,7 +92,7 @@ public class MulTagSubIT extends BaseConf { @Test public void testSubTwoTabAndMatchTwo() { - String tags[] = {"jueyin1", "jueyin2"}; + String[] tags = {"jueyin1", "jueyin2"}; String subExpress = String.format("%s||%s", tags[0], tags[1]); int msgSize = 10; @@ -113,7 +113,7 @@ public class MulTagSubIT extends BaseConf { @Test public void testSubThreeTabAndMatchTwo() { - String tags[] = {"jueyin1", "jueyin2", "jueyin3"}; + String[] tags = {"jueyin1", "jueyin2", "jueyin3"}; String subExpress = String.format("%s||%s", tags[0], tags[1]); int msgSize = 10; @@ -135,7 +135,7 @@ public class MulTagSubIT extends BaseConf { @Test public void testNoMatch() { - String tags[] = {"jueyin1", "jueyin2", "jueyin3"}; + String[] tags = {"jueyin1", "jueyin2", "jueyin3"}; String subExpress = "no_match"; int msgSize = 10; diff --git a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithMulConsumerIT.java b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithMulConsumerIT.java index 31321975b5..8de1b7d4e0 100644 --- a/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithMulConsumerIT.java +++ b/test/src/test/java/org/apache/rocketmq/test/client/consumer/tag/TagMessageWithMulConsumerIT.java @@ -84,7 +84,7 @@ public class TagMessageWithMulConsumerIT extends BaseConf { @Test public void testSendMessagesWithTwoTag() { - String tags[] = {"jueyin1", "jueyin2"}; + String[] tags = {"jueyin1", "jueyin2"}; int msgSize = 10; TagMessage tagMessage = new TagMessage(tags, topic, msgSize); @@ -113,7 +113,7 @@ public class TagMessageWithMulConsumerIT extends BaseConf { @Test public void testTwoConsumerOneMatchOneOtherMatchAll() { - String tags[] = {"jueyin1", "jueyin2"}; + String[] tags = {"jueyin1", "jueyin2"}; String sub1 = String.format("%s||%s", tags[0], tags[1]); String sub2 = String.format("%s|| noExist", tags[0]); int msgSize = 10; @@ -144,7 +144,7 @@ public class TagMessageWithMulConsumerIT extends BaseConf { @Test public void testSubKindsOf() { - String tags[] = {"jueyin1", "jueyin2"}; + String[] tags = {"jueyin1", "jueyin2"}; String sub1 = String.format("%s||%s", tags[0], tags[1]); String sub2 = String.format("%s|| noExist", tags[0]); String sub3 = tags[0]; diff --git a/test/src/test/resources/log4j.xml b/test/src/test/resources/log4j.xml index 3031095e70..7840ab78c1 100644 --- a/test/src/test/resources/log4j.xml +++ b/test/src/test/resources/log4j.xml @@ -30,7 +30,7 @@ - + diff --git a/tools/pom.xml b/tools/pom.xml index 4c1b647577..eeccae81bf 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -19,7 +19,7 @@ org.apache.rocketmq rocketmq-all - 4.8.0-SNAPSHOT + 4.8.1-SNAPSHOT 4.0.0 diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java index e80a813eca..8b1c228f4f 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExt.java @@ -320,6 +320,13 @@ public class DefaultMQAdminExt extends ClientConfig implements MQAdminExt { defaultMQAdminExtImpl.deleteSubscriptionGroup(addr, groupName); } + @Override + public void deleteSubscriptionGroup(String addr, + String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, InterruptedException, + MQClientException { + defaultMQAdminExtImpl.deleteSubscriptionGroup(addr, groupName, removeOffset); + } + @Override public void createAndUpdateKvConfig(String namespace, String key, String value) throws RemotingException, MQBrokerException, diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java index 22d4005ce9..5c343708b3 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/DefaultMQAdminExtImpl.java @@ -424,7 +424,14 @@ public class DefaultMQAdminExtImpl implements MQAdminExt, MQAdminExtInner { public void deleteSubscriptionGroup(String addr, String groupName) throws RemotingException, MQBrokerException, InterruptedException, MQClientException { - this.mqClientInstance.getMQClientAPIImpl().deleteSubscriptionGroup(addr, groupName, timeoutMillis); + this.mqClientInstance.getMQClientAPIImpl().deleteSubscriptionGroup(addr, groupName, false, timeoutMillis); + } + + @Override + public void deleteSubscriptionGroup(String addr, + String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, InterruptedException, + MQClientException { + this.mqClientInstance.getMQClientAPIImpl().deleteSubscriptionGroup(addr, groupName, removeOffset, timeoutMillis); } @Override diff --git a/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java b/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java index 17b62251c6..d5462cb04e 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/admin/MQAdminExt.java @@ -152,6 +152,9 @@ public interface MQAdminExt extends MQAdmin { void deleteSubscriptionGroup(final String addr, String groupName) throws RemotingException, MQBrokerException, InterruptedException, MQClientException; + void deleteSubscriptionGroup(final String addr, String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, + InterruptedException, MQClientException; + void createAndUpdateKvConfig(String namespace, String key, String value) throws RemotingException, MQBrokerException, InterruptedException, MQClientException; diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java index 5038123561..872a130d9f 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/cluster/CLusterSendMsgRTCommand.java @@ -38,7 +38,7 @@ import org.apache.rocketmq.tools.command.SubCommandException; public class CLusterSendMsgRTCommand implements SubCommand { - public static void main(String args[]) { + public static void main(String[] args) { } @Override diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java index b946ee141e..7985f9d591 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerProgressSubCommand.java @@ -111,17 +111,26 @@ public class ConsumerProgressSubCommand implements SubCommand { if (showClientIP) { messageQueueAllocationResult = getMessageQueueAllocationResult(defaultMQAdminExt, consumerGroup); } - - System.out.printf("%-32s %-32s %-4s %-20s %-20s %-20s %-20s %s%n", - "#Topic", - "#Broker Name", - "#QID", - "#Broker Offset", - "#Consumer Offset", - "#Client IP", - "#Diff", - "#LastTime"); - + if (showClientIP) { + System.out.printf("%-32s %-32s %-4s %-20s %-20s %-20s %-20s %s%n", + "#Topic", + "#Broker Name", + "#QID", + "#Broker Offset", + "#Consumer Offset", + "#Client IP", + "#Diff", + "#LastTime"); + } else { + System.out.printf("%-32s %-32s %-4s %-20s %-20s %-20s %s%n", + "#Topic", + "#Broker Name", + "#QID", + "#Broker Offset", + "#Consumer Offset", + "#Diff", + "#LastTime"); + } long diffTotal = 0L; for (MessageQueue mq : mqList) { OffsetWrapper offsetWrapper = consumeStats.getOffsetTable().get(mq); @@ -141,17 +150,28 @@ public class ConsumerProgressSubCommand implements SubCommand { if (showClientIP) { clientIP = messageQueueAllocationResult.get(mq); } - - System.out.printf("%-32s %-32s %-4d %-20d %-20d %-20s %-20d %s%n", - UtilAll.frontStringAtLeast(mq.getTopic(), 32), - UtilAll.frontStringAtLeast(mq.getBrokerName(), 32), - mq.getQueueId(), - offsetWrapper.getBrokerOffset(), - offsetWrapper.getConsumerOffset(), - null != clientIP ? clientIP : "N/A", - diff, - lastTime - ); + if (showClientIP) { + System.out.printf("%-32s %-32s %-4d %-20d %-20d %-20s %-20d %s%n", + UtilAll.frontStringAtLeast(mq.getTopic(), 32), + UtilAll.frontStringAtLeast(mq.getBrokerName(), 32), + mq.getQueueId(), + offsetWrapper.getBrokerOffset(), + offsetWrapper.getConsumerOffset(), + null != clientIP ? clientIP : "N/A", + diff, + lastTime + ); + } else { + System.out.printf("%-32s %-32s %-4d %-20d %-20d %-20d %s%n", + UtilAll.frontStringAtLeast(mq.getTopic(), 32), + UtilAll.frontStringAtLeast(mq.getBrokerName(), 32), + mq.getQueueId(), + offsetWrapper.getBrokerOffset(), + offsetWrapper.getConsumerOffset(), + diff, + lastTime + ); + } } System.out.printf("%n"); diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java index 96d81956e4..fb0efebaa2 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/consumer/DeleteSubscriptionGroupCommand.java @@ -54,6 +54,10 @@ public class DeleteSubscriptionGroupCommand implements SubCommand { opt.setRequired(true); options.addOption(opt); + opt = new Option("r", "removeOffset", true, "remove offset"); + opt.setRequired(false); + options.addOption(opt); + return options; } @@ -65,11 +69,16 @@ public class DeleteSubscriptionGroupCommand implements SubCommand { // groupName String groupName = commandLine.getOptionValue('g').trim(); + boolean removeOffset = false; + if (commandLine.hasOption('r')) { + removeOffset = Boolean.valueOf(commandLine.getOptionValue("r").trim()); + } + if (commandLine.hasOption('b')) { String addr = commandLine.getOptionValue('b').trim(); adminExt.start(); - adminExt.deleteSubscriptionGroup(addr, groupName); + adminExt.deleteSubscriptionGroup(addr, groupName, removeOffset); System.out.printf("delete subscription group [%s] from broker [%s] success.%n", groupName, addr); @@ -80,7 +89,7 @@ public class DeleteSubscriptionGroupCommand implements SubCommand { Set masterSet = CommandUtil.fetchMasterAddrByClusterName(adminExt, clusterName); for (String master : masterSet) { - adminExt.deleteSubscriptionGroup(master, groupName); + adminExt.deleteSubscriptionGroup(master, groupName, removeOffset); System.out.printf( "delete subscription group [%s] from broker [%s] in cluster [%s] success.%n", groupName, master, clusterName); diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java index c770db01d4..a78a4a63ab 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/TopicRouteSubCommand.java @@ -19,14 +19,23 @@ package org.apache.rocketmq.tools.command.topic; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; +import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.QueueData; import org.apache.rocketmq.common.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.tools.admin.DefaultMQAdminExt; import org.apache.rocketmq.tools.command.SubCommand; import org.apache.rocketmq.tools.command.SubCommandException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class TopicRouteSubCommand implements SubCommand { + private static final String FORMAT = "%-45s %-32s %-50s %-10s %-11s %-5s%n"; + @Override public String commandName() { return "topicRoute"; @@ -43,6 +52,9 @@ public class TopicRouteSubCommand implements SubCommand { opt.setRequired(true); options.addOption(opt); + opt = new Option("l", "list", false, "Use list format to print data"); + opt.setRequired(false); + options.addOption(opt); return options; } @@ -58,12 +70,46 @@ public class TopicRouteSubCommand implements SubCommand { String topic = commandLine.getOptionValue('t').trim(); TopicRouteData topicRouteData = defaultMQAdminExt.examineTopicRouteInfo(topic); - String json = topicRouteData.toJson(true); - System.out.printf("%s%n", json); + printData(topicRouteData, commandLine.hasOption('l')); } catch (Exception e) { throw new SubCommandException(this.getClass().getSimpleName() + " command failed", e); } finally { defaultMQAdminExt.shutdown(); } } -} + + private void printData(TopicRouteData topicRouteData, boolean useListFormat) { + if (!useListFormat) { + System.out.printf("%s%n", topicRouteData.toJson(true)); + return; + } + + int totalReadQueue = 0, totalWriteQueue = 0; + List queueDataList = topicRouteData.getQueueDatas(); + Map map = new HashMap<>(); + for (QueueData queueData : queueDataList) { + map.put(queueData.getBrokerName(), queueData); + } + queueDataList.sort(Comparator.comparing(QueueData::getBrokerName)); + + List brokerDataList = topicRouteData.getBrokerDatas(); + brokerDataList.sort(Comparator.comparing(BrokerData::getBrokerName)); + + System.out.printf(FORMAT, "#ClusterName", "#BrokerName", "#BrokerAddrs", "#ReadQueue", "#WriteQueue", "#Perm"); + + for (BrokerData brokerData : brokerDataList) { + String brokerName = brokerData.getBrokerName(); + QueueData queueData = map.get(brokerName); + totalReadQueue += queueData.getReadQueueNums(); + totalWriteQueue += queueData.getWriteQueueNums(); + System.out.printf(FORMAT, brokerData.getCluster(), brokerName, brokerData.getBrokerAddrs(), + queueData.getReadQueueNums(), queueData.getWriteQueueNums(), queueData.getPerm()); + } + + for (int i = 0; i < 158; i++) { + System.out.print("-"); + } + System.out.printf("%n"); + System.out.printf(FORMAT, "Total:", map.keySet().size(), "", totalReadQueue, totalWriteQueue, ""); + } +} \ No newline at end of file diff --git a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java index a06a19d1fd..63a2a19275 100644 --- a/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java +++ b/tools/src/main/java/org/apache/rocketmq/tools/command/topic/UpdateTopicPermSubCommand.java @@ -94,7 +94,7 @@ public class UpdateTopicPermSubCommand implements SubCommand { topicConfig.setTopicName(topic); topicConfig.setWriteQueueNums(queueData.getWriteQueueNums()); topicConfig.setReadQueueNums(queueData.getReadQueueNums()); - topicConfig.setTopicSysFlag(queueData.getTopicSynFlag()); + topicConfig.setTopicSysFlag(queueData.getTopicSysFlag()); //new perm int perm; if (commandLine.hasOption('p')) {