You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by di...@apache.org on 2022/03/14 06:05:19 UTC

[rocketmq] branch 5.0.0-beta-tmp updated: feature(namesrv&common):[RIP-29]Optimize RocketMQ NameServer (#3974)

This is an automated email from the ASF dual-hosted git repository.

dinglei pushed a commit to branch 5.0.0-beta-tmp
in repository https://gitbox.apache.org/repos/asf/rocketmq.git


The following commit(s) were added to refs/heads/5.0.0-beta-tmp by this push:
     new ecc9726  feature(namesrv&common):[RIP-29]Optimize RocketMQ NameServer (#3974)
ecc9726 is described below

commit ecc972674f7284191b5dae0e8cc6647476fd0401
Author: rongtong <ji...@163.com>
AuthorDate: Mon Mar 14 14:05:05 2022 +0800

    feature(namesrv&common):[RIP-29]Optimize RocketMQ NameServer (#3974)
    
    Co-authored-by: rongtong.jrt <ro...@alibaba-inc.com>
---
 ...KeyBuilder.java => AbstractBrokerRunnable.java} |   31 +-
 .../org/apache/rocketmq/common/BrokerConfig.java   |  404 +++++--
 .../org/apache/rocketmq/common/BrokerIdentity.java |  149 +++
 .../org/apache/rocketmq/common/BrokerSyncInfo.java |   70 ++
 .../org/apache/rocketmq/common/Configuration.java  |   41 +
 .../org/apache/rocketmq/common/DataVersion.java    |   49 +-
 .../org/apache/rocketmq/common/KeyBuilder.java     |    4 +
 .../MessageType.java => LockCallback.java}         |   13 +-
 .../java/org/apache/rocketmq/common/MixAll.java    |    4 +
 .../apache/rocketmq/common/PopAckConstants.java    |    9 +
 .../org/apache/rocketmq/common/ServiceThread.java  |    8 +-
 .../apache/rocketmq/common/ThreadFactoryImpl.java  |   13 +
 .../org/apache/rocketmq/common/TopicConfig.java    |   61 +-
 .../MessageType.java => UnlockCallback.java}       |   10 +-
 .../java/org/apache/rocketmq/common/UtilAll.java   |  108 +-
 .../rocketmq/common/constant/LoggerName.java       |    7 +-
 .../apache/rocketmq/common/constant/PermName.java  |   18 +-
 .../MessageType.java => future/FutureTaskExt.java} |   26 +-
 .../rocketmq/common/message/MessageConst.java      |   26 +-
 .../rocketmq/common/message/MessageDecoder.java    |  156 ++-
 .../apache/rocketmq/common/message/MessageExt.java |   55 +
 .../rocketmq/common/message/MessageExtBatch.java   |   51 +
 .../common/message/MessageExtBrokerInner.java      |   63 ++
 .../rocketmq/common/message/MessageType.java       |   28 +-
 .../rocketmq/common/message/MessageVersion.java    |   70 ++
 .../common/namesrv/DefaultTopAddressing.java       |  165 +++
 .../NameServerUpdateCallback.java}                 |   10 +-
 .../rocketmq/common/namesrv/NamesrvConfig.java     |  117 +++
 .../rocketmq/common/namesrv/TopAddressing.java     |   88 +-
 ...amesrvRequestHeader.java => ForbiddenType.java} |   44 +-
 .../rocketmq/common/protocol/RequestCode.java      |   34 +
 .../rocketmq/common/protocol/ResponseCode.java     |    7 +-
 .../common/protocol/body/BrokerMemberGroup.java    |   90 ++
 .../protocol/body/CheckClientRequestBody.java      |    9 +
 .../rocketmq/common/protocol/body/ClusterInfo.java |   14 +-
 .../body/ConsumerOffsetSerializeWrapper.java       |   10 +
 .../common/protocol/body/ConsumerRunningInfo.java  |   31 +-
 ....java => GetBrokerMemberGroupResponseBody.java} |   16 +-
 .../body/GetRemoteClientConfigBody.java}           |   21 +-
 .../common/protocol/body/HARuntimeInfo.java        |  188 ++++
 .../common/protocol/body/LockBatchRequestBody.java |    9 +
 ...ody.java => QuerySubscriptionResponseBody.java} |   22 +-
 .../protocol/body/UnlockBatchRequestBody.java      |    9 +
 ...uestHeader.java => AddBrokerRequestHeader.java} |   24 +-
 .../ConsumeMessageDirectlyResultRequestHeader.java |   30 +
 .../DeleteSubscriptionGroupRequestHeader.java      |   10 +-
 ...eader.java => ExchangeHAInfoRequestHeader.java} |   44 +-
 ...ader.java => ExchangeHAInfoResponseHeader.java} |   44 +-
 ...java => GetBrokerMemberGroupRequestHeader.java} |   28 +-
 .../protocol/header/GetMaxOffsetRequestHeader.java |   17 +
 ...> GetSubscriptionGroupConfigRequestHeader.java} |   32 +-
 ...r.java => InitConsumerOffsetRequestHeader.java} |   22 +-
 ...tHeader.java => NotificationRequestHeader.java} |   56 +-
 ...Header.java => NotificationResponseHeader.java} |   20 +-
 ...a => NotifyMinBrokerIdChangeRequestHeader.java} |   52 +-
 ...stHeader.java => PeekMessageRequestHeader.java} |   42 +-
 ...stHeader.java => PollingInfoRequestHeader.java} |   31 +-
 ...tHeader.java => PollingInfoResponseHeader.java} |   21 +-
 .../protocol/header/PullMessageResponseHeader.java |   33 +
 ... QuerySubscriptionByConsumerRequestHeader.java} |   16 +-
 ...ava => QueryTopicsByConsumerRequestHeader.java} |   17 +-
 ...tHeader.java => RemoveBrokerRequestHeader.java} |   39 +-
 ...ader.java => ResetMasterFlushOffsetHeader.java} |   16 +-
 ...r.java => StatisticsMessagesRequestHeader.java} |   53 +-
 ...java => UpdateGroupForbiddenRequestHeader.java} |   33 +-
 ...ader.java => BrokerHeartbeatRequestHeader.java} |   58 +-
 .../DeleteTopicFromNamesrvRequestHeader.java       |   10 +
 .../header/namesrv/GetRouteInfoRequestHeader.java  |    2 +
 .../namesrv/RegisterBrokerRequestHeader.java       |   22 +
 ...Header.java => RegisterTopicRequestHeader.java} |    2 +-
 .../rocketmq/common/protocol/route/BrokerData.java |   52 +-
 .../rocketmq/common/protocol/route/QueueData.java  |   13 +
 .../common/protocol/route/TopicRouteData.java      |   53 +
 .../route/TopicRouteDatas.java}                    |   23 +-
 .../rocketmq/common/statistics/FutureHolder.java   |   53 +
 .../Interceptor.java}                              |   18 +-
 .../common/statistics/StatisticsBrief.java         |  184 ++++
 .../statistics/StatisticsBriefInterceptor.java     |   76 ++
 .../rocketmq/common/statistics/StatisticsItem.java |  175 ++++
 .../StatisticsItemFormatter.java}                  |   31 +-
 .../common/statistics/StatisticsItemPrinter.java   |   50 +
 .../StatisticsItemScheduledIncrementPrinter.java   |  290 ++++++
 .../statistics/StatisticsItemScheduledPrinter.java |   97 ++
 .../StatisticsItemStateGetter.java}                |   10 +-
 .../StatisticsKindMeta.java}                       |   38 +-
 .../common/statistics/StatisticsManager.java       |  157 +++
 .../common/subscription/GroupForbidden.java        |   86 ++
 .../subscription/SubscriptionGroupConfig.java      |   47 +-
 .../rocketmq/common/topic/TopicValidator.java      |    2 +
 .../apache/rocketmq/common/utils/MessageUtils.java |   49 +
 .../PositiveAtomicCounter.java}                    |   27 +-
 .../rocketmq/common/utils/ServiceProvider.java     |  211 ++++
 .../apache/rocketmq/common/utils/ThreadUtils.java  |   17 +-
 .../apache/rocketmq/common/DataVersionTest.java    |    7 +
 .../rocketmq/common/RegisterBrokerBodyTest.java    |    2 +-
 .../org/apache/rocketmq/common/UtilAllTest.java    |   21 +-
 .../common/message/MessageDecoderTest.java         |    9 +-
 .../rocketmq/common/message/MessageTest.java       |    1 -
 .../rocketmq/common/protocol/body/KVTableTest.java |    1 -
 .../common/statictopic/TopicQueueMappingTest.java  |    2 -
 namesrv/pom.xml                                    |   15 +-
 .../apache/rocketmq/namesrv/NamesrvController.java |  156 ++-
 .../apache/rocketmq/namesrv/NamesrvStartup.java    |   11 +-
 .../namesrv/processor/ClientRequestProcessor.java  |  106 ++
 .../processor/ClusterTestRequestProcessor.java     |    2 +-
 .../namesrv/processor/DefaultRequestProcessor.java |  287 ++---
 .../namesrv/routeinfo/BatchUnRegisterService.java  |   85 ++
 .../routeinfo/BrokerHousekeepingService.java       |    6 +-
 .../namesrv/routeinfo/RouteInfoManager.java        | 1099 ++++++++++++++------
 .../namesrv/processor/RequestProcessorTest.java    |  563 ++++++++++
 .../namesrv/routeinfo/GetRouteInfoBenchmark.java   |  148 +++
 .../namesrv/routeinfo/RegisterBrokerBenchmark.java |  177 ++++
 .../namesrv/routeinfo/RouteInfoManagerTest.java    |   85 +-
 .../routeinfo/RouteInfoManager_NewTest.java        |  783 ++++++++++++++
 114 files changed, 7214 insertions(+), 1243 deletions(-)

diff --git a/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java b/common/src/main/java/org/apache/rocketmq/common/AbstractBrokerRunnable.java
similarity index 56%
copy from common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java
copy to common/src/main/java/org/apache/rocketmq/common/AbstractBrokerRunnable.java
index d30789f..89b780a 100644
--- a/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java
+++ b/common/src/main/java/org/apache/rocketmq/common/AbstractBrokerRunnable.java
@@ -14,24 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.rocketmq.common;
 
-public class KeyBuilder {
-    public static final int POP_ORDER_REVIVE_QUEUE = 999;
+import org.apache.rocketmq.logging.InnerLoggerFactory;
 
-    public static String buildPopRetryTopic(String topic, String cid) {
-        return MixAll.RETRY_GROUP_TOPIC_PREFIX + cid + "_" + topic;
-    }
+public abstract class AbstractBrokerRunnable implements Runnable {
+    protected final BrokerIdentity brokerIdentity;
 
-    public static String parseNormalTopic(String topic, String cid) {
-        if (topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)) {
-            return topic.substring((MixAll.RETRY_GROUP_TOPIC_PREFIX + cid + "_").length());
-        } else {
-            return topic;
-        }
+    public AbstractBrokerRunnable(BrokerIdentity brokerIdentity) {
+        this.brokerIdentity = brokerIdentity;
     }
 
-    public static String buildPollingKey(String topic, String cid, int queueId) {
-        return topic + PopAckConstants.SPLIT + cid + PopAckConstants.SPLIT + queueId;
+    /**
+     * real logic for running
+     */
+    public abstract void run2();
+
+    @Override
+    public void run() {
+        if (brokerIdentity.isInBrokerContainer()) {
+            // set threadlocal broker identity to forward logging to corresponding broker
+            InnerLoggerFactory.brokerIdentity.set(brokerIdentity.getCanonicalName());
+        }
+        run2();
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
index 66fd449..efd4ec9 100644
--- a/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
+++ b/common/src/main/java/org/apache/rocketmq/common/BrokerConfig.java
@@ -16,8 +16,6 @@
  */
 package org.apache.rocketmq.common;
 
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import org.apache.rocketmq.common.annotation.ImportantField;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.constant.PermName;
@@ -27,21 +25,25 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.common.RemotingUtil;
 
-public class BrokerConfig {
+public class BrokerConfig extends BrokerIdentity {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
 
+    private String brokerConfigPath = null;
+
     private String rocketmqHome = System.getProperty(MixAll.ROCKETMQ_HOME_PROPERTY, System.getenv(MixAll.ROCKETMQ_HOME_ENV));
     @ImportantField
     private String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV));
+
+    /**
+     * Listen port for single broker
+     */
+    @ImportantField
+    private int listenPort = 6888;
+
     @ImportantField
     private String brokerIP1 = RemotingUtil.getLocalAddress();
     private String brokerIP2 = RemotingUtil.getLocalAddress();
-    @ImportantField
-    private String brokerName = localHostName();
-    @ImportantField
-    private String brokerClusterName = "DefaultCluster";
-    @ImportantField
-    private long brokerId = MixAll.MASTER_ID;
+
     private int brokerPermission = PermName.PERM_READ | PermName.PERM_WRITE;
     private int defaultTopicQueueNums = 8;
     @ImportantField
@@ -53,6 +55,8 @@ public class BrokerConfig {
     @ImportantField
     private boolean autoCreateSubscriptionGroup = true;
     private String messageStorePlugIn = "";
+
+    private static final int PROCESSOR_NUMBER = Runtime.getRuntime().availableProcessors();
     @ImportantField
     private String msgTraceTopicName = TopicValidator.RMQ_SYS_TRACE_TOPIC;
     @ImportantField
@@ -60,24 +64,25 @@ public class BrokerConfig {
     /**
      * thread numbers for send message thread pool.
      */
-    private int sendMessageThreadPoolNums = Math.min(Runtime.getRuntime().availableProcessors(), 4);
-    private int putMessageFutureThreadPoolNums = Math.min(Runtime.getRuntime().availableProcessors(), 4);
-    private int pullMessageThreadPoolNums = 16 + Runtime.getRuntime().availableProcessors() * 2;
+    private int sendMessageThreadPoolNums = Math.min(PROCESSOR_NUMBER, 4);
+    private int putMessageFutureThreadPoolNums = Math.min(PROCESSOR_NUMBER, 4);
+    private int pullMessageThreadPoolNums = 16 + PROCESSOR_NUMBER * 2;
+    private int litePullMessageThreadPoolNums = 16 + PROCESSOR_NUMBER * 2;
     private int ackMessageThreadPoolNums = 3;
-    private int processReplyMessageThreadPoolNums = 16 + Runtime.getRuntime().availableProcessors() * 2;
-    private int queryMessageThreadPoolNums = 8 + Runtime.getRuntime().availableProcessors();
+    private int processReplyMessageThreadPoolNums = 16 + PROCESSOR_NUMBER * 2;
+    private int queryMessageThreadPoolNums = 8 + PROCESSOR_NUMBER;
 
     private int adminBrokerThreadPoolNums = 16;
     private int clientManageThreadPoolNums = 32;
     private int consumerManageThreadPoolNums = 32;
     private int loadBalanceProcessorThreadPoolNums = 32;
-    private int heartbeatThreadPoolNums = Math.min(32, Runtime.getRuntime().availableProcessors());
+    private int heartbeatThreadPoolNums = Math.min(32, PROCESSOR_NUMBER);
 
     /**
      * Thread numbers for EndTransactionProcessor
      */
-    private int endTransactionThreadPoolNums = Math.max(8 + Runtime.getRuntime().availableProcessors() * 2,
-        sendMessageThreadPoolNums * 4);
+    private int endTransactionThreadPoolNums = Math.max(8 + PROCESSOR_NUMBER * 2,
+            sendMessageThreadPoolNums * 4);
 
     private int flushConsumerOffsetInterval = 1000 * 5;
 
@@ -90,6 +95,7 @@ public class BrokerConfig {
     private int sendThreadPoolQueueCapacity = 10000;
     private int putThreadPoolQueueCapacity = 10000;
     private int pullThreadPoolQueueCapacity = 100000;
+    private int litePullThreadPoolQueueCapacity = 100000;
     private int ackThreadPoolQueueCapacity = 100000;
     private int replyThreadPoolQueueCapacity = 10000;
     private int queryThreadPoolQueueCapacity = 20000;
@@ -97,6 +103,8 @@ public class BrokerConfig {
     private int consumerManagerThreadPoolQueueCapacity = 1000000;
     private int heartbeatThreadPoolQueueCapacity = 50000;
     private int endTransactionPoolQueueCapacity = 100000;
+    private int adminBrokerThreadPoolQueueCapacity = 10000;
+    private int loadBalanceThreadPoolQueueCapacity = 100000;
 
     private int filterServerNums = 0;
 
@@ -108,17 +116,20 @@ public class BrokerConfig {
 
     private boolean highSpeedMode = false;
 
-    private boolean commercialEnable = true;
-    private int commercialTimerCount = 1;
-    private int commercialTransCount = 1;
-    private int commercialBigCount = 1;
     private int commercialBaseCount = 1;
 
+    private int commercialSizePerMsg = 4 * 1024;
+
+    private boolean accountStatsEnable = true;
+    private boolean accountStatsPrintZeroValues = true;
+
     private boolean transferMsgByHeap = true;
     private int maxDelayTime = 40;
 
     private String regionId = MixAll.DEFAULT_TRACE_REGION_ID;
-    private int registerBrokerTimeoutMills = 6000;
+    private int registerBrokerTimeoutMills = 24000;
+
+    private int sendHeartbeatTimeoutMillis = 1000;
 
     private boolean slaveReadEnable = false;
 
@@ -128,8 +139,10 @@ public class BrokerConfig {
     private boolean brokerFastFailureEnable = true;
     private long waitTimeMillsInSendQueue = 200;
     private long waitTimeMillsInPullQueue = 5 * 1000;
+    private long waitTimeMillsInLitePullQueue = 5 * 1000;
     private long waitTimeMillsInHeartbeatQueue = 31 * 1000;
     private long waitTimeMillsInTransactionQueue = 3 * 1000;
+    private long waitTimeMillsInAckQueue = 3000;
 
     private long startAcceptSendRequestTimeStamp = 0L;
 
@@ -141,6 +154,9 @@ public class BrokerConfig {
     // 2. Filter bit map will be saved to consume queue extend file if allowed.
     private boolean enableCalcFilterBitMap = false;
 
+    //Reject the pull consumer instance to pull messages from broker.
+    private boolean rejectPullConsumerEnable = false;
+
     // Expect num of consumers will use filter.
     private int expectConsumerNumUseFilter = 32;
 
@@ -160,10 +176,23 @@ public class BrokerConfig {
 
     /**
      * This configurable item defines interval of topics registration of broker to name server. Allowing values are
-     * between 10, 000 and 60, 000 milliseconds.
+     * between 10,000 and 60,000 milliseconds.
      */
     private int registerNameServerPeriod = 1000 * 30;
 
+    /**
+     * the interval to send heartbeat to name server for liveness detection.
+     */
+    private int brokerHeartbeatInterval = 1000;
+
+    /**
+     * How long the broker will be considered as inactive by nameserver since last heartbeat. Effective only if
+     * enableSlaveActingMaster is true
+     */
+    private long brokerNotActiveTimeoutMillis = 10 * 1000;
+
+    private boolean netWorkFlowController = true;
+
     private int popPollingSize = 1024;
     private int popPollingMapSize = 100000;
     // 20w cost 200M heap memory.
@@ -172,13 +201,20 @@ public class BrokerConfig {
     private long reviveInterval = 1000;
     private long reviveMaxSlow = 3;
     private long reviveScanTime = 10000;
-    private boolean enablePopLog = true;
+    private boolean enablePopLog = false;
     private boolean enablePopBufferMerge = false;
     private int popCkStayBufferTime = 10 * 1000;
     private int popCkStayBufferTimeOut = 3 * 1000;
     private int popCkMaxBufferSize = 200000;
     private int popCkOffsetMaxQueueSize = 20000;
 
+    private boolean realTimeNotifyConsumerChange = true;
+
+    private boolean litePullMessageEnable = true;
+
+    // The period to sync broker member group from namesrv, default value is 1 second
+    private int syncBrokerMemberGroupPeriod = 1000;
+
     /**
      * the interval of pulling topic information from the named server
      */
@@ -233,15 +269,31 @@ public class BrokerConfig {
 
     private long forwardTimeout = 3 * 1000;
 
-    public static String localHostName() {
-        try {
-            return InetAddress.getLocalHost().getHostName();
-        } catch (UnknownHostException e) {
-            log.error("Failed to obtain the host name", e);
-        }
+    /**
+     * Slave will act master when failover. For example, if master down, timer or transaction message which is expire in slave will
+     * put to master (master of the same process in broker container mode or other masters in cluster when enableFailoverRemotingActing is true)
+     * when enableSlaveActingMaster is true
+     */
+    private boolean enableSlaveActingMaster = false;
+
+    private boolean enableRemoteEscape = false;
 
-        return "DEFAULT_BROKER";
-    }
+    private boolean skipPreOnline = false;
+
+    private boolean asyncSendEnable = true;
+
+    private long consumerOffsetUpdateVersionStep = 500;
+
+    private long delayOffsetUpdateVersionStep = 200;
+
+    /**
+     * Whether to lock quorum replicas.
+     *
+     * True: need to lock quorum replicas succeed. False: only need to lock one replica succeed.
+     */
+    private boolean lockInStrictMode = false;
+
+    private String metaDataHosts = "";
 
     public long getMaxPopPollingSize() {
         return maxPopPollingSize;
@@ -451,22 +503,6 @@ public class BrokerConfig {
         this.highSpeedMode = highSpeedMode;
     }
 
-    public String getRocketmqHome() {
-        return rocketmqHome;
-    }
-
-    public void setRocketmqHome(String rocketmqHome) {
-        this.rocketmqHome = rocketmqHome;
-    }
-
-    public String getBrokerName() {
-        return brokerName;
-    }
-
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
-    }
-
     public int getBrokerPermission() {
         return brokerPermission;
     }
@@ -491,14 +527,6 @@ public class BrokerConfig {
         this.autoCreateTopicEnable = autoCreateTopic;
     }
 
-    public String getBrokerClusterName() {
-        return brokerClusterName;
-    }
-
-    public void setBrokerClusterName(String brokerClusterName) {
-        this.brokerClusterName = brokerClusterName;
-    }
-
     public String getBrokerIP1() {
         return brokerIP1;
     }
@@ -603,14 +631,6 @@ public class BrokerConfig {
         this.namesrvAddr = namesrvAddr;
     }
 
-    public long getBrokerId() {
-        return brokerId;
-    }
-
-    public void setBrokerId(long brokerId) {
-        this.brokerId = brokerId;
-    }
-
     public boolean isAutoCreateSubscriptionGroup() {
         return autoCreateSubscriptionGroup;
     }
@@ -619,6 +639,94 @@ public class BrokerConfig {
         this.autoCreateSubscriptionGroup = autoCreateSubscriptionGroup;
     }
 
+    public String getBrokerConfigPath() {
+        return brokerConfigPath;
+    }
+
+    public void setBrokerConfigPath(String brokerConfigPath) {
+        this.brokerConfigPath = brokerConfigPath;
+    }
+
+    public String getRocketmqHome() {
+        return rocketmqHome;
+    }
+
+    public void setRocketmqHome(String rocketmqHome) {
+        this.rocketmqHome = rocketmqHome;
+    }
+
+    public int getListenPort() {
+        return listenPort;
+    }
+
+    public void setListenPort(int listenPort) {
+        this.listenPort = listenPort;
+    }
+
+    public int getLitePullMessageThreadPoolNums() {
+        return litePullMessageThreadPoolNums;
+    }
+
+    public void setLitePullMessageThreadPoolNums(int litePullMessageThreadPoolNums) {
+        this.litePullMessageThreadPoolNums = litePullMessageThreadPoolNums;
+    }
+
+    public int getLitePullThreadPoolQueueCapacity() {
+        return litePullThreadPoolQueueCapacity;
+    }
+
+    public void setLitePullThreadPoolQueueCapacity(int litePullThreadPoolQueueCapacity) {
+        this.litePullThreadPoolQueueCapacity = litePullThreadPoolQueueCapacity;
+    }
+
+    public int getAdminBrokerThreadPoolQueueCapacity() {
+        return adminBrokerThreadPoolQueueCapacity;
+    }
+
+    public void setAdminBrokerThreadPoolQueueCapacity(int adminBrokerThreadPoolQueueCapacity) {
+        this.adminBrokerThreadPoolQueueCapacity = adminBrokerThreadPoolQueueCapacity;
+    }
+
+    public int getLoadBalanceThreadPoolQueueCapacity() {
+        return loadBalanceThreadPoolQueueCapacity;
+    }
+
+    public void setLoadBalanceThreadPoolQueueCapacity(int loadBalanceThreadPoolQueueCapacity) {
+        this.loadBalanceThreadPoolQueueCapacity = loadBalanceThreadPoolQueueCapacity;
+    }
+
+    public int getSendHeartbeatTimeoutMillis() {
+        return sendHeartbeatTimeoutMillis;
+    }
+
+    public void setSendHeartbeatTimeoutMillis(int sendHeartbeatTimeoutMillis) {
+        this.sendHeartbeatTimeoutMillis = sendHeartbeatTimeoutMillis;
+    }
+
+    public long getWaitTimeMillsInLitePullQueue() {
+        return waitTimeMillsInLitePullQueue;
+    }
+
+    public void setWaitTimeMillsInLitePullQueue(long waitTimeMillsInLitePullQueue) {
+        this.waitTimeMillsInLitePullQueue = waitTimeMillsInLitePullQueue;
+    }
+
+    public boolean isLitePullMessageEnable() {
+        return litePullMessageEnable;
+    }
+
+    public void setLitePullMessageEnable(boolean litePullMessageEnable) {
+        this.litePullMessageEnable = litePullMessageEnable;
+    }
+
+    public int getSyncBrokerMemberGroupPeriod() {
+        return syncBrokerMemberGroupPeriod;
+    }
+
+    public void setSyncBrokerMemberGroupPeriod(int syncBrokerMemberGroupPeriod) {
+        this.syncBrokerMemberGroupPeriod = syncBrokerMemberGroupPeriod;
+    }
+
     public boolean isRejectTransactionMessage() {
         return rejectTransactionMessage;
     }
@@ -731,38 +839,6 @@ public class BrokerConfig {
         this.clientManageThreadPoolNums = clientManageThreadPoolNums;
     }
 
-    public boolean isCommercialEnable() {
-        return commercialEnable;
-    }
-
-    public void setCommercialEnable(final boolean commercialEnable) {
-        this.commercialEnable = commercialEnable;
-    }
-
-    public int getCommercialTimerCount() {
-        return commercialTimerCount;
-    }
-
-    public void setCommercialTimerCount(final int commercialTimerCount) {
-        this.commercialTimerCount = commercialTimerCount;
-    }
-
-    public int getCommercialTransCount() {
-        return commercialTransCount;
-    }
-
-    public void setCommercialTransCount(final int commercialTransCount) {
-        this.commercialTransCount = commercialTransCount;
-    }
-
-    public int getCommercialBigCount() {
-        return commercialBigCount;
-    }
-
-    public void setCommercialBigCount(final int commercialBigCount) {
-        this.commercialBigCount = commercialBigCount;
-    }
-
     public int getMaxDelayTime() {
         return maxDelayTime;
     }
@@ -1051,6 +1127,142 @@ public class BrokerConfig {
         this.forwardTimeout = timeout;
     }
 
+    public int getBrokerHeartbeatInterval() {
+        return brokerHeartbeatInterval;
+    }
+
+    public void setBrokerHeartbeatInterval(int brokerHeartbeatInterval) {
+        this.brokerHeartbeatInterval = brokerHeartbeatInterval;
+    }
+
+    public long getBrokerNotActiveTimeoutMillis() {
+        return brokerNotActiveTimeoutMillis;
+    }
+
+    public void setBrokerNotActiveTimeoutMillis(long brokerNotActiveTimeoutMillis) {
+        this.brokerNotActiveTimeoutMillis = brokerNotActiveTimeoutMillis;
+    }
+
+    public boolean isNetWorkFlowController() {
+        return netWorkFlowController;
+    }
+
+    public void setNetWorkFlowController(boolean netWorkFlowController) {
+        this.netWorkFlowController = netWorkFlowController;
+    }
+
+    public boolean isRealTimeNotifyConsumerChange() {
+        return realTimeNotifyConsumerChange;
+    }
+
+    public void setRealTimeNotifyConsumerChange(boolean realTimeNotifyConsumerChange) {
+        this.realTimeNotifyConsumerChange = realTimeNotifyConsumerChange;
+    }
+
+    public boolean isEnableSlaveActingMaster() {
+        return enableSlaveActingMaster;
+    }
+
+    public void setEnableSlaveActingMaster(boolean enableSlaveActingMaster) {
+        this.enableSlaveActingMaster = enableSlaveActingMaster;
+    }
+
+    public boolean isEnableRemoteEscape() {
+        return enableRemoteEscape;
+    }
+
+    public void setEnableRemoteEscape(boolean enableRemoteEscape) {
+        this.enableRemoteEscape = enableRemoteEscape;
+    }
+
+    public boolean isSkipPreOnline() {
+        return skipPreOnline;
+    }
+
+    public void setSkipPreOnline(boolean skipPreOnline) {
+        this.skipPreOnline = skipPreOnline;
+    }
+
+    public boolean isAsyncSendEnable() {
+        return asyncSendEnable;
+    }
+
+    public void setAsyncSendEnable(boolean asyncSendEnable) {
+        this.asyncSendEnable = asyncSendEnable;
+    }
+
+    public long getConsumerOffsetUpdateVersionStep() {
+        return consumerOffsetUpdateVersionStep;
+    }
+
+    public void setConsumerOffsetUpdateVersionStep(long consumerOffsetUpdateVersionStep) {
+        this.consumerOffsetUpdateVersionStep = consumerOffsetUpdateVersionStep;
+    }
+
+    public long getDelayOffsetUpdateVersionStep() {
+        return delayOffsetUpdateVersionStep;
+    }
+
+    public void setDelayOffsetUpdateVersionStep(long delayOffsetUpdateVersionStep) {
+        this.delayOffsetUpdateVersionStep = delayOffsetUpdateVersionStep;
+    }
+
+    public int getCommercialSizePerMsg() {
+        return commercialSizePerMsg;
+    }
+
+    public void setCommercialSizePerMsg(int commercialSizePerMsg) {
+        this.commercialSizePerMsg = commercialSizePerMsg;
+    }
+
+    public long getWaitTimeMillsInAckQueue() {
+        return waitTimeMillsInAckQueue;
+    }
+
+    public void setWaitTimeMillsInAckQueue(long waitTimeMillsInAckQueue) {
+        this.waitTimeMillsInAckQueue = waitTimeMillsInAckQueue;
+    }
+
+    public boolean isRejectPullConsumerEnable() {
+        return rejectPullConsumerEnable;
+    }
+
+    public void setRejectPullConsumerEnable(boolean rejectPullConsumerEnable) {
+        this.rejectPullConsumerEnable = rejectPullConsumerEnable;
+    }
+
+    public boolean isAccountStatsEnable() {
+        return accountStatsEnable;
+    }
+
+    public void setAccountStatsEnable(boolean accountStatsEnable) {
+        this.accountStatsEnable = accountStatsEnable;
+    }
+
+    public boolean isAccountStatsPrintZeroValues() {
+        return accountStatsPrintZeroValues;
+    }
+
+    public void setAccountStatsPrintZeroValues(boolean accountStatsPrintZeroValues) {
+        this.accountStatsPrintZeroValues = accountStatsPrintZeroValues;
+    }
+
+    public boolean isLockInStrictMode() {
+        return lockInStrictMode;
+    }
+
+    public void setLockInStrictMode(boolean lockInStrictMode) {
+        this.lockInStrictMode = lockInStrictMode;
+    }
+
+    public String getMetaDataHosts() {
+        return metaDataHosts;
+    }
+
+    public void setMetaDataHosts(String metaDataHosts) {
+        this.metaDataHosts = metaDataHosts;
+    }
+
     public boolean isIsolateLogEnable() {
         return isolateLogEnable;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/BrokerIdentity.java b/common/src/main/java/org/apache/rocketmq/common/BrokerIdentity.java
new file mode 100644
index 0000000..e5ef3d7
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/BrokerIdentity.java
@@ -0,0 +1,149 @@
+/*
+ * 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;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.rocketmq.common.annotation.ImportantField;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.logging.InnerLoggerFactory;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+
+public class BrokerIdentity {
+    private static final String DEFAULT_CLUSTER_NAME = "DefaultCluster";
+    protected static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
+
+    public static final BrokerIdentity BROKER_CONTAINER_IDENTITY = new BrokerIdentity(true);
+
+    @ImportantField
+    private String brokerName = localHostName();
+    @ImportantField
+    private String brokerClusterName = DEFAULT_CLUSTER_NAME;
+    @ImportantField
+    private volatile long brokerId = MixAll.MASTER_ID;
+
+    private boolean isBrokerContainer = false;
+
+    // Do not set it manually, it depends on the startup mode
+    // Broker start by BrokerStartup is false, start or add by BrokerContainer is true
+    private boolean isInBrokerContainer = false;
+
+    public BrokerIdentity() {
+    }
+
+    public BrokerIdentity(boolean isBrokerContainer) {
+        this.isBrokerContainer = isBrokerContainer;
+    }
+
+    public BrokerIdentity(String brokerClusterName, String brokerName, long brokerId) {
+        this.brokerName = brokerName;
+        this.brokerClusterName = brokerClusterName;
+        this.brokerId = brokerId;
+    }
+
+    public BrokerIdentity(String brokerClusterName, String brokerName, long brokerId, boolean isInBrokerContainer) {
+        this.brokerName = brokerName;
+        this.brokerClusterName = brokerClusterName;
+        this.brokerId = brokerId;
+        this.isInBrokerContainer = isInBrokerContainer;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(final String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public String getBrokerClusterName() {
+        return brokerClusterName;
+    }
+
+    public void setBrokerClusterName(final String brokerClusterName) {
+        this.brokerClusterName = brokerClusterName;
+    }
+
+    public long getBrokerId() {
+        return brokerId;
+    }
+
+    public void setBrokerId(final long brokerId) {
+        this.brokerId = brokerId;
+    }
+
+    public boolean isInBrokerContainer() {
+        return isInBrokerContainer;
+    }
+
+    public void setInBrokerContainer(boolean inBrokerContainer) {
+        isInBrokerContainer = inBrokerContainer;
+    }
+
+    protected static String localHostName() {
+        try {
+            return InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException e) {
+            LOGGER.error("Failed to obtain the host name", e);
+        }
+
+        return "DEFAULT_BROKER";
+    }
+
+    public String getCanonicalName() {
+        if (isBrokerContainer) {
+            return InnerLoggerFactory.BROKER_CONTAINER_NAME;
+        }
+        return this.getBrokerClusterName() + "_" + this.getBrokerName() + "_" + this.getBrokerId();
+    }
+
+    public String getLoggerIdentifier() {
+        return "#" + getCanonicalName() + "#";
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final BrokerIdentity identity = (BrokerIdentity) o;
+
+        return new EqualsBuilder()
+            .append(brokerId, identity.brokerId)
+            .append(brokerName, identity.brokerName)
+            .append(brokerClusterName, identity.brokerClusterName)
+            .isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37)
+            .append(brokerName)
+            .append(brokerClusterName)
+            .append(brokerId)
+            .toHashCode();
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/BrokerSyncInfo.java b/common/src/main/java/org/apache/rocketmq/common/BrokerSyncInfo.java
new file mode 100644
index 0000000..1ec4bc5
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/BrokerSyncInfo.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+public class BrokerSyncInfo extends RemotingSerializable {
+    /**
+     * For slave online sync, retrieve HA address before register
+     */
+    private String masterHaAddress;
+
+    private long masterFlushOffset;
+
+    private String masterAddress;
+
+    public BrokerSyncInfo(String masterHaAddress, long masterFlushOffset, String masterAddress) {
+        this.masterHaAddress = masterHaAddress;
+        this.masterFlushOffset = masterFlushOffset;
+        this.masterAddress = masterAddress;
+    }
+
+    public String getMasterHaAddress() {
+        return masterHaAddress;
+    }
+
+    public void setMasterHaAddress(String masterHaAddress) {
+        this.masterHaAddress = masterHaAddress;
+    }
+
+    public long getMasterFlushOffset() {
+        return masterFlushOffset;
+    }
+
+    public void setMasterFlushOffset(long masterFlushOffset) {
+        this.masterFlushOffset = masterFlushOffset;
+    }
+
+    public String getMasterAddress() {
+        return masterAddress;
+    }
+
+    public void setMasterAddress(String masterAddress) {
+        this.masterAddress = masterAddress;
+    }
+
+    @Override
+    public String toString() {
+        return "BrokerSyncInfo{" +
+            "masterHaAddress='" + masterHaAddress + '\'' +
+            ", masterFlushOffset=" + masterFlushOffset +
+            ", masterAddress=" + masterAddress +
+            '}';
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/Configuration.java b/common/src/main/java/org/apache/rocketmq/common/Configuration.java
index 064b5e6..29dafa1 100644
--- a/common/src/main/java/org/apache/rocketmq/common/Configuration.java
+++ b/common/src/main/java/org/apache/rocketmq/common/Configuration.java
@@ -55,6 +55,9 @@ public class Configuration {
             return;
         }
         for (Object configObject : configObjects) {
+            if (configObject == null) {
+                continue;
+            }
             registerConfig(configObject);
         }
     }
@@ -234,6 +237,24 @@ public class Configuration {
         return null;
     }
 
+    public String getClientConfigsFormatString(List<String> clientKeys) {
+        try {
+            readWriteLock.readLock().lockInterruptibly();
+
+            try {
+
+                return getClientConfigsInternal(clientKeys);
+
+            } finally {
+                readWriteLock.readLock().unlock();
+            }
+        } catch (InterruptedException e) {
+            log.error("getAllConfigsFormatString lock error");
+        }
+
+        return null;
+    }
+
     public String getDataVersionJson() {
         return this.dataVersion.toJson();
     }
@@ -276,6 +297,26 @@ public class Configuration {
         return stringBuilder.toString();
     }
 
+    private String getClientConfigsInternal(List<String> clientConigKeys) {
+        StringBuilder stringBuilder = new StringBuilder();
+        Properties clientProperties = new Properties();
+
+        // reload from config object ?
+        for (Object configObject : this.configObjectList) {
+            Properties properties = MixAll.object2Properties(configObject);
+
+            for (String nameNow : clientConigKeys) {
+                if (properties.containsKey(nameNow)) {
+                    clientProperties.put(nameNow, properties.get(nameNow));
+                }
+            }
+
+        }
+        stringBuilder.append(MixAll.properties2String(clientProperties));
+
+        return stringBuilder.toString();
+    }
+
     private void merge(Properties from, Properties to) {
         for (Entry<Object, Object> next : from.entrySet()) {
             Object fromObj = next.getValue(), toObj = to.get(next.getKey());
diff --git a/common/src/main/java/org/apache/rocketmq/common/DataVersion.java b/common/src/main/java/org/apache/rocketmq/common/DataVersion.java
index e54000d..6d53bd8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/DataVersion.java
+++ b/common/src/main/java/org/apache/rocketmq/common/DataVersion.java
@@ -20,19 +20,34 @@ import java.util.concurrent.atomic.AtomicLong;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
 public class DataVersion extends RemotingSerializable {
+    private long stateVersion = 0L;
     private long timestamp = System.currentTimeMillis();
     private AtomicLong counter = new AtomicLong(0);
 
     public void assignNewOne(final DataVersion dataVersion) {
         this.timestamp = dataVersion.timestamp;
+        this.stateVersion = dataVersion.stateVersion;
         this.counter.set(dataVersion.counter.get());
     }
 
     public void nextVersion() {
+        this.nextVersion(0L);
+    }
+
+    public void nextVersion(long stateVersion) {
         this.timestamp = System.currentTimeMillis();
+        this.stateVersion = stateVersion;
         this.counter.incrementAndGet();
     }
 
+    public long getStateVersion() {
+        return stateVersion;
+    }
+
+    public void setStateVersion(long stateVersion) {
+        this.stateVersion = stateVersion;
+    }
+
     public long getTimestamp() {
         return timestamp;
     }
@@ -56,22 +71,25 @@ public class DataVersion extends RemotingSerializable {
         if (o == null || getClass() != o.getClass())
             return false;
 
-        final DataVersion that = (DataVersion) o;
+        DataVersion version = (DataVersion) o;
 
-        if (timestamp != that.timestamp) {
+        if (getStateVersion() != version.getStateVersion())
+            return false;
+        if (getTimestamp() != version.getTimestamp())
             return false;
-        }
 
-        if (counter != null && that.counter != null) {
-            return counter.longValue() == that.counter.longValue();
+        if (counter != null && version.counter != null) {
+            return counter.longValue() == version.counter.longValue();
         }
 
-        return (null == counter) && (null == that.counter);
+        return (null == counter) && (null == version.counter);
+
     }
 
     @Override
     public int hashCode() {
-        int result = (int) (timestamp ^ (timestamp >>> 32));
+        int result = (int) (getStateVersion() ^ (getStateVersion() >>> 32));
+        result = 31 * result + (int) (getTimestamp() ^ (getTimestamp() >>> 32));
         if (null != counter) {
             long l = counter.get();
             result = 31 * result + (int) (l ^ (l >>> 32));
@@ -87,4 +105,21 @@ public class DataVersion extends RemotingSerializable {
         sb.append(']');
         return sb.toString();
     }
+
+    public int compare(DataVersion dataVersion) {
+        if (this.getStateVersion() > dataVersion.getStateVersion()) {
+            return 1;
+        } else if (this.getStateVersion() < dataVersion.getStateVersion()) {
+            return -1;
+        } else if (this.getCounter().get() > dataVersion.getCounter().get()) {
+            return 1;
+        } else if (this.getCounter().get() < dataVersion.getCounter().get()) {
+            return -1;
+        } else if (this.getTimestamp() > dataVersion.getTimestamp()) {
+            return 1;
+        } else if (this.getTimestamp() < dataVersion.getTimestamp()) {
+            return -1;
+        }
+        return 0;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java b/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java
index d30789f..e1532d9 100644
--- a/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java
+++ b/common/src/main/java/org/apache/rocketmq/common/KeyBuilder.java
@@ -34,4 +34,8 @@ public class KeyBuilder {
     public static String buildPollingKey(String topic, String cid, int queueId) {
         return topic + PopAckConstants.SPLIT + cid + PopAckConstants.SPLIT + queueId;
     }
+
+    public static String buildPollingNotificationKey(String topic, int queueId) {
+        return topic + PopAckConstants.SPLIT + queueId;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/LockCallback.java
similarity index 76%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/LockCallback.java
index 08091f4..9abf367 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/LockCallback.java
@@ -14,12 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common;
 
-package org.apache.rocketmq.common.message;
+import java.util.Set;
+import org.apache.rocketmq.common.message.MessageQueue;
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+public interface LockCallback {
+    void onSuccess(final Set<MessageQueue> lockOKMQSet);
+
+    void onException(final Throwable e);
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/MixAll.java b/common/src/main/java/org/apache/rocketmq/common/MixAll.java
index a15294f..638d038 100644
--- a/common/src/main/java/org/apache/rocketmq/common/MixAll.java
+++ b/common/src/main/java/org/apache/rocketmq/common/MixAll.java
@@ -74,7 +74,10 @@ public class MixAll {
     public static final String LOCALHOST = localhost();
     public static final String DEFAULT_CHARSET = "UTF-8";
     public static final long MASTER_ID = 0L;
+    public static final long FIRST_SLAVE_ID = 1L;
     public static final long CURRENT_JVM_PID = getPID();
+    public final static int UNIT_PRE_SIZE_FOR_MSG = 28;
+
     public static final String RETRY_GROUP_TOPIC_PREFIX = "%RETRY%";
     public static final String DLQ_GROUP_TOPIC_PREFIX = "%DLQ%";
     public static final String REPLY_TOPIC_POSTFIX = "REPLY_TOPIC";
@@ -90,6 +93,7 @@ public class MixAll {
     public static final String LOGICAL_QUEUE_MOCK_BROKER_PREFIX = "__syslo__";
     public static final String METADATA_SCOPE_GLOBAL = "__global__";
     public static final String LOGICAL_QUEUE_MOCK_BROKER_NAME_NOT_EXIST = "__syslo__none__";
+    public static final String MULTI_PATH_SPLITTER = System.getProperty("rocketmq.broker.multiPathSplitter", ",");
 
     public static String getWSAddr() {
         String wsDomainName = System.getProperty("rocketmq.namesrv.domain", DEFAULT_NAMESRV_ADDR_LOOKUP);
diff --git a/common/src/main/java/org/apache/rocketmq/common/PopAckConstants.java b/common/src/main/java/org/apache/rocketmq/common/PopAckConstants.java
index 839f947..ac5a1a1 100644
--- a/common/src/main/java/org/apache/rocketmq/common/PopAckConstants.java
+++ b/common/src/main/java/org/apache/rocketmq/common/PopAckConstants.java
@@ -32,4 +32,13 @@ public class PopAckConstants {
     public static final String ACK_TAG = "ack";
     public static final String SPLIT = "@";
 
+    /**
+     * Build cluster revive topic
+     *
+     * @param clusterName cluster name
+     * @return revive topic
+     */
+    public static String buildClusterReviveTopic(String clusterName) {
+        return PopAckConstants.REVIVE_TOPIC + clusterName;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/ServiceThread.java b/common/src/main/java/org/apache/rocketmq/common/ServiceThread.java
index ead8117..a84bcc8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/ServiceThread.java
+++ b/common/src/main/java/org/apache/rocketmq/common/ServiceThread.java
@@ -27,7 +27,7 @@ public abstract class ServiceThread implements Runnable {
 
     private static final long JOIN_TIME = 90 * 1000;
 
-    private Thread thread;
+    protected Thread thread;
     protected final CountDownLatch2 waitPoint = new CountDownLatch2(1);
     protected volatile AtomicBoolean hasNotified = new AtomicBoolean(false);
     protected volatile boolean stopped = false;
@@ -76,17 +76,17 @@ public abstract class ServiceThread implements Runnable {
 
             long beginTime = System.currentTimeMillis();
             if (!this.thread.isDaemon()) {
-                this.thread.join(this.getJointime());
+                this.thread.join(this.getJoinTime());
             }
             long elapsedTime = System.currentTimeMillis() - beginTime;
             log.info("join thread " + this.getServiceName() + " elapsed time(ms) " + elapsedTime + " "
-                + this.getJointime());
+                + this.getJoinTime());
         } catch (InterruptedException e) {
             log.error("Interrupted", e);
         }
     }
 
-    public long getJointime() {
+    public long getJoinTime() {
         return JOIN_TIME;
     }
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/ThreadFactoryImpl.java b/common/src/main/java/org/apache/rocketmq/common/ThreadFactoryImpl.java
index 564d60c..a76f605 100644
--- a/common/src/main/java/org/apache/rocketmq/common/ThreadFactoryImpl.java
+++ b/common/src/main/java/org/apache/rocketmq/common/ThreadFactoryImpl.java
@@ -34,6 +34,19 @@ public class ThreadFactoryImpl implements ThreadFactory {
         this.daemon = daemon;
     }
 
+    public ThreadFactoryImpl(final String threadNamePrefix, BrokerIdentity brokerIdentity) {
+        this(threadNamePrefix, false, brokerIdentity);
+    }
+
+    public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon, BrokerIdentity brokerIdentity) {
+        this.daemon = daemon;
+        if (brokerIdentity != null && brokerIdentity.isInBrokerContainer()) {
+            this.threadNamePrefix = brokerIdentity.getLoggerIdentifier() + threadNamePrefix;
+        } else {
+            this.threadNamePrefix = threadNamePrefix;
+        }
+    }
+
     @Override
     public Thread newThread(Runnable r) {
         Thread thread = new Thread(r, threadNamePrefix + this.threadIndex.incrementAndGet());
diff --git a/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java b/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java
index 610c3e2..f11b3b3 100644
--- a/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java
+++ b/common/src/main/java/org/apache/rocketmq/common/TopicConfig.java
@@ -40,16 +40,6 @@ public class TopicConfig {
         this.topicName = topicName;
     }
 
-    public TopicConfig(TopicConfig other) {
-        this.topicName = other.topicName;
-        this.readQueueNums = other.readQueueNums;
-        this.writeQueueNums = other.writeQueueNums;
-        this.perm = other.perm;
-        this.topicFilterType = other.topicFilterType;
-        this.topicSysFlag = other.topicSysFlag;
-        this.order = other.order;
-    }
-
     public TopicConfig(String topicName, int readQueueNums, int writeQueueNums) {
         this.topicName = topicName;
         this.readQueueNums = readQueueNums;
@@ -63,16 +53,38 @@ public class TopicConfig {
         this.perm = perm;
     }
 
+    public TopicConfig(String topicName, int readQueueNums, int writeQueueNums, int perm, int topicSysFlag) {
+        this.topicName = topicName;
+        this.readQueueNums = readQueueNums;
+        this.writeQueueNums = writeQueueNums;
+        this.perm = perm;
+        this.topicSysFlag = topicSysFlag;
+    }
+
+    public TopicConfig(TopicConfig other) {
+        this.topicName = other.topicName;
+        this.readQueueNums = other.readQueueNums;
+        this.writeQueueNums = other.writeQueueNums;
+        this.perm = other.perm;
+        this.topicFilterType = other.topicFilterType;
+        this.topicSysFlag = other.topicSysFlag;
+        this.order = other.order;
+    }
     public String encode() {
         StringBuilder sb = new StringBuilder();
+        //[0]
         sb.append(this.topicName);
         sb.append(SEPARATOR);
+        //[1]
         sb.append(this.readQueueNums);
         sb.append(SEPARATOR);
+        //[2]
         sb.append(this.writeQueueNums);
         sb.append(SEPARATOR);
+        //[3]
         sb.append(this.perm);
         sb.append(SEPARATOR);
+        //[4]
         sb.append(this.topicFilterType);
 
         // Leave the encode/decode [attributes] out for now
@@ -82,7 +94,7 @@ public class TopicConfig {
 
     public boolean decode(final String in) {
         String[] strs = in.split(SEPARATOR);
-        if (strs != null && strs.length == 5) {
+        if (strs != null && strs.length >= 5) {
             this.topicName = strs[0];
 
             this.readQueueNums = Integer.parseInt(strs[1]);
@@ -165,18 +177,27 @@ public class TopicConfig {
 
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
 
         TopicConfig that = (TopicConfig) o;
 
-        if (readQueueNums != that.readQueueNums) return false;
-        if (writeQueueNums != that.writeQueueNums) return false;
-        if (perm != that.perm) return false;
-        if (topicSysFlag != that.topicSysFlag) return false;
-        if (order != that.order) return false;
-        if (topicName != null ? !topicName.equals(that.topicName) : that.topicName != null) return false;
-        if (topicFilterType != that.topicFilterType) return false;
+        if (readQueueNums != that.readQueueNums)
+            return false;
+        if (writeQueueNums != that.writeQueueNums)
+            return false;
+        if (perm != that.perm)
+            return false;
+        if (topicSysFlag != that.topicSysFlag)
+            return false;
+        if (order != that.order)
+            return false;
+        if (topicName != null ? !topicName.equals(that.topicName) : that.topicName != null)
+            return false;
+        if (topicFilterType != that.topicFilterType)
+            return false;
         return attributes != null ? attributes.equals(that.attributes) : that.attributes == null;
     }
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/UnlockCallback.java
similarity index 84%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/UnlockCallback.java
index 08091f4..e86ec8b 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/UnlockCallback.java
@@ -14,12 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common;
 
-package org.apache.rocketmq.common.message;
+public interface UnlockCallback {
+    void onSuccess();
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+    void onException(final Throwable e);
 }
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 a0d35ab..7727544 100644
--- a/common/src/main/java/org/apache/rocketmq/common/UtilAll.java
+++ b/common/src/main/java/org/apache/rocketmq/common/UtilAll.java
@@ -21,10 +21,14 @@ import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.text.NumberFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -229,7 +233,6 @@ public class UtilAll {
                 return -1;
             }
 
-
             long totalSpace = file.getTotalSpace();
 
             if (totalSpace > 0) {
@@ -251,6 +254,25 @@ public class UtilAll {
         return -1;
     }
 
+    public static long getDiskPartitionTotalSpace(final String path) {
+        if (null == path || path.isEmpty()) {
+            return -1;
+        }
+
+        try {
+            File file = new File(path);
+
+
+            if (!file.exists()) {
+                return -1;
+            }
+
+            return file.getTotalSpace() -  file.getFreeSpace() + file.getUsableSpace();
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
     public static int crc32(byte[] array) {
         if (array != null) {
             return crc32(array, 0, array.length);
@@ -619,4 +641,88 @@ public class UtilAll {
         String[] addrArray = str.split(splitter);
         return Arrays.asList(addrArray);
     }
+
+    public static void deleteEmptyDirectory(File file) {
+        if (file == null || !file.exists()) {
+            return;
+        }
+        if (!file.isDirectory()) {
+            return;
+        }
+        File[] files = file.listFiles();
+        if (files == null || files.length <= 0) {
+            file.delete();
+            log.info("delete empty direct, {}", file.getPath());
+        }
+    }
+
+    public static void cleanBuffer(final ByteBuffer buffer) {
+        if (buffer == null || !buffer.isDirect() || buffer.capacity() == 0) {
+            return;
+        }
+        invoke(invoke(viewed(buffer), "cleaner"), "clean");
+    }
+
+    public static Object invoke(final Object target, final String methodName, final Class<?>... args) {
+        return AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @Override
+            public Object run() {
+                try {
+                    Method method = method(target, methodName, args);
+                    method.setAccessible(true);
+                    return method.invoke(target);
+                } catch (Exception e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+        });
+    }
+
+    public static Method method(Object target, String methodName, Class<?>[] args) throws NoSuchMethodException {
+        try {
+            return target.getClass().getMethod(methodName, args);
+        } catch (NoSuchMethodException e) {
+            return target.getClass().getDeclaredMethod(methodName, args);
+        }
+    }
+
+    private static ByteBuffer viewed(ByteBuffer buffer) {
+        String methodName = "viewedBuffer";
+
+        Method[] methods = buffer.getClass().getMethods();
+        for (Method method : methods) {
+            if (method.getName().equals("attachment")) {
+                methodName = "attachment";
+                break;
+            }
+        }
+
+        ByteBuffer viewedBuffer = (ByteBuffer) invoke(buffer, methodName);
+        if (viewedBuffer == null) {
+            return buffer;
+        } else {
+            return viewed(viewedBuffer);
+        }
+    }
+
+    public static void ensureDirOK(final String dirName) {
+        if (dirName != null) {
+            if (dirName.contains(MixAll.MULTI_PATH_SPLITTER)) {
+                String[] dirs = dirName.trim().split(MixAll.MULTI_PATH_SPLITTER);
+                for (String dir : dirs) {
+                    createDirIfNotExist(dir);
+                }
+            } else {
+                createDirIfNotExist(dirName);
+            }
+        }
+    }
+
+    private static void  createDirIfNotExist(String dirName) {
+        File f = new File(dirName);
+        if (!f.exists()) {
+            boolean result = f.mkdirs();
+            log.info(dirName + " mkdir " + (result ? "OK" : "Failed"));
+        }
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java b/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
index c29eccd..a77d5c2 100644
--- a/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
+++ b/common/src/main/java/org/apache/rocketmq/common/constant/LoggerName.java
@@ -19,7 +19,7 @@ package org.apache.rocketmq.common.constant;
 public class LoggerName {
     public static final String FILTERSRV_LOGGER_NAME = "RocketmqFiltersrv";
     public static final String NAMESRV_LOGGER_NAME = "RocketmqNamesrv";
-    public static final String NAMESRV_CONSOLE_NAME = "RocketmqNamesrvConsole";
+    public static final String NAMESRV_WATER_MARK_LOGGER_NAME = "RocketmqNamesrvWaterMark";
     public static final String BROKER_LOGGER_NAME = "RocketmqBroker";
     public static final String BROKER_CONSOLE_NAME = "RocketmqConsole";
     public static final String CLIENT_LOGGER_NAME = "RocketmqClient";
@@ -30,7 +30,11 @@ public class LoggerName {
     public static final String TRANSACTION_LOGGER_NAME = "RocketmqTransaction";
     public static final String REBALANCE_LOCK_LOGGER_NAME = "RocketmqRebalanceLock";
     public static final String ROCKETMQ_STATS_LOGGER_NAME = "RocketmqStats";
+    public static final String DLQ_STATS_LOGGER_NAME = "RocketmqDLQStats";
+    public static final String DLQ_LOGGER_NAME = "RocketmqDLQ";
+    public static final String CONSUMER_STATS_LOGGER_NAME = "RocketmqConsumerStats";
     public static final String COMMERCIAL_LOGGER_NAME = "RocketmqCommercial";
+    public static final String ACCOUNT_LOGGER_NAME = "RocketmqAccount";
     public static final String FLOW_CONTROL_LOGGER_NAME = "RocketmqFlowControl";
     public static final String ROCKETMQ_AUTHORIZE_LOGGER_NAME = "RocketmqAuthorize";
     public static final String DUPLICATION_LOGGER_NAME = "RocketmqDuplication";
@@ -38,5 +42,6 @@ public class LoggerName {
     public static final String WATER_MARK_LOGGER_NAME = "RocketmqWaterMark";
     public static final String FILTER_LOGGER_NAME = "RocketmqFilter";
     public static final String ROCKETMQ_POP_LOGGER_NAME = "RocketmqPop";
+    public static final String FAILOVER_LOGGER_NAME = "RocketmqFailover";
     public static final String STDOUT_LOGGER_NAME = "STDOUT";
 }
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 1fe9aa1..434870c 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
@@ -17,10 +17,16 @@
 package org.apache.rocketmq.common.constant;
 
 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;
+    public static final int INDEX_PERM_PRIORITY = 3;
+    public static final int INDEX_PERM_READ = 2;
+    public static final int INDEX_PERM_WRITE = 1;
+    public static final int INDEX_PERM_INHERIT = 0;
+
+
+    public static final int PERM_PRIORITY = 0x1 << INDEX_PERM_PRIORITY;
+    public static final int PERM_READ = 0x1 << INDEX_PERM_READ;
+    public static final int PERM_WRITE = 0x1 << INDEX_PERM_WRITE;
+    public static final int PERM_INHERIT = 0x1 << INDEX_PERM_INHERIT;
 
     public static String perm2String(final int perm) {
         final StringBuilder sb = new StringBuilder("---");
@@ -50,4 +56,8 @@ public class PermName {
     public static boolean isInherited(final int perm) {
         return (perm & PERM_INHERIT) == PERM_INHERIT;
     }
+
+    public static boolean isPriority(final int perm) {
+        return (perm & PERM_PRIORITY) == PERM_PRIORITY;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/future/FutureTaskExt.java
similarity index 59%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/future/FutureTaskExt.java
index 08091f4..1bd46ea 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/future/FutureTaskExt.java
@@ -15,11 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.rocketmq.common.message;
+package org.apache.rocketmq.common.future;
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+public class FutureTaskExt<V> extends FutureTask<V> {
+    private final Runnable runnable;
+
+    public FutureTaskExt(final Callable<V> callable) {
+        super(callable);
+        this.runnable = null;
+    }
+
+    public FutureTaskExt(final Runnable runnable, final V result) {
+        super(runnable, result);
+        this.runnable = runnable;
+    }
+
+    public Runnable getRunnable() {
+        return runnable;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java
index 105ef5b..a823466 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageConst.java
@@ -39,13 +39,16 @@ public class MessageConst {
     public static final String PROPERTY_MSG_REGION = "MSG_REGION";
     public static final String PROPERTY_TRACE_SWITCH = "TRACE_ON";
     public static final String PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX = "UNIQ_KEY";
+    public static final String PROPERTY_EXTEND_UNIQ_INFO = "EXTEND_UNIQ_INFO";
     public static final String PROPERTY_MAX_RECONSUME_TIMES = "MAX_RECONSUME_TIMES";
     public static final String PROPERTY_CONSUME_START_TIMESTAMP = "CONSUME_START_TIME";
     public static final String PROPERTY_INNER_NUM = "INNER_NUM";
     public static final String PROPERTY_INNER_BASE = "INNER_BASE";
+    public static final String DUP_INFO = "DUP_INFO";
+    public static final String PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS = "CHECK_IMMUNITY_TIME_IN_SECONDS";
     public static final String PROPERTY_TRANSACTION_PREPARED_QUEUE_OFFSET = "TRAN_PREPARED_QUEUE_OFFSET";
+    public static final String PROPERTY_TRANSACTION_ID = "__transactionId__";
     public static final String PROPERTY_TRANSACTION_CHECK_TIMES = "TRANSACTION_CHECK_TIMES";
-    public static final String PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS = "CHECK_IMMUNITY_TIME_IN_SECONDS";
     public static final String PROPERTY_INSTANCE_ID = "INSTANCE_ID";
     public static final String PROPERTY_CORRELATION_ID = "CORRELATION_ID";
     public static final String PROPERTY_MESSAGE_REPLY_TO_CLIENT = "REPLY_TO_CLIENT";
@@ -55,12 +58,29 @@ public class MessageConst {
     public static final String PROPERTY_CLUSTER = "CLUSTER";
     public static final String PROPERTY_MESSAGE_TYPE = "MSG_TYPE";
     public static final String PROPERTY_POP_CK = "POP_CK";
+    public static final String PROPERTY_POP_CK_OFFSET = "POP_CK_OFFSET";
     public static final String PROPERTY_FIRST_POP_TIME = "1ST_POP_TIME";
+    public static final String PROPERTY_SHARDING_KEY = "__SHARDINGKEY";
     public static final String PROPERTY_FORWARD_QUEUE_ID = "PROPERTY_FORWARD_QUEUE_ID";
     public static final String PROPERTY_REDIRECT = "REDIRECT";
     public static final String PROPERTY_INNER_MULTI_DISPATCH = "INNER_MULTI_DISPATCH";
     public static final String PROPERTY_INNER_MULTI_QUEUE_OFFSET = "INNER_MULTI_QUEUE_OFFSET";
 
+    /**
+     * property which name starts with "__RMQ.TRANSIENT." is called transient one that will not stored in broker disks.
+     */
+    public static final String PROPERTY_TRANSIENT_PREFIX = "__RMQ.TRANSIENT.";
+
+    /**
+     * the transient property key of topicSysFlag (set by client when pulling messages)
+     */
+    public static final String PROPERTY_TRANSIENT_TOPIC_CONFIG = PROPERTY_TRANSIENT_PREFIX + "TOPIC_SYS_FLAG";
+
+    /**
+     * the transient property key of groupSysFlag (set by client when pulling messages)
+     */
+    public static final String PROPERTY_TRANSIENT_GROUP_CONFIG = PROPERTY_TRANSIENT_PREFIX + "GROUP_SYS_FLAG";
+
     public static final String KEY_SEPARATOR = " ";
 
     public static final HashSet<String> STRING_HASH_SET = new HashSet<>(64);
@@ -89,7 +109,11 @@ public class MessageConst {
         STRING_HASH_SET.add(PROPERTY_MAX_RECONSUME_TIMES);
         STRING_HASH_SET.add(PROPERTY_CONSUME_START_TIMESTAMP);
         STRING_HASH_SET.add(PROPERTY_POP_CK);
+        STRING_HASH_SET.add(PROPERTY_POP_CK_OFFSET);
         STRING_HASH_SET.add(PROPERTY_FIRST_POP_TIME);
+        STRING_HASH_SET.add(PROPERTY_TRANSACTION_PREPARED_QUEUE_OFFSET);
+        STRING_HASH_SET.add(DUP_INFO);
+        STRING_HASH_SET.add(PROPERTY_EXTEND_UNIQ_INFO);
         STRING_HASH_SET.add(PROPERTY_INSTANCE_ID);
         STRING_HASH_SET.add(PROPERTY_CORRELATION_ID);
         STRING_HASH_SET.add(PROPERTY_MESSAGE_REPLY_TO_CLIENT);
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 813ad89..d6d4a8f 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
@@ -16,6 +16,7 @@
  */
 package org.apache.rocketmq.common.message;
 
+import java.io.IOException;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -263,13 +264,132 @@ public class MessageDecoder {
         return byteBuffer.array();
     }
 
+    /**
+     * Encode without store timestamp and store host, skip blank msg.
+     *
+     * @param messageExt msg
+     * @param needCompress need compress or not
+     * @return byte array
+     * @throws IOException when compress failed
+     */
+    public static byte[] encodeUniquely(MessageExt messageExt, boolean needCompress) throws IOException {
+        byte[] body = messageExt.getBody();
+        byte[] topics = messageExt.getTopic().getBytes(CHARSET_UTF8);
+        byte topicLen = (byte) topics.length;
+        String properties = messageProperties2String(messageExt.getProperties());
+        byte[] propertiesBytes = properties.getBytes(CHARSET_UTF8);
+        short propertiesLength = (short) propertiesBytes.length;
+        int sysFlag = messageExt.getSysFlag();
+        int bornhostLength = (sysFlag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 8 : 20;
+        byte[] newBody = messageExt.getBody();
+        if (needCompress && (sysFlag & MessageSysFlag.COMPRESSED_FLAG) == MessageSysFlag.COMPRESSED_FLAG) {
+            newBody = UtilAll.compress(body, 5);
+        }
+        int bodyLength = newBody.length;
+        int storeSize = messageExt.getStoreSize();
+        ByteBuffer byteBuffer;
+        if (storeSize > 0) {
+            byteBuffer = ByteBuffer.allocate(storeSize - 8); // except size for store timestamp
+        } else {
+            storeSize = 4 +  // 1 TOTALSIZE
+                4 +  // 2 MAGICCODE
+                4 +  // 3 BODYCRC
+                4 +  // 4 QUEUEID
+                4 +  // 5 FLAG
+                8 +  // 6 QUEUEOFFSET
+                8 +  // 7 PHYSICALOFFSET
+                4 +  // 8 SYSFLAG
+                8 +  // 9 BORNTIMESTAMP
+                bornhostLength + // 10 BORNHOST
+                4 +  // 11 RECONSUMETIMES
+                8 +  // 12 Prepared Transaction Offset
+                4 + bodyLength +  // 13 BODY
+                +1 + topicLen +  // 14 TOPIC
+                2 + propertiesLength // 15 propertiesLength
+            ;
+            byteBuffer = ByteBuffer.allocate(storeSize);
+        }
+
+        // 1 TOTALSIZE
+        byteBuffer.putInt(storeSize);
+
+        // 2 MAGICCODE
+        byteBuffer.putInt(MESSAGE_MAGIC_CODE);
+
+        // 3 BODYCRC
+        int bodyCRC = messageExt.getBodyCRC();
+        byteBuffer.putInt(bodyCRC);
+
+        // 4 QUEUEID
+        int queueId = messageExt.getQueueId();
+        byteBuffer.putInt(queueId);
+
+        // 5 FLAG
+        int flag = messageExt.getFlag();
+        byteBuffer.putInt(flag);
+
+        // 6 QUEUEOFFSET
+        long queueOffset = messageExt.getQueueOffset();
+        byteBuffer.putLong(queueOffset);
+
+        // 7 PHYSICALOFFSET
+        long physicOffset = messageExt.getCommitLogOffset();
+        byteBuffer.putLong(physicOffset);
+
+        // 8 SYSFLAG
+        byteBuffer.putInt(sysFlag);
+
+        // 9 BORNTIMESTAMP
+        long bornTimeStamp = messageExt.getBornTimestamp();
+        byteBuffer.putLong(bornTimeStamp);
+
+        // 10 BORNHOST
+        InetSocketAddress bornHost = (InetSocketAddress) messageExt.getBornHost();
+        byteBuffer.put(bornHost.getAddress().getAddress());
+        byteBuffer.putInt(bornHost.getPort());
+
+        // 11 RECONSUMETIMES
+        int reconsumeTimes = messageExt.getReconsumeTimes();
+        byteBuffer.putInt(reconsumeTimes);
+
+        // 12 Prepared Transaction Offset
+        long preparedTransactionOffset = messageExt.getPreparedTransactionOffset();
+        byteBuffer.putLong(preparedTransactionOffset);
+
+        // 13 BODY
+        byteBuffer.putInt(bodyLength);
+        byteBuffer.put(newBody);
+
+        // 14 TOPIC
+        byteBuffer.put(topicLen);
+        byteBuffer.put(topics);
+
+        // 15 properties
+        byteBuffer.putShort(propertiesLength);
+        byteBuffer.put(propertiesBytes);
+
+        return byteBuffer.array();
+    }
+
     public static MessageExt decode(
         ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody) {
         return decode(byteBuffer, readBody, deCompressBody, false);
     }
 
     public static MessageExt decode(
-        ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient) {
+        java.nio.ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient) {
+        return decode(byteBuffer, readBody, deCompressBody, isClient, false, false);
+    }
+
+    public static MessageExt decode(
+        java.nio.ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient,
+        final boolean isSetPropertiesString) {
+        return decode(byteBuffer, readBody, deCompressBody, isClient, isSetPropertiesString, false);
+    }
+
+    public static MessageExt decode(
+        java.nio.ByteBuffer byteBuffer, final boolean readBody, final boolean deCompressBody, final boolean isClient,
+        final boolean isSetPropertiesString, final boolean checkCRC) {
         try {
 
             MessageExt msgExt;
@@ -284,7 +404,10 @@ public class MessageDecoder {
             msgExt.setStoreSize(storeSize);
 
             // 2 MAGICCODE
-            byteBuffer.getInt();
+            int magicCode = byteBuffer.getInt();
+            if (magicCode != MESSAGE_MAGIC_CODE) {
+                throw new RuntimeException("Unknown magicCode: " + magicCode);
+            }
 
             // 3 BODYCRC
             int bodyCRC = byteBuffer.getInt();
@@ -347,6 +470,14 @@ public class MessageDecoder {
                     byte[] body = new byte[bodyLen];
                     byteBuffer.get(body);
 
+                    if (checkCRC) {
+                        //crc body
+                        int crc = UtilAll.crc32(body, 0, bodyLen);
+                        if (crc != bodyCRC) {
+                            throw new Exception("Msg crc is error!");
+                        }
+                    }
+
                     // uncompress body
                     if (deCompressBody && (sysFlag & MessageSysFlag.COMPRESSED_FLAG) == MessageSysFlag.COMPRESSED_FLAG) {
                         body = UtilAll.uncompress(body);
@@ -370,8 +501,14 @@ public class MessageDecoder {
                 byte[] properties = new byte[propertiesLength];
                 byteBuffer.get(properties);
                 String propertiesString = new String(properties, CHARSET_UTF8);
-                Map<String, String> map = string2messageProperties(propertiesString);
-                msgExt.setProperties(map);
+                if (!isSetPropertiesString) {
+                    Map<String, String> map = string2messageProperties(propertiesString);
+                    msgExt.setProperties(map);
+                } else {
+                    Map<String, String> map = string2messageProperties(propertiesString);
+                    map.put("propertiesString", propertiesString);
+                    msgExt.setProperties(map);
+                }
             }
 
             int msgIDLength = storehostIPLength + 4 + 8;
@@ -396,9 +533,9 @@ public class MessageDecoder {
     }
 
     public static List<MessageExt> decodesBatch(ByteBuffer byteBuffer,
-                                                final boolean readBody,
-                                                final boolean decompressBody,
-                                                final boolean isClient) {
+        final boolean readBody,
+        final boolean decompressBody,
+        final boolean isClient) {
         List<MessageExt> msgExts = new ArrayList<MessageExt>();
         while (byteBuffer.hasRemaining()) {
             MessageExt msgExt = decode(byteBuffer, readBody, decompressBody, isClient);
@@ -494,10 +631,7 @@ public class MessageDecoder {
         String properties = messageProperties2String(message.getProperties());
         byte[] propertiesBytes = properties.getBytes(CHARSET_UTF8);
         //note properties length must not more than Short.MAX
-        int propsLen = propertiesBytes.length;
-        if (propsLen > Short.MAX_VALUE)
-            throw new RuntimeException(String.format("Properties size of message exceeded, properties size: {}, maxSize: {}.", propsLen, Short.MAX_VALUE));
-        short propertiesLength = (short) propsLen;
+        short propertiesLength = (short) propertiesBytes.length;
         int sysFlag = message.getFlag();
         int storeSize = 4 // 1 TOTALSIZE
             + 4 // 2 MAGICCOD
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageExt.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageExt.java
index 133cb93..5905d28 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageExt.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageExt.java
@@ -249,6 +249,61 @@ public class MessageExt extends Message {
         this.preparedTransactionOffset = preparedTransactionOffset;
     }
 
+    /**
+     *
+     * achieves topicSysFlag value from transient properties
+     *
+     * @return
+     */
+    public Integer getTopicSysFlag() {
+        String topicSysFlagString = getProperty(MessageConst.PROPERTY_TRANSIENT_TOPIC_CONFIG);
+        if (topicSysFlagString != null && topicSysFlagString.length() > 0) {
+            return Integer.valueOf(topicSysFlagString);
+        }
+        return null;
+    }
+
+    /**
+     * set topicSysFlag to transient properties, or clear it
+     *
+     * @param topicSysFlag
+     */
+    public void setTopicSysFlag(Integer topicSysFlag) {
+        if (topicSysFlag == null) {
+            clearProperty(MessageConst.PROPERTY_TRANSIENT_TOPIC_CONFIG);
+        } else {
+            putProperty(MessageConst.PROPERTY_TRANSIENT_TOPIC_CONFIG, String.valueOf(topicSysFlag));
+        }
+    }
+
+    /**
+     *
+     * achieves groupSysFlag value from transient properties
+     *
+     * @return
+     */
+    public Integer getGroupSysFlag() {
+        String groupSysFlagString = getProperty(MessageConst.PROPERTY_TRANSIENT_GROUP_CONFIG);
+        if (groupSysFlagString != null && groupSysFlagString.length() > 0) {
+            return Integer.valueOf(groupSysFlagString);
+        }
+        return null;
+    }
+
+    /**
+     *
+     * set groupSysFlag to transient properties, or clear it
+     *
+     * @param groupSysFlag
+     */
+    public void setGroupSysFlag(Integer groupSysFlag) {
+        if (groupSysFlag == null) {
+            clearProperty(MessageConst.PROPERTY_TRANSIENT_GROUP_CONFIG);
+        } else {
+            putProperty(MessageConst.PROPERTY_TRANSIENT_GROUP_CONFIG, String.valueOf(groupSysFlag));
+        }
+    }
+
     @Override
     public String toString() {
         return "MessageExt [brokerName=" + brokerName + ", queueId=" + queueId + ", storeSize=" + storeSize + ", queueOffset=" + queueOffset
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBatch.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBatch.java
new file mode 100644
index 0000000..7cdd84d
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBatch.java
@@ -0,0 +1,51 @@
+/**
+ * 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.message;
+
+import java.nio.ByteBuffer;
+
+public class MessageExtBatch extends MessageExtBrokerInner {
+
+    private static final long serialVersionUID = -2353110995348498537L;
+    /**
+     * Inner batch means the batch dose not need to be unwrapped
+     */
+    private boolean isInnerBatch = false;
+    public ByteBuffer wrap() {
+        assert getBody() != null;
+        return ByteBuffer.wrap(getBody(), 0, getBody().length);
+    }
+
+    public boolean isInnerBatch() {
+        return isInnerBatch;
+    }
+
+    public void setInnerBatch(boolean innerBatch) {
+        isInnerBatch = innerBatch;
+    }
+
+    private ByteBuffer encodedBuff;
+
+    public ByteBuffer getEncodedBuff() {
+        return encodedBuff;
+    }
+
+    public void setEncodedBuff(ByteBuffer encodedBuff) {
+        this.encodedBuff = encodedBuff;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBrokerInner.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBrokerInner.java
new file mode 100644
index 0000000..7af7014
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageExtBrokerInner.java
@@ -0,0 +1,63 @@
+/*
+ * 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.message;
+
+import java.nio.ByteBuffer;
+
+import org.apache.rocketmq.common.TopicFilterType;
+
+public class MessageExtBrokerInner extends MessageExt {
+    private static final long serialVersionUID = 7256001576878700634L;
+    private String propertiesString;
+    private long tagsCode;
+
+    private ByteBuffer encodedBuff;
+
+    public ByteBuffer getEncodedBuff() {
+        return encodedBuff;
+    }
+
+    public void setEncodedBuff(ByteBuffer encodedBuff) {
+        this.encodedBuff = encodedBuff;
+    }
+
+    public static long tagsString2tagsCode(final TopicFilterType filter, final String tags) {
+        if (null == tags || tags.length() == 0) { return 0; }
+
+        return tags.hashCode();
+    }
+
+    public static long tagsString2tagsCode(final String tags) {
+        return tagsString2tagsCode(null, tags);
+    }
+
+    public String getPropertiesString() {
+        return propertiesString;
+    }
+
+    public void setPropertiesString(String propertiesString) {
+        this.propertiesString = propertiesString;
+    }
+
+    public long getTagsCode() {
+        return tagsCode;
+    }
+
+    public void setTagsCode(long tagsCode) {
+        this.tagsCode = tagsCode;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
index 08091f4..3afbb1f 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
@@ -18,8 +18,28 @@
 package org.apache.rocketmq.common.message;
 
 public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+    Normal_Msg("Normal"),
+    Order_Msg("Order"),
+    Trans_Msg_Half("Trans"),
+    Trans_msg_Commit("TransCommit"),
+    Delay_Msg("Delay");
+
+    private final String shortName;
+
+    MessageType(String shortName) {
+        this.shortName = shortName;
+    }
+
+    public String getShortName() {
+        return shortName;
+    }
+
+    public static MessageType getByShortName(String shortName) {
+        for (MessageType msgType : MessageType.values()) {
+            if (msgType.getShortName().equals(shortName)) {
+                return msgType;
+            }
+        }
+        return Normal_Msg;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageVersion.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageVersion.java
new file mode 100644
index 0000000..c81275a
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageVersion.java
@@ -0,0 +1,70 @@
+/*
+ * 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.message;
+
+import java.nio.ByteBuffer;
+
+public enum MessageVersion {
+
+    MESSAGE_VERSION_V1(MessageDecoder.MESSAGE_MAGIC_CODE) {
+        @Override
+        public int getTopicLengthSize() {
+            return 1;
+        }
+
+        @Override
+        public int getTopicLength(ByteBuffer buffer) {
+            return buffer.get();
+        }
+
+        @Override
+        public int getTopicLength(ByteBuffer buffer, int index) {
+            return buffer.get(index);
+        }
+
+        @Override
+        public void putTopicLength(ByteBuffer buffer, int topicLength) {
+            buffer.put((byte) topicLength);
+        }
+    };
+
+    private final int magicCode;
+
+    MessageVersion(int magicCode) {
+        this.magicCode = magicCode;
+    }
+
+    public static MessageVersion valueOfMagicCode(int magicCode) {
+        for (MessageVersion version : MessageVersion.values()) {
+            if (version.getMagicCode() == magicCode) {
+                return version;
+            }
+        }
+
+        throw new IllegalArgumentException("Invalid magicCode " + magicCode);
+    }
+
+    public int getMagicCode() {
+        return magicCode;
+    }
+
+    public abstract int getTopicLengthSize();
+
+    public abstract int getTopicLength(java.nio.ByteBuffer buffer);
+    public abstract int getTopicLength(java.nio.ByteBuffer buffer, int index);
+    public abstract void putTopicLength(java.nio.ByteBuffer buffer, int topicLength);
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/namesrv/DefaultTopAddressing.java b/common/src/main/java/org/apache/rocketmq/common/namesrv/DefaultTopAddressing.java
new file mode 100644
index 0000000..6e7a97d
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/namesrv/DefaultTopAddressing.java
@@ -0,0 +1,165 @@
+/*
+ * 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.namesrv;
+
+import com.google.common.base.Strings;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.Map;
+import org.apache.rocketmq.common.MixAll;
+import org.apache.rocketmq.common.UtilAll;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.utils.HttpTinyClient;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+
+public class DefaultTopAddressing implements TopAddressing {
+
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
+
+    private String nsAddr;
+    private String wsAddr;
+    private String unitName;
+    private Map<String, String> para;
+    private List<TopAddressing> topAddressingList;
+
+    public DefaultTopAddressing(final String wsAddr) {
+        this(wsAddr, null);
+    }
+
+    public DefaultTopAddressing(final String wsAddr, final String unitName) {
+        this.wsAddr = wsAddr;
+        this.unitName = unitName;
+        this.topAddressingList = loadCustomTopAddressing();
+    }
+
+
+    public DefaultTopAddressing(final String unitName, final Map<String, String> para, final String wsAddr) {
+        this.wsAddr = wsAddr;
+        this.unitName = unitName;
+        this.para = para;
+        this.topAddressingList = loadCustomTopAddressing();
+    }
+
+    private static String clearNewLine(final String str) {
+        String newString = str.trim();
+        int index = newString.indexOf("\r");
+        if (index != -1) {
+            return newString.substring(0, index);
+        }
+
+        index = newString.indexOf("\n");
+        if (index != -1) {
+            return newString.substring(0, index);
+        }
+
+        return newString;
+    }
+
+    private List<TopAddressing> loadCustomTopAddressing() {
+        ServiceLoader<TopAddressing> serviceLoader = ServiceLoader.load(TopAddressing.class);
+        Iterator<TopAddressing> iterator = serviceLoader.iterator();
+        List<TopAddressing> topAddressingList = new ArrayList<TopAddressing>();
+        if (iterator.hasNext()) {
+            topAddressingList.add(iterator.next());
+        }
+        return topAddressingList;
+    }
+
+    @Override
+    public final String fetchNSAddr() {
+        if (!topAddressingList.isEmpty()) {
+            for (TopAddressing topAddressing : topAddressingList) {
+                String nsAddress = topAddressing.fetchNSAddr();
+                if (!Strings.isNullOrEmpty(nsAddress)) {
+                    return nsAddress;
+                }
+            }
+        }
+        // Return result of default implementation
+        return fetchNSAddr(true, 3000);
+    }
+
+    @Override
+    public void registerChangeCallBack(NameServerUpdateCallback changeCallBack) {
+        if (!topAddressingList.isEmpty()) {
+            for (TopAddressing topAddressing : topAddressingList) {
+                topAddressing.registerChangeCallBack(changeCallBack);
+            }
+        }
+    }
+
+    public final String fetchNSAddr(boolean verbose, long timeoutMills) {
+        String url = this.wsAddr;
+        try {
+            if (null != para && para.size() > 0) {
+                if (!UtilAll.isBlank(this.unitName)) {
+                    url = url + "-" + this.unitName + "?nofix=1&";
+                }
+                else {
+                    url = url + "?";
+                }
+                for (Map.Entry<String, String> entry : this.para.entrySet()) {
+                    url += entry.getKey() + "=" + entry.getValue() + "&";
+                }
+                url = url.substring(0, url.length() - 1);
+            }
+            else {
+                if (!UtilAll.isBlank(this.unitName)) {
+                    url = url + "-" + this.unitName + "?nofix=1";
+                }
+            }
+
+            HttpTinyClient.HttpResult result = HttpTinyClient.httpGet(url, null, null, "UTF-8", timeoutMills);
+            if (200 == result.code) {
+                String responseStr = result.content;
+                if (responseStr != null) {
+                    return clearNewLine(responseStr);
+                } else {
+                    LOGGER.error("fetch nameserver address is null");
+                }
+            } else {
+                LOGGER.error("fetch nameserver address failed. statusCode=" + result.code);
+            }
+        } catch (IOException e) {
+            if (verbose) {
+                LOGGER.error("fetch name server address exception", e);
+            }
+        }
+
+        if (verbose) {
+            String errorMsg =
+                "connect to " + url + " failed, maybe the domain name " + MixAll.getWSAddr() + " not bind in /etc/hosts";
+            errorMsg += FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL);
+
+            LOGGER.warn(errorMsg);
+        }
+        return null;
+    }
+
+    public String getNsAddr() {
+        return nsAddr;
+    }
+
+    public void setNsAddr(String nsAddr) {
+        this.nsAddr = nsAddr;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/namesrv/NameServerUpdateCallback.java
similarity index 84%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/namesrv/NameServerUpdateCallback.java
index 08091f4..67ce2b8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/namesrv/NameServerUpdateCallback.java
@@ -14,12 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.namesrv;
 
-package org.apache.rocketmq.common.message;
-
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+public interface NameServerUpdateCallback {
+    String onNameServerAddressChange(String namesrvAddress);
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/namesrv/NamesrvConfig.java b/common/src/main/java/org/apache/rocketmq/common/namesrv/NamesrvConfig.java
index f687d2c..002c956 100644
--- a/common/src/main/java/org/apache/rocketmq/common/namesrv/NamesrvConfig.java
+++ b/common/src/main/java/org/apache/rocketmq/common/namesrv/NamesrvConfig.java
@@ -35,6 +35,51 @@ public class NamesrvConfig {
     private String productEnvName = "center";
     private boolean clusterTest = false;
     private boolean orderMessageEnable = false;
+    private boolean returnOrderTopicConfigToBroker = true;
+    private volatile boolean remoteFaultTolerance = true;
+
+    /**
+     * Indicates the nums of thread to handle client requests, like GET_ROUTEINTO_BY_TOPIC.
+     */
+    private int clientRequestThreadPoolNums = 8;
+    /**
+     * Indicates the nums of thread to handle broker or operation requests, like REGISTER_BROKER.
+     */
+    private int defaultThreadPoolNums = 16;
+    /**
+     * Indicates the capacity of queue to hold client requests.
+     */
+    private int clientRequestThreadPoolQueueCapacity = 50000;
+    /**
+     * Indicates the capacity of queue to hold broker or operation requests.
+     */
+    private int defaultThreadPoolQueueCapacity = 10000;
+    /**
+     * Interval of periodic scanning for non-active broker;
+     */
+    private long scanNotActiveBrokerInterval = 5 * 1000;
+
+    private int unRegisterBrokerQueueCapacity = 3000;
+
+    /**
+     * Support acting master or not.
+     *
+     * The slave can be an acting master when master node is down to support following operations:
+     * 1. support lock/unlock message queue operation.
+     * 2. support searchOffset, query maxOffset/minOffset operation.
+     * 3. support query earliest msg store time.
+     */
+    private boolean supportActingMaster = false;
+
+    private volatile boolean enableAllTopicList = false;
+
+    public void setRemoteFaultTolerance(boolean remoteFaultTolerance) {
+        this.remoteFaultTolerance = remoteFaultTolerance;
+    }
+
+    public boolean isRemoteFaultTolerance() {
+        return remoteFaultTolerance;
+    }
 
     public boolean isOrderMessageEnable() {
         return orderMessageEnable;
@@ -83,4 +128,76 @@ public class NamesrvConfig {
     public void setConfigStorePath(final String configStorePath) {
         this.configStorePath = configStorePath;
     }
+
+    public boolean isReturnOrderTopicConfigToBroker() {
+        return returnOrderTopicConfigToBroker;
+    }
+
+    public void setReturnOrderTopicConfigToBroker(boolean returnOrderTopicConfigToBroker) {
+        this.returnOrderTopicConfigToBroker = returnOrderTopicConfigToBroker;
+    }
+
+    public int getClientRequestThreadPoolNums() {
+        return clientRequestThreadPoolNums;
+    }
+
+    public void setClientRequestThreadPoolNums(final int clientRequestThreadPoolNums) {
+        this.clientRequestThreadPoolNums = clientRequestThreadPoolNums;
+    }
+
+    public int getDefaultThreadPoolNums() {
+        return defaultThreadPoolNums;
+    }
+
+    public void setDefaultThreadPoolNums(final int defaultThreadPoolNums) {
+        this.defaultThreadPoolNums = defaultThreadPoolNums;
+    }
+
+    public int getClientRequestThreadPoolQueueCapacity() {
+        return clientRequestThreadPoolQueueCapacity;
+    }
+
+    public void setClientRequestThreadPoolQueueCapacity(final int clientRequestThreadPoolQueueCapacity) {
+        this.clientRequestThreadPoolQueueCapacity = clientRequestThreadPoolQueueCapacity;
+    }
+
+    public int getDefaultThreadPoolQueueCapacity() {
+        return defaultThreadPoolQueueCapacity;
+    }
+
+    public void setDefaultThreadPoolQueueCapacity(final int defaultThreadPoolQueueCapacity) {
+        this.defaultThreadPoolQueueCapacity = defaultThreadPoolQueueCapacity;
+    }
+
+    public long getScanNotActiveBrokerInterval() {
+        return scanNotActiveBrokerInterval;
+    }
+
+    public void setScanNotActiveBrokerInterval(long scanNotActiveBrokerInterval) {
+        this.scanNotActiveBrokerInterval = scanNotActiveBrokerInterval;
+    }
+
+    public int getUnRegisterBrokerQueueCapacity() {
+        return unRegisterBrokerQueueCapacity;
+    }
+
+    public void setUnRegisterBrokerQueueCapacity(final int unRegisterBrokerQueueCapacity) {
+        this.unRegisterBrokerQueueCapacity = unRegisterBrokerQueueCapacity;
+    }
+
+    public boolean isSupportActingMaster() {
+        return supportActingMaster;
+    }
+
+    public void setSupportActingMaster(final boolean supportActingMaster) {
+        this.supportActingMaster = supportActingMaster;
+    }
+
+    public boolean isEnableAllTopicList() {
+        return enableAllTopicList;
+    }
+
+    public void setEnableAllTopicList(boolean enableAllTopicList) {
+        this.enableAllTopicList = enableAllTopicList;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java b/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
index 88a4122..3ca1828 100644
--- a/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
+++ b/common/src/main/java/org/apache/rocketmq/common/namesrv/TopAddressing.java
@@ -14,94 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/**
- * $Id: TopAddressing.java 1831 2013-05-16 01:39:51Z vintagewang@apache.org $
- */
 package org.apache.rocketmq.common.namesrv;
 
-import java.io.IOException;
-import org.apache.rocketmq.common.MixAll;
-import org.apache.rocketmq.common.UtilAll;
-import org.apache.rocketmq.common.constant.LoggerName;
-import org.apache.rocketmq.common.help.FAQUrl;
-import org.apache.rocketmq.logging.InternalLogger;
-import org.apache.rocketmq.logging.InternalLoggerFactory;
-import org.apache.rocketmq.common.utils.HttpTinyClient;
-
-public class TopAddressing {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
-
-    private String nsAddr;
-    private String wsAddr;
-    private String unitName;
-
-    public TopAddressing(final String wsAddr) {
-        this(wsAddr, null);
-    }
-
-    public TopAddressing(final String wsAddr, final String unitName) {
-        this.wsAddr = wsAddr;
-        this.unitName = unitName;
-    }
-
-    private static String clearNewLine(final String str) {
-        String newString = str.trim();
-        int index = newString.indexOf("\r");
-        if (index != -1) {
-            return newString.substring(0, index);
-        }
-
-        index = newString.indexOf("\n");
-        if (index != -1) {
-            return newString.substring(0, index);
-        }
-
-        return newString;
-    }
-
-    public final String fetchNSAddr() {
-        return fetchNSAddr(true, 3000);
-    }
-
-    public final String fetchNSAddr(boolean verbose, long timeoutMills) {
-        String url = this.wsAddr;
-        try {
-            if (!UtilAll.isBlank(this.unitName)) {
-                url = url + "-" + this.unitName + "?nofix=1";
-            }
-            HttpTinyClient.HttpResult result = HttpTinyClient.httpGet(url, null, null, "UTF-8", timeoutMills);
-            if (200 == result.code) {
-                String responseStr = result.content;
-                if (responseStr != null) {
-                    return clearNewLine(responseStr);
-                } else {
-                    log.error("fetch nameserver address is null");
-                }
-            } else {
-                log.error("fetch nameserver address failed. statusCode=" + result.code);
-            }
-        } catch (IOException e) {
-            if (verbose) {
-                log.error("fetch name server address exception", e);
-            }
-        }
-
-        if (verbose) {
-            String errorMsg =
-                "connect to " + url + " failed, maybe the domain name " + MixAll.getWSAddr() + " not bind in /etc/hosts";
-            errorMsg += FAQUrl.suggestTodo(FAQUrl.NAME_SERVER_ADDR_NOT_EXIST_URL);
 
-            log.warn(errorMsg);
-        }
-        return null;
-    }
+public interface TopAddressing {
 
-    public String getNsAddr() {
-        return nsAddr;
-    }
+    String fetchNSAddr();
 
-    public void setNsAddr(String nsAddr) {
-        this.nsAddr = nsAddr;
-    }
+    void registerChangeCallBack(NameServerUpdateCallback changeCallBack);
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/ForbiddenType.java
similarity index 55%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/ForbiddenType.java
index ed8e7a3..1c661c2 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/ForbiddenType.java
@@ -14,25 +14,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
 
-import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+package org.apache.rocketmq.common.protocol;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String topic;
-
-    @Override
-    public void checkFields() throws RemotingCommandException {
-    }
+/**
+ * 
+ * gives the reason for a no permission messaging pulling.
+ *
+ */
+public interface ForbiddenType {
 
-    public String getTopic() {
-        return topic;
-    }
+    /**
+     * 1=forbidden by broker
+     */
+    int BROKER_FORBIDDEN               = 1;
+    /**
+     * 2=forbidden by groupId
+     */
+    int GROUP_FORBIDDEN                = 2;
+    /**
+     * 3=forbidden by topic
+     */
+    int TOPIC_FORBIDDEN                = 3;
+    /**
+     * 4=forbidden by brocasting mode
+     */
+    int BROADCASTING_DISABLE_FORBIDDEN = 4;
+    /**
+     * 5=forbidden for a substription(group with a topic)
+     */
+    int SUBSCRIPTION_FORBIDDEN         = 5;
 
-    public void setTopic(String topic) {
-        this.topic = topic;
-    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java b/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
index 534a2d2..541227f 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/RequestCode.java
@@ -70,6 +70,8 @@ public class RequestCode {
 
     public static final int CHECK_CLIENT_CONFIG = 46;
 
+    public static final int GET_CLIENT_CONFIG = 47;
+
     public static final int UPDATE_AND_CREATE_ACL_CONFIG = 50;
 
     public static final int DELETE_ACL_CONFIG = 51;
@@ -82,7 +84,10 @@ public class RequestCode {
 
     public static final int POP_MESSAGE = 200050;
     public static final int ACK_MESSAGE = 200051;
+    public static final int PEEK_MESSAGE = 200052;
     public static final int CHANGE_MESSAGE_INVISIBLETIME = 200053;
+    public static final int NOTIFICATION = 200054;
+    public static final int POLLING_INFO = 200055;
 
     public static final int PUT_KV_CONFIG = 100;
 
@@ -121,6 +126,7 @@ public class RequestCode {
     public static final int DELETE_TOPIC_IN_BROKER = 215;
 
     public static final int DELETE_TOPIC_IN_NAMESRV = 216;
+    public static final int REGISTER_TOPIC_IN_NAMESRV = 217;
     public static final int GET_KVLIST_BY_NAMESPACE = 219;
 
     public static final int RESET_CONSUMER_CLIENT_OFFSET = 220;
@@ -135,6 +141,9 @@ public class RequestCode {
 
     public static final int GET_TOPICS_BY_CLUSTER = 224;
 
+    public static final int QUERY_TOPICS_BY_CONSUMER = 343;
+    public static final int QUERY_SUBSCRIPTION_BY_CONSUMER = 345;
+
     public static final int REGISTER_FILTER_SERVER = 301;
     public static final int REGISTER_MESSAGE_FILTER_CLASS = 302;
 
@@ -195,11 +204,36 @@ public class RequestCode {
 
     public static final int ADD_WRITE_PERM_OF_BROKER = 327;
 
+
     public static final int GET_TOPIC_CONFIG = 351;
 
+    public static final int GET_SUBSCRIPTIONGROUP_CONFIG = 352;
+    public static final int UPDATE_AND_GET_GROUP_FORBIDDEN = 353;
+
+    public static final int LITE_PULL_MESSAGE = 361;
+
     public static final int QUERY_ASSIGNMENT = 400;
     public static final int SET_MESSAGE_REQUEST_MODE = 401;
 
     public static final int UPDATE_AND_CREATE_STATIC_TOPIC = 513;
 
+    /**
+     * Below request codes are used by broker container,
+     * these request codes are started with '9'.
+     */
+    public static final int GET_BROKER_MEMBER_GROUP = 901;
+
+    public static final int ADD_BROKER = 902;
+
+    public static final int REMOVE_BROKER = 903;
+
+    public static final int BROKER_HEARTBEAT = 904;
+
+    public static final int NOTIFY_MIN_BROKER_ID_CHANGE = 905;
+
+    public static final int EXCHANGE_BROKER_HA_INFO = 906;
+
+    public static final int GET_BROKER_HA_STATUS = 907;
+
+    public static final int RESET_MASTER_FLUSH_OFFSET = 908;
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java b/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
index 3f691b5..db2a275 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/ResponseCode.java
@@ -20,7 +20,6 @@ package org.apache.rocketmq.common.protocol;
 import org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode;
 
 public class ResponseCode extends RemotingSysResponseCode {
-    public static final int ASYNC_AND_RETURN_NULL = -2;
 
     public static final int FLUSH_DISK_TIMEOUT = 10;
 
@@ -85,6 +84,12 @@ public class ResponseCode extends RemotingSysResponseCode {
 
     public static final int POLLING_TIMEOUT = 210;
 
+    public static final int BROKER_NOT_EXIST = 211;
+
+    public static final int BROKER_DISPATCH_NOT_COMPLETE = 212;
+
+    public static final int FLOW_CONTROL = 215;
+
     public static final int NOT_LEADER_FOR_QUEUE = 501;
 
     public static final int RPC_UNKNOWN = -1000;
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/BrokerMemberGroup.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/BrokerMemberGroup.java
new file mode 100644
index 0000000..eb02f88
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/BrokerMemberGroup.java
@@ -0,0 +1,90 @@
+/*
+ * 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.protocol.body;
+
+import com.google.common.base.Objects;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BrokerMemberGroup {
+    private String cluster;
+    private String brokerName;
+    private Map<Long/* brokerId */, String/* broker address */> brokerAddrs;
+
+    // Provide default constructor for serializer
+    public BrokerMemberGroup() {
+        this.brokerAddrs = new HashMap<Long, String>();
+    }
+
+    public BrokerMemberGroup(final String cluster, final String brokerName) {
+        this.cluster = cluster;
+        this.brokerName = brokerName;
+        this.brokerAddrs = new HashMap<Long, String>();
+    }
+
+    public long minimumBrokerId() {
+        if (this.brokerAddrs.isEmpty()) {
+            return 0;
+        }
+        return Collections.min(brokerAddrs.keySet());
+    }
+
+    public String getCluster() {
+        return cluster;
+    }
+
+    public void setCluster(final String cluster) {
+        this.cluster = cluster;
+    }
+
+    public String getBrokerName() {
+        return brokerName;
+    }
+
+    public void setBrokerName(final String brokerName) {
+        this.brokerName = brokerName;
+    }
+
+    public Map<Long, String> getBrokerAddrs() {
+        return brokerAddrs;
+    }
+
+    public void setBrokerAddrs(final Map<Long, String> brokerAddrs) {
+        this.brokerAddrs = brokerAddrs;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        BrokerMemberGroup that = (BrokerMemberGroup) o;
+        return Objects.equal(cluster, that.cluster) &&
+            Objects.equal(brokerName, that.brokerName) &&
+            Objects.equal(brokerAddrs, that.brokerAddrs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(cluster, brokerName, brokerAddrs);
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
index a78ce55..70d6011 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
@@ -25,6 +25,7 @@ public class CheckClientRequestBody extends RemotingSerializable {
     private String clientId;
     private String group;
     private SubscriptionData subscriptionData;
+    private String namespace;
 
     public String getClientId() {
         return clientId;
@@ -49,4 +50,12 @@ public class CheckClientRequestBody extends RemotingSerializable {
     public void setSubscriptionData(SubscriptionData subscriptionData) {
         this.subscriptionData = subscriptionData;
     }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ClusterInfo.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ClusterInfo.java
index 222e51a..700cff5 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ClusterInfo.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ClusterInfo.java
@@ -19,29 +19,29 @@ package org.apache.rocketmq.common.protocol.body;
 
 import com.google.common.base.Objects;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Map;
 import java.util.List;
 import java.util.Set;
 import org.apache.rocketmq.common.protocol.route.BrokerData;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
 public class ClusterInfo extends RemotingSerializable {
-    private HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
-    private HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
+    private Map<String/* brokerName */, BrokerData> brokerAddrTable;
+    private Map<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
 
-    public HashMap<String, BrokerData> getBrokerAddrTable() {
+    public Map<String, BrokerData> getBrokerAddrTable() {
         return brokerAddrTable;
     }
 
-    public void setBrokerAddrTable(HashMap<String, BrokerData> brokerAddrTable) {
+    public void setBrokerAddrTable(Map<String, BrokerData> brokerAddrTable) {
         this.brokerAddrTable = brokerAddrTable;
     }
 
-    public HashMap<String, Set<String>> getClusterAddrTable() {
+    public Map<String, Set<String>> getClusterAddrTable() {
         return clusterAddrTable;
     }
 
-    public void setClusterAddrTable(HashMap<String, Set<String>> clusterAddrTable) {
+    public void setClusterAddrTable(Map<String, Set<String>> clusterAddrTable) {
         this.clusterAddrTable = clusterAddrTable;
     }
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java
index 5b08d78..1317d54 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java
@@ -19,11 +19,13 @@ package org.apache.rocketmq.common.protocol.body;
 
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import org.apache.rocketmq.common.DataVersion;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
 public class ConsumerOffsetSerializeWrapper extends RemotingSerializable {
     private ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
         new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
+    private DataVersion dataVersion;
 
     public ConcurrentMap<String, ConcurrentMap<Integer, Long>> getOffsetTable() {
         return offsetTable;
@@ -32,4 +34,12 @@ public class ConsumerOffsetSerializeWrapper extends RemotingSerializable {
     public void setOffsetTable(ConcurrentMap<String, ConcurrentMap<Integer, Long>> offsetTable) {
         this.offsetTable = offsetTable;
     }
+
+    public DataVersion getDataVersion() {
+        return dataVersion;
+    }
+
+    public void setDataVersion(DataVersion dataVersion) {
+        this.dataVersion = dataVersion;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerRunningInfo.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerRunningInfo.java
index 0543add..6b0355f 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerRunningInfo.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerRunningInfo.java
@@ -45,6 +45,8 @@ public class ConsumerRunningInfo extends RemotingSerializable {
 
     private TreeMap<String/* Topic */, ConsumeStatus> statusTable = new TreeMap<String, ConsumeStatus>();
 
+    private TreeMap<String, String> userConsumerInfo = new TreeMap<String, String>();
+
     private String jstack;
 
     public static boolean analyzeSubscription(final TreeMap<String/* clientId */, ConsumerRunningInfo> criTable) {
@@ -87,13 +89,14 @@ public class ConsumerRunningInfo extends RemotingSerializable {
                     prev = next.getValue();
                 }
 
-                if (prev != null) {
-
-                    if (prev.getSubscriptionSet().isEmpty()) {
-                        // Subscription empty!
-                        return false;
-                    }
-                }
+                // after consumer.unsubscribe , SubscriptionSet is Empty
+                //if (prev != null) {
+                //
+                //    if (prev.getSubscriptionSet().isEmpty()) {
+                //        // Subscription empty!
+                //        return false;
+                //    }
+                //}
             }
         }
 
@@ -193,6 +196,10 @@ public class ConsumerRunningInfo extends RemotingSerializable {
         this.statusTable = statusTable;
     }
 
+    public TreeMap<String, String> getUserConsumerInfo() {
+        return userConsumerInfo;
+    }
+
     public String formatString() {
         StringBuilder sb = new StringBuilder();
 
@@ -318,6 +325,16 @@ public class ConsumerRunningInfo extends RemotingSerializable {
             }
         }
 
+        if (this.userConsumerInfo != null) {
+            sb.append("\n\n#User Consume Info#\n");
+            Iterator<Entry<String, String>> it = this.userConsumerInfo.entrySet().iterator();
+            while (it.hasNext()) {
+                Entry<String, String> next = it.next();
+                String item = String.format("%-40s: %s%n", next.getKey(), next.getValue());
+                sb.append(item);
+            }
+        }
+
         if (this.jstack != null) {
             sb.append("\n\n#Consumer jstack#\n");
             sb.append(this.jstack);
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/GetBrokerMemberGroupResponseBody.java
similarity index 61%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/body/GetBrokerMemberGroupResponseBody.java
index 5b08d78..2762587 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/ConsumerOffsetSerializeWrapper.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/GetBrokerMemberGroupResponseBody.java
@@ -17,19 +17,17 @@
 
 package org.apache.rocketmq.common.protocol.body;
 
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
-public class ConsumerOffsetSerializeWrapper extends RemotingSerializable {
-    private ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
-        new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
+public class GetBrokerMemberGroupResponseBody extends RemotingSerializable {
+    // Contains the broker member info of the same broker group
+    private BrokerMemberGroup brokerMemberGroup;
 
-    public ConcurrentMap<String, ConcurrentMap<Integer, Long>> getOffsetTable() {
-        return offsetTable;
+    public BrokerMemberGroup getBrokerMemberGroup() {
+        return brokerMemberGroup;
     }
 
-    public void setOffsetTable(ConcurrentMap<String, ConcurrentMap<Integer, Long>> offsetTable) {
-        this.offsetTable = offsetTable;
+    public void setBrokerMemberGroup(final BrokerMemberGroup brokerMemberGroup) {
+        this.brokerMemberGroup = brokerMemberGroup;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/GetRemoteClientConfigBody.java
similarity index 64%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/body/GetRemoteClientConfigBody.java
index 08091f4..38b3b48 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/GetRemoteClientConfigBody.java
@@ -14,12 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.protocol.body;
 
-package org.apache.rocketmq.common.message;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+import java.util.ArrayList;
+import java.util.List;
+
+public class GetRemoteClientConfigBody extends RemotingSerializable {
+    private List<String> keys = new ArrayList<String>();
+
+    public List<String> getKeys() {
+        return keys;
+    }
+
+    public void setKeys(List<String> keys) {
+        this.keys = keys;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/HARuntimeInfo.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/HARuntimeInfo.java
new file mode 100644
index 0000000..8994b51
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/HARuntimeInfo.java
@@ -0,0 +1,188 @@
+/*
+ * 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.protocol.body;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+public class HARuntimeInfo extends RemotingSerializable {
+
+    private boolean master;
+    private long masterCommitLogMaxOffset;
+    private int inSyncSlaveNums;
+    private List<HAConnectionRuntimeInfo> haConnectionInfo = new ArrayList<HARuntimeInfo.HAConnectionRuntimeInfo>();
+    private HAClientRuntimeInfo haClientRuntimeInfo = new HAClientRuntimeInfo();
+
+    public boolean isMaster() {
+        return this.master;
+    }
+
+    public void setMaster(boolean master) {
+        this.master = master;
+    }
+
+    public long getMasterCommitLogMaxOffset() {
+        return this.masterCommitLogMaxOffset;
+    }
+
+    public void setMasterCommitLogMaxOffset(long masterCommitLogMaxOffset) {
+        this.masterCommitLogMaxOffset = masterCommitLogMaxOffset;
+    }
+
+    public int getInSyncSlaveNums() {
+        return this.inSyncSlaveNums;
+    }
+
+    public void setInSyncSlaveNums(int inSyncSlaveNums) {
+        this.inSyncSlaveNums = inSyncSlaveNums;
+    }
+
+    public List<HAConnectionRuntimeInfo> getHaConnectionInfo() {
+        return this.haConnectionInfo;
+    }
+
+    public void setHaConnectionInfo(List<HAConnectionRuntimeInfo> haConnectionInfo) {
+        this.haConnectionInfo = haConnectionInfo;
+    }
+
+    public HAClientRuntimeInfo getHaClientRuntimeInfo() {
+        return this.haClientRuntimeInfo;
+    }
+
+    public void setHaClientRuntimeInfo(HAClientRuntimeInfo haClientRuntimeInfo) {
+        this.haClientRuntimeInfo = haClientRuntimeInfo;
+    }
+
+    public static class HAConnectionRuntimeInfo extends RemotingSerializable {
+        private String addr;
+        private long slaveAckOffset;
+        private long diff;
+        private boolean inSync;
+        private long transferredByteInSecond;
+        private long transferFromWhere;
+
+        public String getAddr() {
+            return this.addr;
+        }
+
+        public void setAddr(String addr) {
+            this.addr = addr;
+        }
+
+        public long getSlaveAckOffset() {
+            return this.slaveAckOffset;
+        }
+
+        public void setSlaveAckOffset(long slaveAckOffset) {
+            this.slaveAckOffset = slaveAckOffset;
+        }
+
+        public long getDiff() {
+            return this.diff;
+        }
+
+        public void setDiff(long diff) {
+            this.diff = diff;
+        }
+
+        public boolean isInSync() {
+            return this.inSync;
+        }
+
+        public void setInSync(boolean inSync) {
+            this.inSync = inSync;
+        }
+
+        public long getTransferredByteInSecond() {
+            return this.transferredByteInSecond;
+        }
+
+        public void setTransferredByteInSecond(long transferredByteInSecond) {
+            this.transferredByteInSecond = transferredByteInSecond;
+        }
+
+        public long getTransferFromWhere() {
+            return transferFromWhere;
+        }
+
+        public void setTransferFromWhere(long transferFromWhere) {
+            this.transferFromWhere = transferFromWhere;
+        }
+    }
+
+    public static class HAClientRuntimeInfo extends RemotingSerializable {
+        private String masterAddr;
+        private long transferredByteInSecond;
+        private long maxOffset;
+        private long lastReadTimestamp;
+        private long lastWriteTimestamp;
+        private long masterFlushOffset;
+        private boolean isActivated = false;
+
+        public String getMasterAddr() {
+            return this.masterAddr;
+        }
+
+        public void setMasterAddr(String masterAddr) {
+            this.masterAddr = masterAddr;
+        }
+
+        public long getTransferredByteInSecond() {
+            return this.transferredByteInSecond;
+        }
+
+        public void setTransferredByteInSecond(long transferredByteInSecond) {
+            this.transferredByteInSecond = transferredByteInSecond;
+        }
+
+        public long getMaxOffset() {
+            return this.maxOffset;
+        }
+
+        public void setMaxOffset(long maxOffset) {
+            this.maxOffset = maxOffset;
+        }
+
+        public long getLastReadTimestamp() {
+            return this.lastReadTimestamp;
+        }
+
+        public void setLastReadTimestamp(long lastReadTimestamp) {
+            this.lastReadTimestamp = lastReadTimestamp;
+        }
+
+        public long getLastWriteTimestamp() {
+            return this.lastWriteTimestamp;
+        }
+
+        public void setLastWriteTimestamp(long lastWriteTimestamp) {
+            this.lastWriteTimestamp = lastWriteTimestamp;
+        }
+
+        public long getMasterFlushOffset() {
+            return masterFlushOffset;
+        }
+
+        public void setMasterFlushOffset(long masterFlushOffset) {
+            this.masterFlushOffset = masterFlushOffset;
+        }
+    }
+
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/LockBatchRequestBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/LockBatchRequestBody.java
index 480862b..92e4d4e 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/LockBatchRequestBody.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/LockBatchRequestBody.java
@@ -25,6 +25,7 @@ import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 public class LockBatchRequestBody extends RemotingSerializable {
     private String consumerGroup;
     private String clientId;
+    private boolean onlyThisBroker = false;
     private Set<MessageQueue> mqSet = new HashSet<MessageQueue>();
 
     public String getConsumerGroup() {
@@ -43,6 +44,14 @@ public class LockBatchRequestBody extends RemotingSerializable {
         this.clientId = clientId;
     }
 
+    public boolean isOnlyThisBroker() {
+        return onlyThisBroker;
+    }
+
+    public void setOnlyThisBroker(boolean onlyThisBroker) {
+        this.onlyThisBroker = onlyThisBroker;
+    }
+
     public Set<MessageQueue> getMqSet() {
         return mqSet;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/QuerySubscriptionResponseBody.java
similarity index 85%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/body/QuerySubscriptionResponseBody.java
index a78ce55..413f8c4 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/CheckClientRequestBody.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/QuerySubscriptionResponseBody.java
@@ -20,18 +20,18 @@ package org.apache.rocketmq.common.protocol.body;
 import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
-public class CheckClientRequestBody extends RemotingSerializable {
+public class QuerySubscriptionResponseBody extends RemotingSerializable {
 
-    private String clientId;
-    private String group;
     private SubscriptionData subscriptionData;
+    private String group;
+    private String topic;
 
-    public String getClientId() {
-        return clientId;
+    public SubscriptionData getSubscriptionData() {
+        return subscriptionData;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setSubscriptionData(SubscriptionData subscriptionData) {
+        this.subscriptionData = subscriptionData;
     }
 
     public String getGroup() {
@@ -42,11 +42,11 @@ public class CheckClientRequestBody extends RemotingSerializable {
         this.group = group;
     }
 
-    public SubscriptionData getSubscriptionData() {
-        return subscriptionData;
+    public String getTopic() {
+        return topic;
     }
 
-    public void setSubscriptionData(SubscriptionData subscriptionData) {
-        this.subscriptionData = subscriptionData;
+    public void setTopic(String topic) {
+        this.topic = topic;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/body/UnlockBatchRequestBody.java b/common/src/main/java/org/apache/rocketmq/common/protocol/body/UnlockBatchRequestBody.java
index baf4071..f71dafb 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/body/UnlockBatchRequestBody.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/body/UnlockBatchRequestBody.java
@@ -25,6 +25,7 @@ import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 public class UnlockBatchRequestBody extends RemotingSerializable {
     private String consumerGroup;
     private String clientId;
+    private boolean onlyThisBroker = false;
     private Set<MessageQueue> mqSet = new HashSet<MessageQueue>();
 
     public String getConsumerGroup() {
@@ -43,6 +44,14 @@ public class UnlockBatchRequestBody extends RemotingSerializable {
         this.clientId = clientId;
     }
 
+    public boolean isOnlyThisBroker() {
+        return onlyThisBroker;
+    }
+
+    public void setOnlyThisBroker(boolean onlyThisBroker) {
+        this.onlyThisBroker = onlyThisBroker;
+    }
+
     public Set<MessageQueue> getMqSet() {
         return mqSet;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/AddBrokerRequestHeader.java
similarity index 67%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/AddBrokerRequestHeader.java
index ed8e7a3..e38e350 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/AddBrokerRequestHeader.java
@@ -14,25 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
+import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String topic;
+public class AddBrokerRequestHeader implements CommandCustomHeader {
+    @CFNullable
+    private String configPath;
+
+
+    @Override public void checkFields() throws RemotingCommandException {
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
     }
 
-    public String getTopic() {
-        return topic;
+    public String getConfigPath() {
+        return configPath;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    public void setConfigPath(String configPath) {
+        this.configPath = configPath;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
index 1bd089d..7bad639 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
@@ -31,6 +31,12 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
     private String msgId;
     @CFNullable
     private String brokerName;
+    @CFNullable
+    private String topic;
+    @CFNullable
+    private Integer topicSysFlag;
+    @CFNullable
+    private Integer groupSysFlag;
 
     @Override
     public void checkFields() throws RemotingCommandException {
@@ -67,4 +73,28 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
     public void setMsgId(String msgId) {
         this.msgId = msgId;
     }
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public Integer getTopicSysFlag() {
+        return topicSysFlag;
+    }
+
+    public void setTopicSysFlag(Integer topicSysFlag) {
+        this.topicSysFlag = topicSysFlag;
+    }
+
+    public Integer getGroupSysFlag() {
+        return groupSysFlag;
+    }
+
+    public void setGroupSysFlag(Integer groupSysFlag) {
+        this.groupSysFlag = groupSysFlag;
+    }
 }
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 6591d77..8a33564 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,7 +25,7 @@ public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader
     @CFNotNull
     private String groupName;
 
-    private boolean removeOffset;
+    private boolean cleanOffset = false;
 
     @Override
     public void checkFields() throws RemotingCommandException {
@@ -39,11 +39,11 @@ public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader
         this.groupName = groupName;
     }
 
-    public boolean isRemoveOffset() {
-        return removeOffset;
+    public boolean isCleanOffset() {
+        return cleanOffset;
     }
 
-    public void setRemoveOffset(boolean removeOffset) {
-        this.removeOffset = removeOffset;
+    public void setCleanOffset(boolean cleanOffset) {
+        this.cleanOffset = cleanOffset;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoRequestHeader.java
similarity index 58%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoRequestHeader.java
index 1bd089d..4a59320 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoRequestHeader.java
@@ -18,53 +18,45 @@
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String consumerGroup;
+public class ExchangeHAInfoRequestHeader implements CommandCustomHeader {
     @CFNullable
-    private String clientId;
+    public String masterHaAddress;
+
     @CFNullable
-    private String msgId;
+    public Long masterFlushOffset;
+
     @CFNullable
-    private String brokerName;
+    public String masterAddress;
 
     @Override
     public void checkFields() throws RemotingCommandException {
-    }
-
-    public String getConsumerGroup() {
-        return consumerGroup;
-    }
 
-    public void setConsumerGroup(String consumerGroup) {
-        this.consumerGroup = consumerGroup;
     }
 
-    public String getBrokerName() {
-        return brokerName;
+    public String getMasterHaAddress() {
+        return masterHaAddress;
     }
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
+    public void setMasterHaAddress(String masterHaAddress) {
+        this.masterHaAddress = masterHaAddress;
     }
 
-    public String getClientId() {
-        return clientId;
+    public Long getMasterFlushOffset() {
+        return masterFlushOffset;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setMasterFlushOffset(Long masterFlushOffset) {
+        this.masterFlushOffset = masterFlushOffset;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public String getMasterAddress() {
+        return masterAddress;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setMasterAddress(String masterAddress) {
+        this.masterAddress = masterAddress;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoResponseHeader.java
similarity index 58%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoResponseHeader.java
index 1bd089d..7b6ea77 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ExchangeHAInfoResponseHeader.java
@@ -18,53 +18,45 @@
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String consumerGroup;
+public class ExchangeHAInfoResponseHeader implements CommandCustomHeader {
     @CFNullable
-    private String clientId;
+    public String masterHaAddress;
+
     @CFNullable
-    private String msgId;
+    public Long masterFlushOffset;
+
     @CFNullable
-    private String brokerName;
+    public String masterAddress;
 
     @Override
     public void checkFields() throws RemotingCommandException {
-    }
-
-    public String getConsumerGroup() {
-        return consumerGroup;
-    }
 
-    public void setConsumerGroup(String consumerGroup) {
-        this.consumerGroup = consumerGroup;
     }
 
-    public String getBrokerName() {
-        return brokerName;
+    public String getMasterHaAddress() {
+        return masterHaAddress;
     }
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
+    public void setMasterHaAddress(String masterHaAddress) {
+        this.masterHaAddress = masterHaAddress;
     }
 
-    public String getClientId() {
-        return clientId;
+    public Long getMasterFlushOffset() {
+        return masterFlushOffset;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setMasterFlushOffset(Long masterFlushOffset) {
+        this.masterFlushOffset = masterFlushOffset;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public String getMasterAddress() {
+        return masterAddress;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setMasterAddress(String masterAddress) {
+        this.masterAddress = masterAddress;
     }
 }
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/GetBrokerMemberGroupRequestHeader.java
similarity index 70%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/GetBrokerMemberGroupRequestHeader.java
index 6591d77..c111463 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/GetBrokerMemberGroupRequestHeader.java
@@ -21,29 +21,31 @@ import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader {
+public class GetBrokerMemberGroupRequestHeader implements CommandCustomHeader {
     @CFNotNull
-    private String groupName;
+    private String clusterName;
 
-    private boolean removeOffset;
+    @CFNotNull
+    private String brokerName;
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
+    public String getClusterName() {
+        return clusterName;
     }
 
-    public String getGroupName() {
-        return groupName;
+    public void setClusterName(final String clusterName) {
+        this.clusterName = clusterName;
     }
 
-    public void setGroupName(String groupName) {
-        this.groupName = groupName;
+    public String getBrokerName() {
+        return brokerName;
     }
 
-    public boolean isRemoveOffset() {
-        return removeOffset;
+    public void setBrokerName(final String brokerName) {
+        this.brokerName = brokerName;
     }
 
-    public void setRemoveOffset(boolean removeOffset) {
-        this.removeOffset = removeOffset;
+    @Override
+    public void checkFields() throws RemotingCommandException {
+
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
index e961af9..f58e050 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
@@ -22,6 +22,7 @@ package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
+import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
 public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
@@ -30,6 +31,15 @@ public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
     @CFNotNull
     private Integer queueId;
 
+    /**
+     * A message at committed offset has been dispatched from Topic to MessageQueue, so it can be consumed immediately,
+     * while a message at inflight offset is not visible for a consumer temporarily.
+     * Set this flag true if the max committed offset is needed, or false if the max inflight offset is preferred.
+     * The default value is true.
+     */
+    @CFNullable
+    private boolean committed = true;
+
     @Override
     public void checkFields() throws RemotingCommandException {
     }
@@ -54,4 +64,11 @@ public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
         this.queueId = queueId;
     }
 
+    public boolean isCommitted() {
+        return committed;
+    }
+
+    public void setCommitted(final boolean committed) {
+        this.committed = committed;
+    }
 }
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/GetSubscriptionGroupConfigRequestHeader.java
similarity index 71%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/DeleteSubscriptionGroupRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/GetSubscriptionGroupConfigRequestHeader.java
index 6591d77..8b0cfbf 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/GetSubscriptionGroupConfigRequestHeader.java
@@ -15,35 +15,35 @@
  * limitations under the License.
  */
 
+/**
+ * $Id: GetAllTopicConfigResponseHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
+ */
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteSubscriptionGroupRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String groupName;
-
-    private boolean removeOffset;
+public class GetSubscriptionGroupConfigRequestHeader implements CommandCustomHeader {
 
     @Override
     public void checkFields() throws RemotingCommandException {
     }
 
-    public String getGroupName() {
-        return groupName;
-    }
-
-    public void setGroupName(String groupName) {
-        this.groupName = groupName;
-    }
+    @CFNotNull
+    private String group;
 
-    public boolean isRemoveOffset() {
-        return removeOffset;
+    /**
+     * @return the group
+     */
+    public String getGroup() {
+        return group;
     }
 
-    public void setRemoveOffset(boolean removeOffset) {
-        this.removeOffset = removeOffset;
+    /**
+     * @param group the group to set
+     */
+    public void setGroup(String group) {
+        this.group = group;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/InitConsumerOffsetRequestHeader.java
similarity index 78%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/InitConsumerOffsetRequestHeader.java
index a2806e6..27a64ed 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/InitConsumerOffsetRequestHeader.java
@@ -14,22 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/**
- * $Id: GetRouteInfoRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
- */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class GetRouteInfoRequestHeader implements CommandCustomHeader {
-    @CFNotNull
+public class InitConsumerOffsetRequestHeader implements CommandCustomHeader {
+
     private String topic;
+    // @see ConsumeInitMode
+    private int initMode;
 
     @Override
     public void checkFields() throws RemotingCommandException {
+
     }
 
     public String getTopic() {
@@ -39,4 +37,12 @@ public class GetRouteInfoRequestHeader implements CommandCustomHeader {
     public void setTopic(String topic) {
         this.topic = topic;
     }
+
+    public int getInitMode() {
+        return initMode;
+    }
+
+    public void setInitMode(int initMode) {
+        this.initMode = initMode;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationRequestHeader.java
similarity index 62%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationRequestHeader.java
index 1bd089d..79db24e 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationRequestHeader.java
@@ -14,28 +14,38 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
+
+public class NotificationRequestHeader implements CommandCustomHeader {
     @CFNotNull
     private String consumerGroup;
-    @CFNullable
-    private String clientId;
-    @CFNullable
-    private String msgId;
-    @CFNullable
-    private String brokerName;
+    @CFNotNull
+    private String topic;
+    @CFNotNull
+    private int queueId;
+    @CFNotNull
+    private long pollTime;
+    @CFNotNull
+    private long bornTime;
 
+    @CFNotNull
     @Override
     public void checkFields() throws RemotingCommandException {
     }
 
+    public long getPollTime() {
+        return pollTime;
+    }
+
+    public void setPollTime(long pollTime) {
+        this.pollTime = pollTime;
+    }
+
     public String getConsumerGroup() {
         return consumerGroup;
     }
@@ -44,27 +54,31 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
         this.consumerGroup = consumerGroup;
     }
 
-    public String getBrokerName() {
-        return brokerName;
+    public long getBornTime() {
+        return bornTime;
     }
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
+    public void setBornTime(long bornTime) {
+        this.bornTime = bornTime;
     }
 
-    public String getClientId() {
-        return clientId;
+    public String getTopic() {
+        return topic;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setTopic(String topic) {
+        this.topic = topic;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public int getQueueId() {
+        if (queueId < 0) {
+            return -1;
+        }
+        return queueId;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setQueueId(int queueId) {
+        this.queueId = queueId;
     }
+
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationResponseHeader.java
similarity index 79%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationResponseHeader.java
index ed8e7a3..20c19d6 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotificationResponseHeader.java
@@ -14,25 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
+public class NotificationResponseHeader implements CommandCustomHeader {
+
+
     @CFNotNull
-    private String topic;
+    private boolean hasMsg = false;
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
+    public boolean isHasMsg() {
+        return hasMsg;
     }
 
-    public String getTopic() {
-        return topic;
+    public void setHasMsg(boolean hasMsg) {
+        this.hasMsg = hasMsg;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    @Override
+    public void checkFields() throws RemotingCommandException {
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotifyMinBrokerIdChangeRequestHeader.java
similarity index 59%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/NotifyMinBrokerIdChangeRequestHeader.java
index 1bd089d..c5ca4ef 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/NotifyMinBrokerIdChangeRequestHeader.java
@@ -18,30 +18,36 @@
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String consumerGroup;
+public class NotifyMinBrokerIdChangeRequestHeader implements CommandCustomHeader {
     @CFNullable
-    private String clientId;
-    @CFNullable
-    private String msgId;
+    private Long minBrokerId;
+
     @CFNullable
     private String brokerName;
 
+    @CFNullable
+    private String minBrokerAddr;
+
+    @CFNullable
+    private String offlineBrokerAddr;
+
+    @CFNullable
+    private String haBrokerAddr;
+
     @Override
     public void checkFields() throws RemotingCommandException {
+
     }
 
-    public String getConsumerGroup() {
-        return consumerGroup;
+    public Long getMinBrokerId() {
+        return minBrokerId;
     }
 
-    public void setConsumerGroup(String consumerGroup) {
-        this.consumerGroup = consumerGroup;
+    public void setMinBrokerId(Long minBrokerId) {
+        this.minBrokerId = minBrokerId;
     }
 
     public String getBrokerName() {
@@ -52,19 +58,27 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
         this.brokerName = brokerName;
     }
 
-    public String getClientId() {
-        return clientId;
+    public String getMinBrokerAddr() {
+        return minBrokerAddr;
+    }
+
+    public void setMinBrokerAddr(String minBrokerAddr) {
+        this.minBrokerAddr = minBrokerAddr;
+    }
+
+    public String getOfflineBrokerAddr() {
+        return offlineBrokerAddr;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setOfflineBrokerAddr(String offlineBrokerAddr) {
+        this.offlineBrokerAddr = offlineBrokerAddr;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public String getHaBrokerAddr() {
+        return haBrokerAddr;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setHaBrokerAddr(String haBrokerAddr) {
+        this.haBrokerAddr = haBrokerAddr;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PeekMessageRequestHeader.java
similarity index 66%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/PeekMessageRequestHeader.java
index 1bd089d..ba172f5 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PeekMessageRequestHeader.java
@@ -14,23 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
+public class PeekMessageRequestHeader implements CommandCustomHeader {
+    @CFNotNull
+    private String topic;
+    @CFNotNull
+    private int queueId;
+    @CFNotNull
+    private int maxMsgNums;
     @CFNotNull
     private String consumerGroup;
-    @CFNullable
-    private String clientId;
-    @CFNullable
-    private String msgId;
-    @CFNullable
-    private String brokerName;
 
     @Override
     public void checkFields() throws RemotingCommandException {
@@ -44,27 +42,29 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
         this.consumerGroup = consumerGroup;
     }
 
-    public String getBrokerName() {
-        return brokerName;
+    public String getTopic() {
+        return topic;
     }
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
+    public void setTopic(String topic) {
+        this.topic = topic;
     }
 
-    public String getClientId() {
-        return clientId;
+    public int getQueueId() {
+        return queueId;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setQueueId(int queueId) {
+        this.queueId = queueId;
     }
 
-    public String getMsgId() {
-        return msgId;
+
+    public int getMaxMsgNums() {
+        return maxMsgNums;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setMaxMsgNums(int maxMsgNums) {
+        this.maxMsgNums = maxMsgNums;
     }
+
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoRequestHeader.java
similarity index 72%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoRequestHeader.java
index e961af9..a57fa6e 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoRequestHeader.java
@@ -15,42 +15,49 @@
  * limitations under the License.
  */
 
-/**
- * $Id: GetMaxOffsetRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
- */
 package org.apache.rocketmq.common.protocol.header;
 
-import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
+import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
+
+public class PollingInfoRequestHeader implements CommandCustomHeader {
+    @CFNotNull
+    private String consumerGroup;
     @CFNotNull
     private String topic;
     @CFNotNull
-    private Integer queueId;
+    private int queueId;
 
     @Override
     public void checkFields() throws RemotingCommandException {
     }
 
-    @Override
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    public void setConsumerGroup(String consumerGroup) {
+        this.consumerGroup = consumerGroup;
+    }
+
     public String getTopic() {
         return topic;
     }
 
-    @Override
     public void setTopic(String topic) {
         this.topic = topic;
     }
 
-    @Override
-    public Integer getQueueId() {
+    public int getQueueId() {
+        if (queueId < 0) {
+            return -1;
+        }
         return queueId;
     }
 
-    @Override
-    public void setQueueId(Integer queueId) {
+    public void setQueueId(int queueId) {
         this.queueId = queueId;
     }
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoResponseHeader.java
similarity index 78%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoResponseHeader.java
index ed8e7a3..37962f9 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PollingInfoResponseHeader.java
@@ -14,25 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
+public class PollingInfoResponseHeader implements CommandCustomHeader {
+
+
     @CFNotNull
-    private String topic;
+    private int pollingNum;
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
+    public int getPollingNum() {
+        return pollingNum;
     }
 
-    public String getTopic() {
-        return topic;
+    public void setPollingNum(int pollingNum) {
+        this.pollingNum = pollingNum;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    @Override
+    public void checkFields() throws RemotingCommandException {
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageResponseHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageResponseHeader.java
index 88af984..84e6daf 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageResponseHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/PullMessageResponseHeader.java
@@ -36,6 +36,12 @@ public class PullMessageResponseHeader implements CommandCustomHeader {
     private Long maxOffset;
     @CFNullable
     private Long offsetDelta;
+    @CFNullable
+    private Integer topicSysFlag;
+    @CFNullable
+    private Integer groupSysFlag;
+    @CFNullable
+    private Integer forbiddenType;
 
     @Override
     public void checkFields() throws RemotingCommandException {
@@ -73,6 +79,33 @@ public class PullMessageResponseHeader implements CommandCustomHeader {
         this.suggestWhichBrokerId = suggestWhichBrokerId;
     }
 
+    public Integer getTopicSysFlag() {
+        return topicSysFlag;
+    }
+
+    public void setTopicSysFlag(Integer topicSysFlag) {
+        this.topicSysFlag = topicSysFlag;
+    }
+
+    public Integer getGroupSysFlag() {
+        return groupSysFlag;
+    }
+
+    public void setGroupSysFlag(Integer groupSysFlag) {
+        this.groupSysFlag = groupSysFlag;
+    }
+    /**
+     * @return the forbiddenType
+     */
+    public Integer getForbiddenType() {
+        return forbiddenType;
+    }
+    /**
+     */
+    public void setForbiddenType(Integer forbiddenType) {
+        this.forbiddenType = forbiddenType;
+    }
+
     public Long getOffsetDelta() {
         return offsetDelta;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QuerySubscriptionByConsumerRequestHeader.java
similarity index 77%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/QuerySubscriptionByConsumerRequestHeader.java
index a2806e6..94a5ae5 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QuerySubscriptionByConsumerRequestHeader.java
@@ -16,20 +16,30 @@
  */
 
 /**
- * $Id: GetRouteInfoRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
+ * $Id: QueryMessageRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class GetRouteInfoRequestHeader implements CommandCustomHeader {
+public class QuerySubscriptionByConsumerRequestHeader implements CommandCustomHeader {
     @CFNotNull
+    private String group;
     private String topic;
 
     @Override
     public void checkFields() throws RemotingCommandException {
+
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
     }
 
     public String getTopic() {
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryTopicsByConsumerRequestHeader.java
similarity index 76%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryTopicsByConsumerRequestHeader.java
index a2806e6..6a382d7 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/QueryTopicsByConsumerRequestHeader.java
@@ -16,27 +16,28 @@
  */
 
 /**
- * $Id: GetRouteInfoRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
+ * $Id: QueryMessageRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class GetRouteInfoRequestHeader implements CommandCustomHeader {
+public class QueryTopicsByConsumerRequestHeader implements CommandCustomHeader {
     @CFNotNull
-    private String topic;
+    private String group;
 
     @Override
     public void checkFields() throws RemotingCommandException {
+
     }
 
-    public String getTopic() {
-        return topic;
+    public String getGroup() {
+        return group;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    public void setGroup(String group) {
+        this.group = group;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/RemoveBrokerRequestHeader.java
similarity index 61%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/RemoveBrokerRequestHeader.java
index 1bd089d..05a8f11 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/RemoveBrokerRequestHeader.java
@@ -19,29 +19,18 @@ package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
+public class RemoveBrokerRequestHeader implements CommandCustomHeader {
     @CFNotNull
-    private String consumerGroup;
-    @CFNullable
-    private String clientId;
-    @CFNullable
-    private String msgId;
-    @CFNullable
     private String brokerName;
+    @CFNotNull
+    private String brokerClusterName;
+    @CFNotNull
+    private Long brokerId;
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
-    }
-
-    public String getConsumerGroup() {
-        return consumerGroup;
-    }
+    @Override public void checkFields() throws RemotingCommandException {
 
-    public void setConsumerGroup(String consumerGroup) {
-        this.consumerGroup = consumerGroup;
     }
 
     public String getBrokerName() {
@@ -52,19 +41,19 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
         this.brokerName = brokerName;
     }
 
-    public String getClientId() {
-        return clientId;
+    public String getBrokerClusterName() {
+        return brokerClusterName;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setBrokerClusterName(String brokerClusterName) {
+        this.brokerClusterName = brokerClusterName;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public Long getBrokerId() {
+        return brokerId;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setBrokerId(Long brokerId) {
+        this.brokerId = brokerId;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ResetMasterFlushOffsetHeader.java
similarity index 75%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/ResetMasterFlushOffsetHeader.java
index ed8e7a3..b511919 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/ResetMasterFlushOffsetHeader.java
@@ -14,25 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+
+package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
+public class ResetMasterFlushOffsetHeader implements CommandCustomHeader {
     @CFNotNull
-    private String topic;
+    private Long masterFlushOffset;
 
     @Override
     public void checkFields() throws RemotingCommandException {
+
     }
 
-    public String getTopic() {
-        return topic;
+    public Long getMasterFlushOffset() {
+        return masterFlushOffset;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    public void setMasterFlushOffset(Long masterFlushOffset) {
+        this.masterFlushOffset = masterFlushOffset;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/StatisticsMessagesRequestHeader.java
similarity index 63%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/StatisticsMessagesRequestHeader.java
index 1bd089d..e25e21a 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/ConsumeMessageDirectlyResultRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/StatisticsMessagesRequestHeader.java
@@ -19,18 +19,18 @@ package org.apache.rocketmq.common.protocol.header;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomHeader {
+public class StatisticsMessagesRequestHeader implements CommandCustomHeader {
     @CFNotNull
     private String consumerGroup;
-    @CFNullable
-    private String clientId;
-    @CFNullable
-    private String msgId;
-    @CFNullable
-    private String brokerName;
+    @CFNotNull
+    private String topic;
+    @CFNotNull
+    private int queueId;
+
+    private long fromTime;
+    private long toTime;
 
     @Override
     public void checkFields() throws RemotingCommandException {
@@ -44,27 +44,38 @@ public class ConsumeMessageDirectlyResultRequestHeader implements CommandCustomH
         this.consumerGroup = consumerGroup;
     }
 
-    public String getBrokerName() {
-        return brokerName;
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public int getQueueId() {
+        if (queueId < 0) {
+            return -1;
+        }
+        return queueId;
     }
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
+    public void setQueueId(int queueId) {
+        this.queueId = queueId;
     }
 
-    public String getClientId() {
-        return clientId;
+    public long getFromTime() {
+        return fromTime;
     }
 
-    public void setClientId(String clientId) {
-        this.clientId = clientId;
+    public void setFromTime(long fromTime) {
+        this.fromTime = fromTime;
     }
 
-    public String getMsgId() {
-        return msgId;
+    public long getToTime() {
+        return toTime;
     }
 
-    public void setMsgId(String msgId) {
-        this.msgId = msgId;
+    public void setToTime(long toTime) {
+        this.toTime = toTime;
     }
-}
+}
\ No newline at end of file
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/UpdateGroupForbiddenRequestHeader.java
similarity index 68%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/UpdateGroupForbiddenRequestHeader.java
index e961af9..902b4ac 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/GetMaxOffsetRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/UpdateGroupForbiddenRequestHeader.java
@@ -16,42 +16,49 @@
  */
 
 /**
- * $Id: GetMaxOffsetRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
+ * $Id: CreateTopicRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
  */
 package org.apache.rocketmq.common.protocol.header;
 
-import org.apache.rocketmq.common.rpc.TopicQueueRequestHeader;
+import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class GetMaxOffsetRequestHeader extends TopicQueueRequestHeader {
+public class UpdateGroupForbiddenRequestHeader implements CommandCustomHeader {
     @CFNotNull
-    private String topic;
+    private String  group;
     @CFNotNull
-    private Integer queueId;
+    private String  topic;
+
+    private Boolean readable;
 
     @Override
     public void checkFields() throws RemotingCommandException {
+
     }
 
-    @Override
     public String getTopic() {
         return topic;
     }
 
-    @Override
     public void setTopic(String topic) {
         this.topic = topic;
     }
 
-    @Override
-    public Integer getQueueId() {
-        return queueId;
+    public String getGroup() {
+        return group;
     }
 
-    @Override
-    public void setQueueId(Integer queueId) {
-        this.queueId = queueId;
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public Boolean getReadable() {
+        return readable;
+    }
+
+    public void setReadable(Boolean readable) {
+        this.readable = readable;
     }
 
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/BrokerHeartbeatRequestHeader.java
similarity index 61%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/BrokerHeartbeatRequestHeader.java
index 19175b0..e21ef90 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/BrokerHeartbeatRequestHeader.java
@@ -15,40 +15,22 @@
  * limitations under the License.
  */
 
-/**
- * $Id: RegisterBrokerRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
- */
 package org.apache.rocketmq.common.protocol.header.namesrv;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class RegisterBrokerRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String brokerName;
-    @CFNotNull
-    private String brokerAddr;
+public class BrokerHeartbeatRequestHeader implements CommandCustomHeader {
     @CFNotNull
     private String clusterName;
     @CFNotNull
-    private String haServerAddr;
+    private String brokerAddr;
     @CFNotNull
-    private Long brokerId;
-
-    private boolean compressed;
-
-    private Integer bodyCrc32 = 0;
-
-    public void checkFields() throws RemotingCommandException {
-    }
+    private String brokerName;
 
-    public String getBrokerName() {
-        return brokerName;
-    }
+    @Override public void checkFields() throws RemotingCommandException {
 
-    public void setBrokerName(String brokerName) {
-        this.brokerName = brokerName;
     }
 
     public String getBrokerAddr() {
@@ -67,35 +49,11 @@ public class RegisterBrokerRequestHeader implements CommandCustomHeader {
         this.clusterName = clusterName;
     }
 
-    public String getHaServerAddr() {
-        return haServerAddr;
-    }
-
-    public void setHaServerAddr(String haServerAddr) {
-        this.haServerAddr = haServerAddr;
-    }
-
-    public Long getBrokerId() {
-        return brokerId;
-    }
-
-    public void setBrokerId(Long brokerId) {
-        this.brokerId = brokerId;
-    }
-
-    public boolean isCompressed() {
-        return compressed;
-    }
-
-    public void setCompressed(boolean compressed) {
-        this.compressed = compressed;
-    }
-
-    public Integer getBodyCrc32() {
-        return bodyCrc32;
+    public String getBrokerName() {
+        return brokerName;
     }
 
-    public void setBodyCrc32(Integer bodyCrc32) {
-        this.bodyCrc32 = bodyCrc32;
+    public void setBrokerName(String brokerName) {
+        this.brokerName = brokerName;
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
index ed8e7a3..c9dd04b 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
@@ -24,6 +24,8 @@ public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader
     @CFNotNull
     private String topic;
 
+    private String clusterName;
+
     @Override
     public void checkFields() throws RemotingCommandException {
     }
@@ -35,4 +37,12 @@ public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader
     public void setTopic(String topic) {
         this.topic = topic;
     }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public void setClusterName(String clusterName) {
+        this.clusterName = clusterName;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
index a2806e6..d1f5ca8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
@@ -25,6 +25,8 @@ import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
 public class GetRouteInfoRequestHeader implements CommandCustomHeader {
+    public static char split = 1;
+
     @CFNotNull
     private String topic;
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java
index 19175b0..b2c7884 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterBrokerRequestHeader.java
@@ -22,6 +22,7 @@ package org.apache.rocketmq.common.protocol.header.namesrv;
 
 import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
+import org.apache.rocketmq.remoting.annotation.CFNullable;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
 public class RegisterBrokerRequestHeader implements CommandCustomHeader {
@@ -35,11 +36,16 @@ public class RegisterBrokerRequestHeader implements CommandCustomHeader {
     private String haServerAddr;
     @CFNotNull
     private Long brokerId;
+    @CFNullable
+    private Long heartbeatTimeoutMillis;
+    @CFNullable
+    private Boolean enableActingMaster;
 
     private boolean compressed;
 
     private Integer bodyCrc32 = 0;
 
+    @Override
     public void checkFields() throws RemotingCommandException {
     }
 
@@ -83,6 +89,14 @@ public class RegisterBrokerRequestHeader implements CommandCustomHeader {
         this.brokerId = brokerId;
     }
 
+    public Long getHeartbeatTimeoutMillis() {
+        return heartbeatTimeoutMillis;
+    }
+
+    public void setHeartbeatTimeoutMillis(Long heartbeatTimeoutMillis) {
+        this.heartbeatTimeoutMillis = heartbeatTimeoutMillis;
+    }
+
     public boolean isCompressed() {
         return compressed;
     }
@@ -98,4 +112,12 @@ public class RegisterBrokerRequestHeader implements CommandCustomHeader {
     public void setBodyCrc32(Integer bodyCrc32) {
         this.bodyCrc32 = bodyCrc32;
     }
+
+    public Boolean getEnableActingMaster() {
+        return enableActingMaster;
+    }
+
+    public void setEnableActingMaster(Boolean enableActingMaster) {
+        this.enableActingMaster = enableActingMaster;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterTopicRequestHeader.java
similarity index 94%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterTopicRequestHeader.java
index ed8e7a3..ff19b28 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/RegisterTopicRequestHeader.java
@@ -20,7 +20,7 @@ import org.apache.rocketmq.remoting.CommandCustomHeader;
 import org.apache.rocketmq.remoting.annotation.CFNotNull;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
+public class RegisterTopicRequestHeader implements CommandCustomHeader {
     @CFNotNull
     private String topic;
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
index 36599fb..73d725c 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/route/BrokerData.java
@@ -20,6 +20,7 @@ package org.apache.rocketmq.common.protocol.route;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Random;
 import org.apache.rocketmq.common.MixAll;
 
@@ -30,10 +31,27 @@ public class BrokerData implements Comparable<BrokerData> {
 
     private final Random random = new Random();
 
+    /**
+     * Enable acting master or not, used for old version HA adaption,
+     */
+    private boolean enableActingMaster = false;
+
     public BrokerData() {
 
     }
 
+    public BrokerData(BrokerData brokerData) {
+        this.cluster = brokerData.cluster;
+        this.brokerName = brokerData.brokerName;
+        if (brokerData.brokerAddrs != null) {
+            this.brokerAddrs = new HashMap<Long, String>();
+            for (final Map.Entry<Long, String> brokerEntry : brokerData.brokerAddrs.entrySet()) {
+                this.brokerAddrs.put(brokerEntry.getKey(), brokerEntry.getValue());
+            }
+        }
+        this.enableActingMaster = brokerData.enableActingMaster;
+    }
+
     public BrokerData(String cluster, String brokerName, HashMap<Long, String> brokerAddrs) {
         this.cluster = cluster;
         this.brokerName = brokerName;
@@ -73,6 +91,14 @@ public class BrokerData implements Comparable<BrokerData> {
         this.cluster = cluster;
     }
 
+    public boolean isEnableActingMaster() {
+        return enableActingMaster;
+    }
+
+    public void setEnableActingMaster(boolean enableActingMaster) {
+        this.enableActingMaster = enableActingMaster;
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -84,29 +110,33 @@ public class BrokerData implements Comparable<BrokerData> {
 
     @Override
     public boolean equals(Object obj) {
-        if (this == obj)
+        if (this == obj) {
             return true;
-        if (obj == null)
+        }
+        if (obj == null) {
             return false;
-        if (getClass() != obj.getClass())
+        }
+        if (getClass() != obj.getClass()) {
             return false;
+        }
         BrokerData other = (BrokerData) obj;
         if (brokerAddrs == null) {
-            if (other.brokerAddrs != null)
+            if (other.brokerAddrs != null) {
                 return false;
-        } else if (!brokerAddrs.equals(other.brokerAddrs))
+            }
+        } else if (!brokerAddrs.equals(other.brokerAddrs)) {
             return false;
+        }
         if (brokerName == null) {
-            if (other.brokerName != null)
-                return false;
-        } else if (!brokerName.equals(other.brokerName))
-            return false;
-        return true;
+            return other.brokerName == null;
+        } else {
+            return brokerName.equals(other.brokerName);
+        }
     }
 
     @Override
     public String toString() {
-        return "BrokerData [brokerName=" + brokerName + ", brokerAddrs=" + brokerAddrs + "]";
+        return "BrokerData [brokerName=" + brokerName + ", brokerAddrs=" + brokerAddrs + ", enableActingMaster=" + enableActingMaster + "]";
     }
 
     @Override
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 2dbb290..6e9e653 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
@@ -27,6 +27,19 @@ public class QueueData implements Comparable<QueueData> {
     private int perm;
     private int topicSysFlag;
 
+    public QueueData() {
+
+    }
+
+    // Deep copy QueueData
+    public QueueData(QueueData queueData) {
+        this.brokerName = queueData.brokerName;
+        this.readQueueNums = queueData.readQueueNums;
+        this.writeQueueNums = queueData.writeQueueNums;
+        this.perm = queueData.perm;
+        this.topicSysFlag = queueData.topicSysFlag;
+    }
+
     public int getReadQueueNums() {
         return readQueueNums;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteData.java b/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteData.java
index 3fe4630..0c02078 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteData.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteData.java
@@ -25,6 +25,8 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import org.apache.rocketmq.common.statictopic.TopicQueueMappingInfo;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
@@ -37,6 +39,9 @@ public class TopicRouteData extends RemotingSerializable {
     private Map<String/*brokerName*/, TopicQueueMappingInfo> topicQueueMappingByBroker;
 
     public TopicRouteData() {
+        queueDatas = new ArrayList<QueueData>();
+        brokerDatas = new ArrayList<BrokerData>();
+        filterServerTable = new HashMap<String, List<String>>();
     }
 
     public TopicRouteData(TopicRouteData topicRouteData) {
@@ -62,6 +67,54 @@ public class TopicRouteData extends RemotingSerializable {
         }
     }
 
+    public TopicRouteData cloneTopicRouteData() {
+        TopicRouteData topicRouteData = new TopicRouteData();
+
+        topicRouteData.setOrderTopicConf(this.orderTopicConf);
+
+        topicRouteData.getQueueDatas().addAll(this.queueDatas);
+        topicRouteData.getBrokerDatas().addAll(this.brokerDatas);
+        topicRouteData.getFilterServerTable().putAll(this.filterServerTable);
+        if (this.topicQueueMappingByBroker != null) {
+            Map<String, TopicQueueMappingInfo> cloneMap = new HashMap<>(this.topicQueueMappingByBroker);
+            topicRouteData.setTopicQueueMappingByBroker(cloneMap);
+        }
+        return topicRouteData;
+    }
+
+    public TopicRouteData deepCloneTopicRouteData() {
+        TopicRouteData topicRouteData = new TopicRouteData();
+
+        topicRouteData.setOrderTopicConf(this.orderTopicConf);
+
+        for (final QueueData queueData : this.queueDatas) {
+            topicRouteData.getQueueDatas().add(new QueueData(queueData));
+        }
+
+        for (final BrokerData brokerData : this.brokerDatas) {
+            topicRouteData.getBrokerDatas().add(new BrokerData(brokerData));
+        }
+
+        for (final Map.Entry<String, List<String>> listEntry : this.filterServerTable.entrySet()) {
+            topicRouteData.getFilterServerTable().put(listEntry.getKey(),
+                new ArrayList<String>(listEntry.getValue()));
+        }
+        if (this.topicQueueMappingByBroker != null) {
+            Map<String, TopicQueueMappingInfo> cloneMap = new HashMap<>(this.topicQueueMappingByBroker.size());
+            for (final Map.Entry<String, TopicQueueMappingInfo> entry : this.getTopicQueueMappingByBroker().entrySet()) {
+                TopicQueueMappingInfo topicQueueMappingInfo = new TopicQueueMappingInfo(entry.getValue().getTopic(), entry.getValue().getTotalQueues(), entry.getValue().getBname(), entry.getValue().getEpoch());
+                topicQueueMappingInfo.setDirty(entry.getValue().isDirty());
+                topicQueueMappingInfo.setScope(entry.getValue().getScope());
+                ConcurrentMap<Integer, Integer> concurrentMap = new ConcurrentHashMap<Integer, Integer>(entry.getValue().getCurrIdMap());
+                topicQueueMappingInfo.setCurrIdMap(concurrentMap);
+                cloneMap.put(entry.getKey(), topicQueueMappingInfo);
+            }
+            topicRouteData.setTopicQueueMappingByBroker(cloneMap);
+        }
+
+        return topicRouteData;
+    }
+
     public List<QueueData> getQueueDatas() {
         return queueDatas;
     }
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteDatas.java
similarity index 61%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteDatas.java
index 08091f4..42a66b6 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/protocol/route/TopicRouteDatas.java
@@ -15,11 +15,22 @@
  * limitations under the License.
  */
 
-package org.apache.rocketmq.common.message;
+package org.apache.rocketmq.common.protocol.route;
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TopicRouteDatas extends RemotingSerializable {
+
+    private Map<String, TopicRouteData> topics = new HashMap<String, TopicRouteData>();
+
+    public Map<String, TopicRouteData> getTopics() {
+        return topics;
+    }
+
+    public void setTopics(Map<String, TopicRouteData> topics) {
+        this.topics = topics;
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/FutureHolder.java b/common/src/main/java/org/apache/rocketmq/common/statistics/FutureHolder.java
new file mode 100644
index 0000000..6356101
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/FutureHolder.java
@@ -0,0 +1,53 @@
+/*
+ * 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.statistics;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class FutureHolder<T> {
+    private ConcurrentMap<T, BlockingQueue<Future>> futureMap = new ConcurrentHashMap<T, BlockingQueue<Future>>(8);
+
+    public void addFuture(T t, Future future) {
+        BlockingQueue<Future> list = futureMap.get(t);
+        if (list == null) {
+            list = new LinkedBlockingQueue<Future>();
+            BlockingQueue<Future> old = futureMap.putIfAbsent(t, list);
+            if (old != null) {
+                list = old;
+            }
+        }
+        list.add(future);
+    }
+
+    public void removeAllFuture(T t) {
+        cancelAll(t, false);
+        futureMap.remove(t);
+    }
+
+    private void cancelAll(T t, boolean mayInterruptIfRunning) {
+        BlockingQueue<Future> list = futureMap.get(t);
+        if (list != null) {
+            for (Future future : list) {
+                future.cancel(mayInterruptIfRunning);
+            }
+        }
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/statistics/Interceptor.java
similarity index 78%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/statistics/Interceptor.java
index 08091f4..0af55e0 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/Interceptor.java
@@ -14,12 +14,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.statistics;
 
-package org.apache.rocketmq.common.message;
+/**
+ * interceptor
+ */
+public interface Interceptor {
+    /**
+     * increase multiple values
+     *
+     * @param deltas
+     */
+    void inc(long... deltas);
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+    void reset();
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBrief.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBrief.java
new file mode 100644
index 0000000..970bff9
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBrief.java
@@ -0,0 +1,184 @@
+/*
+ * 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.statistics;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class StatisticsBrief {
+    public static final int META_RANGE_INDEX = 0;
+    public static final int META_SLOT_NUM_INDEX = 1;
+
+    // TopPercentile
+    private long[][] topPercentileMeta;
+    private AtomicInteger[] counts;
+    private AtomicLong totalCount;
+
+    // max min avg total
+    private long max;
+    private long min;
+    private long total;
+
+    public StatisticsBrief(long[][] topPercentileMeta) {
+        if (!isLegalMeta(topPercentileMeta)) {
+            throw new IllegalArgumentException("illegal topPercentileMeta");
+        }
+
+        this.topPercentileMeta = topPercentileMeta;
+        this.counts = new AtomicInteger[slotNum(topPercentileMeta)];
+        this.totalCount = new AtomicLong(0);
+        reset();
+    }
+
+    public void reset() {
+        for (int i = 0; i < counts.length; i++) {
+            if (counts[i] == null) {
+                counts[i] = new AtomicInteger(0);
+            } else {
+                counts[i].set(0);
+            }
+        }
+        totalCount.set(0);
+
+        synchronized (this) {
+            max = 0;
+            min = Long.MAX_VALUE;
+            total = 0;
+        }
+    }
+
+    private static boolean isLegalMeta(long[][] meta) {
+        if (ArrayUtils.isEmpty(meta)) {
+            return false;
+        }
+
+        for (long[] line : meta) {
+            if (ArrayUtils.isEmpty(line) || line.length != 2) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static int slotNum(long[][] meta) {
+        int ret = 1;
+        for (long[] line : meta) {
+            ret += line[META_SLOT_NUM_INDEX];
+        }
+        return ret;
+    }
+
+    public void sample(long value) {
+        int index = getSlotIndex(value);
+        counts[index].incrementAndGet();
+        totalCount.incrementAndGet();
+
+        synchronized (this) {
+            max = Math.max(max, value);
+            min = Math.min(min, value);
+            total += value;
+        }
+    }
+
+    public long tp999() {
+        return getTPValue(0.999f);
+    }
+
+    public long getTPValue(float ratio) {
+        if (ratio <= 0 || ratio >= 1) {
+            ratio = 0.99f;
+        }
+        long count = totalCount.get();
+        long excludes = (long)(count - count * ratio);
+        if (excludes == 0) {
+            return getMax();
+        }
+
+        int tmp = 0;
+        for (int i = counts.length - 1; i > 0; i--) {
+            tmp += counts[i].get();
+            if (tmp > excludes) {
+                return Math.min(getSlotTPValue(i), getMax());
+            }
+        }
+        return 0;
+    }
+
+    private long getSlotTPValue(int index) {
+        int slotNumLeft = index;
+        for (int i = 0; i < topPercentileMeta.length; i++) {
+            int slotNum = (int)topPercentileMeta[i][META_SLOT_NUM_INDEX];
+            if (slotNumLeft < slotNum) {
+                long metaRangeMax = topPercentileMeta[i][META_RANGE_INDEX];
+                long metaRangeMin = 0;
+                if (i > 0) {
+                    metaRangeMin = topPercentileMeta[i - 1][META_RANGE_INDEX];
+                }
+
+                return metaRangeMin + (metaRangeMax - metaRangeMin) / slotNum * (slotNumLeft + 1);
+            } else {
+                slotNumLeft -= slotNum;
+            }
+        }
+        // MAX_VALUE: the last slot
+        return Integer.MAX_VALUE;
+    }
+
+    private int getSlotIndex(long num) {
+        int index = 0;
+        for (int i = 0; i < topPercentileMeta.length; i++) {
+            long rangeMax = topPercentileMeta[i][META_RANGE_INDEX];
+            int slotNum = (int)topPercentileMeta[i][META_SLOT_NUM_INDEX];
+            long rangeMin = (i > 0) ? topPercentileMeta[i - 1][META_RANGE_INDEX] : 0;
+            if (rangeMin <= num && num < rangeMax) {
+                index += (num - rangeMin) / ((rangeMax - rangeMin) / slotNum);
+                break;
+            }
+
+            index += slotNum;
+        }
+        return index;
+    }
+
+    /**
+     * Getters
+     *
+     * @return
+     */
+    public long getMax() {
+        return max;
+    }
+
+    public long getMin() {
+        return totalCount.get() > 0 ? min : 0;
+    }
+
+    public long getTotal() {
+        return total;
+    }
+
+    public long getCnt() {
+        return totalCount.get();
+    }
+
+    public double getAvg() {
+        return totalCount.get() != 0 ? ((double)total) / totalCount.get() : 0;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBriefInterceptor.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBriefInterceptor.java
new file mode 100644
index 0000000..b0b6937
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsBriefInterceptor.java
@@ -0,0 +1,76 @@
+/*
+ * 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.statistics;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * interceptor to generate statistics brief
+ */
+public class StatisticsBriefInterceptor implements Interceptor {
+    private int[] indexOfItems;
+
+    private StatisticsBrief[] statisticsBriefs;
+
+    public StatisticsBriefInterceptor(StatisticsItem item, Pair<String, long[][]>[] briefMetas) {
+        indexOfItems = new int[briefMetas.length];
+        statisticsBriefs = new StatisticsBrief[briefMetas.length];
+        for (int i = 0; i < briefMetas.length; i++) {
+            String name = briefMetas[i].getKey();
+            int index = ArrayUtils.indexOf(item.getItemNames(), name);
+            if (index < 0) {
+                throw new IllegalArgumentException("illegal breifItemName: " + name);
+            }
+            indexOfItems[i] = index;
+            statisticsBriefs[i] = new StatisticsBrief(briefMetas[i].getValue());
+        }
+    }
+
+    @Override
+    public void inc(long... itemValues) {
+        for (int i = 0; i < indexOfItems.length; i++) {
+            int indexOfItem = indexOfItems[i];
+            if (indexOfItem < itemValues.length) {
+                statisticsBriefs[i].sample(itemValues[indexOfItem]);
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        for (StatisticsBrief brief : statisticsBriefs) {
+            brief.reset();
+        }
+    }
+
+    public int[] getIndexOfItems() {
+        return indexOfItems;
+    }
+
+    public void setIndexOfItems(int[] indexOfItems) {
+        this.indexOfItems = indexOfItems;
+    }
+
+    public StatisticsBrief[] getStatisticsBriefs() {
+        return statisticsBriefs;
+    }
+
+    public void setStatisticsBriefs(StatisticsBrief[] statisticsBriefs) {
+        this.statisticsBriefs = statisticsBriefs;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItem.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItem.java
new file mode 100644
index 0000000..23633dc
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItem.java
@@ -0,0 +1,175 @@
+/*
+ * 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.statistics;
+
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Statistics Item
+ */
+public class StatisticsItem {
+    private String statKind;
+    private String statObject;
+
+    private String[] itemNames;
+    private AtomicLong[] itemAccumulates;
+    private AtomicLong invokeTimes;
+
+    private Interceptor interceptor;
+
+    /**
+     * last timestamp when the item was updated
+     */
+    private AtomicLong lastTimeStamp;
+
+    public StatisticsItem(String statKind, String statObject, String... itemNames) {
+        if (itemNames == null || itemNames.length <= 0) {
+            throw new InvalidParameterException("StatisticsItem \"itemNames\" is empty");
+        }
+
+        this.statKind = statKind;
+        this.statObject = statObject;
+        this.itemNames = itemNames;
+
+        AtomicLong[] accs = new AtomicLong[itemNames.length];
+        for (int i = 0; i < itemNames.length; i++) {
+            accs[i] = new AtomicLong(0);
+        }
+
+        this.itemAccumulates = accs;
+        this.invokeTimes = new AtomicLong();
+        this.lastTimeStamp = new AtomicLong(System.currentTimeMillis());
+    }
+
+    public void incItems(long... itemIncs) {
+        int len = Math.min(itemIncs.length, itemAccumulates.length);
+        for (int i = 0; i < len; i++) {
+            itemAccumulates[i].addAndGet(itemIncs[i]);
+        }
+
+        invokeTimes.addAndGet(1);
+        lastTimeStamp.set(System.currentTimeMillis());
+
+        if (interceptor != null) {
+            interceptor.inc(itemIncs);
+        }
+    }
+
+    public boolean allZeros() {
+        if (invokeTimes.get() == 0) {
+            return true;
+        }
+
+        for (AtomicLong acc : itemAccumulates) {
+            if (acc.get() != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public String getStatKind() {
+        return statKind;
+    }
+
+    public String getStatObject() {
+        return statObject;
+    }
+
+    public String[] getItemNames() {
+        return itemNames;
+    }
+
+    public AtomicLong[] getItemAccumulates() {
+        return itemAccumulates;
+    }
+
+    public AtomicLong getInvokeTimes() {
+        return invokeTimes;
+    }
+
+    public AtomicLong getLastTimeStamp() {
+        return lastTimeStamp;
+    }
+
+    public AtomicLong getItemAccumulate(String itemName) {
+        int index = ArrayUtils.indexOf(itemNames, itemName);
+        if (index < 0) {
+            return new AtomicLong(0);
+        }
+        return itemAccumulates[index];
+    }
+
+    /**
+     * get snapshot
+     * <p>
+     * Warning: no guarantee of itemAccumulates consistency
+     *
+     * @return
+     */
+    public StatisticsItem snapshot() {
+        StatisticsItem ret = new StatisticsItem(statKind, statObject, itemNames);
+
+        ret.itemAccumulates = new AtomicLong[itemAccumulates.length];
+        for (int i = 0; i < itemAccumulates.length; i++) {
+            ret.itemAccumulates[i] = new AtomicLong(itemAccumulates[i].get());
+        }
+
+        ret.invokeTimes = new AtomicLong(invokeTimes.longValue());
+        ret.lastTimeStamp = new AtomicLong(lastTimeStamp.longValue());
+
+        return ret;
+    }
+
+    /**
+     * subtract another StatisticsItem
+     *
+     * @param item
+     * @return
+     */
+    public StatisticsItem subtract(StatisticsItem item) {
+        if (item == null) {
+            return snapshot();
+        }
+
+        if (!statKind.equals(item.statKind) || !statObject.equals(item.statObject) || !Arrays.equals(itemNames,
+            item.itemNames)) {
+            throw new IllegalArgumentException("StatisticsItem's kind, key and itemNames must be exactly the same");
+        }
+
+        StatisticsItem ret = new StatisticsItem(statKind, statObject, itemNames);
+        ret.invokeTimes = new AtomicLong(invokeTimes.get() - item.invokeTimes.get());
+        ret.itemAccumulates = new AtomicLong[itemAccumulates.length];
+        for (int i = 0; i < itemAccumulates.length; i++) {
+            ret.itemAccumulates[i] = new AtomicLong(itemAccumulates[i].get() - item.itemAccumulates[i].get());
+        }
+        return ret;
+    }
+
+    public Interceptor getInterceptor() {
+        return interceptor;
+    }
+
+    public void setInterceptor(Interceptor interceptor) {
+        this.interceptor = interceptor;
+    }
+
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemFormatter.java
similarity index 57%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemFormatter.java
index ed8e7a3..87e301a 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/DeleteTopicFromNamesrvRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemFormatter.java
@@ -14,25 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+package org.apache.rocketmq.common.statistics;
 
-import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import java.util.concurrent.atomic.AtomicLong;
 
-public class DeleteTopicFromNamesrvRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String topic;
-
-    @Override
-    public void checkFields() throws RemotingCommandException {
-    }
-
-    public String getTopic() {
-        return topic;
-    }
-
-    public void setTopic(String topic) {
-        this.topic = topic;
+public class StatisticsItemFormatter {
+    public String format(StatisticsItem statItem) {
+        final String seperator = "|";
+        StringBuilder sb = new StringBuilder();
+        sb.append(statItem.getStatKind()).append(seperator);
+        sb.append(statItem.getStatObject()).append(seperator);
+        for (AtomicLong acc : statItem.getItemAccumulates()) {
+            sb.append(acc.get()).append(seperator);
+        }
+        sb.append(statItem.getInvokeTimes());
+        return sb.toString();
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemPrinter.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemPrinter.java
new file mode 100644
index 0000000..26c1df8
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemPrinter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.statistics;
+
+import org.apache.rocketmq.logging.InternalLogger;
+
+public class StatisticsItemPrinter {
+    private InternalLogger log;
+
+    private StatisticsItemFormatter formatter;
+
+    public StatisticsItemPrinter(StatisticsItemFormatter formatter, InternalLogger log) {
+        this.formatter = formatter;
+        this.log = log;
+    }
+
+    public void log(InternalLogger log) {
+        this.log = log;
+    }
+
+    public void formatter(StatisticsItemFormatter formatter) {
+        this.formatter = formatter;
+    }
+
+    public void print(String prefix, StatisticsItem statItem, String... suffixs) {
+        StringBuilder suffix = new StringBuilder();
+        for (String str : suffixs) {
+            suffix.append(str);
+        }
+
+        if (log != null) {
+            log.info("{}{}{}", prefix, formatter.format(statItem), suffix.toString());
+        }
+        // System.out.printf("%s %s%s%s\n", new Date().toString(), prefix, formatter.format(statItem), suffix.toString());
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledIncrementPrinter.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledIncrementPrinter.java
new file mode 100644
index 0000000..d949b91
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledIncrementPrinter.java
@@ -0,0 +1,290 @@
+/*
+ * 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.statistics;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class StatisticsItemScheduledIncrementPrinter extends StatisticsItemScheduledPrinter {
+
+    private String[] tpsItemNames;
+
+    public static final int TPS_INITIAL_DELAY = 0;
+    public static final int TPS_INTREVAL = 1000;
+    public static final String SEPERATOR = "|";
+
+    /**
+     * last snapshots of all scheduled items
+     */
+    private final ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>> lastItemSnapshots
+        = new ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>>();
+
+    private final ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItemSampleBrief>> sampleBriefs
+        = new ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItemSampleBrief>>();
+
+    public StatisticsItemScheduledIncrementPrinter(String name, StatisticsItemPrinter printer,
+                                                   ScheduledExecutorService executor, InitialDelay initialDelay,
+                                                   long interval, String[] tpsItemNames, Valve valve) {
+        super(name, printer, executor, initialDelay, interval, valve);
+        this.tpsItemNames = tpsItemNames;
+    }
+
+    /**
+     * schedule a StatisticsItem to print the Increments periodically
+     */
+    @Override
+    public void schedule(final StatisticsItem item) {
+        setItemSampleBrief(item.getStatKind(), item.getStatObject(), new StatisticsItemSampleBrief(item, tpsItemNames));
+
+        // print log every ${interval} miliseconds
+        ScheduledFuture future = executor.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                if (!enabled()) {
+                    return;
+                }
+
+                StatisticsItem snapshot = item.snapshot();
+                StatisticsItem lastSnapshot = getItemSnapshot(lastItemSnapshots, item.getStatKind(),
+                    item.getStatObject());
+                StatisticsItem increment = snapshot.subtract(lastSnapshot);
+
+                Interceptor inteceptor = item.getInterceptor();
+                String inteceptorStr = formatInterceptor(inteceptor);
+                if (inteceptor != null) {
+                    inteceptor.reset();
+                }
+
+                StatisticsItemSampleBrief brief = getSampleBrief(item.getStatKind(), item.getStatObject());
+                if (brief != null && (!increment.allZeros() || printZeroLine())) {
+                    printer.print(name, increment, inteceptorStr, brief.toString());
+                }
+
+                setItemSnapshot(lastItemSnapshots, snapshot);
+
+                if (brief != null) {
+                    brief.reset();
+                }
+            }
+        }, getInitialDelay(), interval, TimeUnit.MILLISECONDS);
+        addFuture(item, future);
+
+        // sample every TPS_INTREVAL
+        ScheduledFuture futureSample = executor.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                if (!enabled()) {
+                    return;
+                }
+
+                StatisticsItem snapshot = item.snapshot();
+                StatisticsItemSampleBrief brief = getSampleBrief(item.getStatKind(), item.getStatObject());
+                if (brief != null) {
+                    brief.sample(snapshot);
+                }
+            }
+        }, TPS_INTREVAL, TPS_INTREVAL, TimeUnit.MILLISECONDS);
+        addFuture(item, futureSample);
+    }
+
+    @Override
+    public void remove(StatisticsItem item) {
+        // remove task
+        removeAllFuture(item);
+
+        String kind = item.getStatKind();
+        String key = item.getStatObject();
+
+        ConcurrentHashMap<String, StatisticsItem> lastItemMap = lastItemSnapshots.get(kind);
+        if (lastItemMap != null) {
+            lastItemMap.remove(key);
+        }
+
+        ConcurrentHashMap<String, StatisticsItemSampleBrief> briefMap = sampleBriefs.get(kind);
+        if (briefMap != null) {
+            briefMap.remove(key);
+        }
+    }
+
+    private StatisticsItem getItemSnapshot(
+        ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>> snapshots,
+        String kind, String key) {
+        ConcurrentHashMap<String, StatisticsItem> itemMap = snapshots.get(kind);
+        return (itemMap != null) ? itemMap.get(key) : null;
+    }
+
+    private StatisticsItemSampleBrief getSampleBrief(String kind, String key) {
+        ConcurrentHashMap<String, StatisticsItemSampleBrief> itemMap = sampleBriefs.get(kind);
+        return (itemMap != null) ? itemMap.get(key) : null;
+    }
+
+    private void setItemSnapshot(ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>> snapshots,
+                                 StatisticsItem item) {
+        String kind = item.getStatKind();
+        String key = item.getStatObject();
+        ConcurrentHashMap<String, StatisticsItem> itemMap = snapshots.get(kind);
+        if (itemMap == null) {
+            itemMap = new ConcurrentHashMap<String, StatisticsItem>();
+            ConcurrentHashMap<String, StatisticsItem> oldItemMap = snapshots.putIfAbsent(kind, itemMap);
+            if (oldItemMap != null) {
+                itemMap = oldItemMap;
+            }
+        }
+
+        itemMap.put(key, item);
+    }
+
+    private void setItemSampleBrief(String kind, String key,
+                                    StatisticsItemSampleBrief brief) {
+        ConcurrentHashMap<String, StatisticsItemSampleBrief> itemMap = sampleBriefs.get(kind);
+        if (itemMap == null) {
+            itemMap = new ConcurrentHashMap<String, StatisticsItemSampleBrief>();
+            ConcurrentHashMap<String, StatisticsItemSampleBrief> oldItemMap = sampleBriefs.putIfAbsent(kind, itemMap);
+            if (oldItemMap != null) {
+                itemMap = oldItemMap;
+            }
+        }
+
+        itemMap.put(key, brief);
+    }
+
+    private String formatInterceptor(Interceptor interceptor) {
+        if (interceptor == null) {
+            return "";
+        }
+
+        if (interceptor instanceof StatisticsBriefInterceptor) {
+            StringBuilder sb = new StringBuilder();
+            StatisticsBriefInterceptor briefInterceptor = (StatisticsBriefInterceptor)interceptor;
+            for (StatisticsBrief brief : briefInterceptor.getStatisticsBriefs()) {
+                long max = brief.getMax();
+                long tp999 = Math.min(brief.tp999(), max);
+                //sb.append(SEPERATOR).append(brief.getTotal());
+                sb.append(SEPERATOR).append(max);
+                //sb.append(SEPERATOR).append(brief.getMin());
+                sb.append(SEPERATOR).append(String.format("%.2f", brief.getAvg()));
+                sb.append(SEPERATOR).append(tp999);
+            }
+            return sb.toString();
+        }
+        return "";
+    }
+
+    public static class StatisticsItemSampleBrief {
+        private StatisticsItem lastSnapshot;
+
+        public String[] itemNames;
+        public ItemSampleBrief[] briefs;
+
+        public StatisticsItemSampleBrief(StatisticsItem statItem, String[] itemNames) {
+            this.lastSnapshot = statItem.snapshot();
+            this.itemNames = itemNames;
+            this.briefs = new ItemSampleBrief[itemNames.length];
+            for (int i = 0; i < itemNames.length; i++) {
+                this.briefs[i] = new ItemSampleBrief();
+            }
+        }
+
+        public synchronized void reset() {
+            for (ItemSampleBrief brief : briefs) {
+                brief.reset();
+            }
+        }
+
+        public synchronized void sample(StatisticsItem snapshot) {
+            if (snapshot == null) {
+                return;
+            }
+
+            for (int i = 0; i < itemNames.length; i++) {
+                String name = itemNames[i];
+
+                long lastValue = lastSnapshot != null ? lastSnapshot.getItemAccumulate(name).get() : 0;
+                long increment = snapshot.getItemAccumulate(name).get() - lastValue;
+                briefs[i].sample(increment);
+            }
+            lastSnapshot = snapshot;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < briefs.length; i++) {
+                ItemSampleBrief brief = briefs[i];
+                sb.append(SEPERATOR).append(brief.getMax());
+                //sb.append(SEPERATOR).append(brief.getMin());
+                sb.append(SEPERATOR).append(String.format("%.2f", brief.getAvg()));
+            }
+            return sb.toString();
+        }
+    }
+
+    /**
+     * sample brief of a item for a period of time
+     */
+    public static class ItemSampleBrief {
+        private long max;
+        private long min;
+        private long total;
+        private long cnt;
+
+        public ItemSampleBrief() {
+            reset();
+        }
+
+        public void sample(long value) {
+            max = Math.max(max, value);
+            min = Math.min(min, value);
+            total += value;
+            cnt++;
+        }
+
+        public void reset() {
+            max = 0;
+            min = Long.MAX_VALUE;
+            total = 0;
+            cnt = 0;
+        }
+
+        /**
+         * Getters
+         *
+         * @return
+         */
+        public long getMax() {
+            return max;
+        }
+
+        public long getMin() {
+            return cnt > 0 ? min : 0;
+        }
+
+        public long getTotal() {
+            return total;
+        }
+
+        public long getCnt() {
+            return cnt;
+        }
+
+        public double getAvg() {
+            return cnt != 0 ? ((double)total) / cnt : 0;
+        }
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledPrinter.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledPrinter.java
new file mode 100644
index 0000000..2f46ac5
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemScheduledPrinter.java
@@ -0,0 +1,97 @@
+/*
+ * 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.statistics;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class StatisticsItemScheduledPrinter extends FutureHolder {
+    protected String name;
+
+    protected StatisticsItemPrinter printer;
+    protected ScheduledExecutorService executor;
+    protected long interval;
+    protected InitialDelay initialDelay;
+    protected Valve valve;
+
+    public StatisticsItemScheduledPrinter(String name, StatisticsItemPrinter printer,
+                                          ScheduledExecutorService executor, InitialDelay initialDelay,
+                                          long interval, Valve valve) {
+        this.name = name;
+        this.printer = printer;
+        this.executor = executor;
+        this.initialDelay = initialDelay;
+        this.interval = interval;
+        this.valve = valve;
+    }
+
+    /**
+     * schedule a StatisticsItem to print all the values periodically
+     */
+    public void schedule(final StatisticsItem statisticsItem) {
+        ScheduledFuture future = executor.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                if (enabled()) {
+                    printer.print(name, statisticsItem);
+                }
+            }
+        }, getInitialDelay(), interval, TimeUnit.MILLISECONDS);
+
+        addFuture(statisticsItem, future);
+    }
+
+    public void remove(final StatisticsItem statisticsItem) {
+        removeAllFuture(statisticsItem);
+    }
+
+    public interface InitialDelay {
+        /**
+         * Get inital delay value
+         * @return
+         */
+        long get();
+    }
+
+    public interface Valve {
+        /**
+         * whether enabled
+         * @return
+         */
+        boolean enabled();
+
+        /**
+         * whether print zero lines
+         * @return
+         */
+        boolean printZeroLine();
+    }
+
+    protected long getInitialDelay() {
+        return initialDelay != null ? initialDelay.get() : 0;
+    }
+
+    protected boolean enabled() {
+        return valve != null ? valve.enabled() : false;
+    }
+
+    protected boolean printZeroLine() {
+        return valve != null ? valve.printZeroLine() : false;
+    }
+
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemStateGetter.java
similarity index 84%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemStateGetter.java
index 08091f4..3b16d00 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsItemStateGetter.java
@@ -14,12 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.statistics;
 
-package org.apache.rocketmq.common.message;
-
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+public interface StatisticsItemStateGetter {
+    boolean online(StatisticsItem item);
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsKindMeta.java
similarity index 52%
copy from common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
copy to common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsKindMeta.java
index a2806e6..27bee19 100644
--- a/common/src/main/java/org/apache/rocketmq/common/protocol/header/namesrv/GetRouteInfoRequestHeader.java
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsKindMeta.java
@@ -14,29 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.statistics;
 
 /**
- * $Id: GetRouteInfoRequestHeader.java 1835 2013-05-16 02:00:50Z vintagewang@apache.org $
+ * Statistics Kind Metadata
  */
-package org.apache.rocketmq.common.protocol.header.namesrv;
+public class StatisticsKindMeta {
+    private String name;
+    private String[] itemNames;
+    private StatisticsItemScheduledPrinter scheduledPrinter;
 
-import org.apache.rocketmq.remoting.CommandCustomHeader;
-import org.apache.rocketmq.remoting.annotation.CFNotNull;
-import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
 
-public class GetRouteInfoRequestHeader implements CommandCustomHeader {
-    @CFNotNull
-    private String topic;
+    public String[] getItemNames() {
+        return itemNames;
+    }
 
-    @Override
-    public void checkFields() throws RemotingCommandException {
+    public void setItemNames(String[] itemNames) {
+        this.itemNames = itemNames;
     }
 
-    public String getTopic() {
-        return topic;
+    public StatisticsItemScheduledPrinter getScheduledPrinter() {
+        return scheduledPrinter;
     }
 
-    public void setTopic(String topic) {
-        this.topic = topic;
+    public void setScheduledPrinter(StatisticsItemScheduledPrinter scheduledPrinter) {
+        this.scheduledPrinter = scheduledPrinter;
     }
-}
+}
\ No newline at end of file
diff --git a/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsManager.java b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsManager.java
new file mode 100644
index 0000000..b517b63
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/statistics/StatisticsManager.java
@@ -0,0 +1,157 @@
+/*
+ * 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.statistics;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.rocketmq.common.utils.ThreadUtils;
+
+public class StatisticsManager {
+
+    /**
+     * Set of Statistics Kind Metadata
+     */
+    private Map<String, StatisticsKindMeta> kindMetaMap;
+
+    /**
+     * item names to calculate statistics brief
+     */
+    private Pair<String, long[][]>[] briefMetas;
+
+    /**
+     * Statistics
+     */
+    private final ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>> statsTable
+        = new ConcurrentHashMap<String, ConcurrentHashMap<String, StatisticsItem>>();
+
+    private static final int MAX_IDLE_TIME = 10 * 60 * 1000;
+    private final ScheduledExecutorService executor = ThreadUtils.newSingleThreadScheduledExecutor(
+        "StatisticsManagerCleaner", true);
+
+    private StatisticsItemStateGetter statisticsItemStateGetter;
+
+    public StatisticsManager() {
+        kindMetaMap = new HashMap<String, StatisticsKindMeta>();
+        start();
+    }
+
+    public StatisticsManager(Map<String, StatisticsKindMeta> kindMeta) {
+        this.kindMetaMap = kindMeta;
+        start();
+    }
+
+    public void addStatisticsKindMeta(StatisticsKindMeta kindMeta) {
+        kindMetaMap.put(kindMeta.getName(), kindMeta);
+        statsTable.putIfAbsent(kindMeta.getName(), new ConcurrentHashMap<String, StatisticsItem>(16));
+    }
+
+    public void setBriefMeta(Pair<String, long[][]>[] briefMetas) {
+        this.briefMetas = briefMetas;
+    }
+
+    private void start() {
+        int maxIdleTime = MAX_IDLE_TIME;
+        executor.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                Iterator<Map.Entry<String, ConcurrentHashMap<String, StatisticsItem>>> iter
+                    = statsTable.entrySet().iterator();
+                while (iter.hasNext()) {
+                    Map.Entry<String, ConcurrentHashMap<String, StatisticsItem>> entry = iter.next();
+                    String kind = entry.getKey();
+                    ConcurrentHashMap<String, StatisticsItem> itemMap = entry.getValue();
+
+                    if (itemMap == null || itemMap.isEmpty()) {
+                        continue;
+                    }
+
+                    HashMap<String, StatisticsItem> tmpItemMap = new HashMap<String, StatisticsItem>(itemMap);
+                    for (StatisticsItem item : tmpItemMap.values()) {
+                        // remove when expired
+                        if (System.currentTimeMillis() - item.getLastTimeStamp().get() > MAX_IDLE_TIME
+                            && (statisticsItemStateGetter == null || !statisticsItemStateGetter.online(item))) {
+                            remove(item);
+                        }
+                    }
+                }
+            }
+        }, maxIdleTime, maxIdleTime / 3, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Increment a StatisticsItem
+     *
+     * @param kind
+     * @param key
+     * @param itemAccumulates
+     */
+    public boolean inc(String kind, String key, long... itemAccumulates) {
+        ConcurrentHashMap<String, StatisticsItem> itemMap = statsTable.get(kind);
+        if (itemMap != null) {
+            StatisticsItem item = itemMap.get(key);
+
+            // if not exist, create and schedule
+            if (item == null) {
+                item = new StatisticsItem(kind, key, kindMetaMap.get(kind).getItemNames());
+                item.setInterceptor(new StatisticsBriefInterceptor(item, briefMetas));
+                StatisticsItem oldItem = itemMap.putIfAbsent(key, item);
+                if (oldItem != null) {
+                    item = oldItem;
+                } else {
+                    scheduleStatisticsItem(item);
+                }
+            }
+
+            // do increment
+            item.incItems(itemAccumulates);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    private void scheduleStatisticsItem(StatisticsItem item) {
+        kindMetaMap.get(item.getStatKind()).getScheduledPrinter().schedule(item);
+    }
+
+    public void remove(StatisticsItem item) {
+        ConcurrentHashMap<String, StatisticsItem> itemMap = statsTable.get(item.getStatKind());
+        if (itemMap != null) {
+            itemMap.remove(item.getStatObject(), item);
+        }
+
+        StatisticsKindMeta kindMeta = kindMetaMap.get(item.getStatKind());
+        if (kindMeta != null && kindMeta.getScheduledPrinter() != null) {
+            kindMeta.getScheduledPrinter().remove(item);
+        }
+    }
+
+    public StatisticsItemStateGetter getStatisticsItemStateGetter() {
+        return statisticsItemStateGetter;
+    }
+
+    public void setStatisticsItemStateGetter(StatisticsItemStateGetter statisticsItemStateGetter) {
+        this.statisticsItemStateGetter = statisticsItemStateGetter;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/subscription/GroupForbidden.java b/common/src/main/java/org/apache/rocketmq/common/subscription/GroupForbidden.java
new file mode 100644
index 0000000..73d50c6
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/subscription/GroupForbidden.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.common.subscription;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
+
+/**
+ *
+ */
+public class GroupForbidden extends RemotingSerializable {
+
+    private String  topic;
+    private String  group;
+    private Boolean readable;
+
+    public String getTopic() {
+        return topic;
+    }
+
+    public void setTopic(String topic) {
+        this.topic = topic;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public Boolean getReadable() {
+        return readable;
+    }
+
+    public void setReadable(Boolean readable) {
+        this.readable = readable;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((group == null) ? 0 : group.hashCode());
+        result = prime * result + ((readable == null) ? 0 : readable.hashCode());
+        result = prime * result + ((topic == null) ? 0 : topic.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        GroupForbidden other = (GroupForbidden) obj;
+        return new EqualsBuilder()
+                .append(topic, other.topic)
+                .append(group, other.group)
+                .append(readable, other.readable)
+                .isEquals();
+    }
+
+    @Override
+    public String toString() {
+        return "GroupForbidden [topic=" + topic + ", group=" + group + ", readable=" + readable + "]";
+    }
+
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/subscription/SubscriptionGroupConfig.java b/common/src/main/java/org/apache/rocketmq/common/subscription/SubscriptionGroupConfig.java
index 8f4703f..6981c23 100644
--- a/common/src/main/java/org/apache/rocketmq/common/subscription/SubscriptionGroupConfig.java
+++ b/common/src/main/java/org/apache/rocketmq/common/subscription/SubscriptionGroupConfig.java
@@ -17,6 +17,7 @@
 
 package org.apache.rocketmq.common.subscription;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.rocketmq.common.MixAll;
 
 public class SubscriptionGroupConfig {
@@ -38,6 +39,8 @@ public class SubscriptionGroupConfig {
 
     private boolean notifyConsumerIdsChangedEnable = true;
 
+    private int groupSysFlag = 0;
+
     public String getGroupName() {
         return groupName;
     }
@@ -110,6 +113,14 @@ public class SubscriptionGroupConfig {
         this.notifyConsumerIdsChangedEnable = notifyConsumerIdsChangedEnable;
     }
 
+    public int getGroupSysFlag() {
+        return groupSysFlag;
+    }
+
+    public void setGroupSysFlag(int groupSysFlag) {
+        this.groupSysFlag = groupSysFlag;
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -136,28 +147,18 @@ public class SubscriptionGroupConfig {
         if (getClass() != obj.getClass())
             return false;
         SubscriptionGroupConfig other = (SubscriptionGroupConfig) obj;
-        if (brokerId != other.brokerId)
-            return false;
-        if (consumeBroadcastEnable != other.consumeBroadcastEnable)
-            return false;
-        if (consumeEnable != other.consumeEnable)
-            return false;
-        if (consumeFromMinEnable != other.consumeFromMinEnable)
-            return false;
-        if (groupName == null) {
-            if (other.groupName != null)
-                return false;
-        } else if (!groupName.equals(other.groupName))
-            return false;
-        if (retryMaxTimes != other.retryMaxTimes)
-            return false;
-        if (retryQueueNums != other.retryQueueNums)
-            return false;
-        if (whichBrokerWhenConsumeSlowly != other.whichBrokerWhenConsumeSlowly)
-            return false;
-        if (notifyConsumerIdsChangedEnable != other.notifyConsumerIdsChangedEnable)
-            return false;
-        return true;
+        return new EqualsBuilder()
+            .append(groupName, other.groupName)
+            .append(consumeEnable, other.consumeEnable)
+            .append(consumeFromMinEnable, other.consumeFromMinEnable)
+            .append(consumeBroadcastEnable, other.consumeBroadcastEnable)
+            .append(retryQueueNums, other.retryQueueNums)
+            .append(retryMaxTimes, other.retryMaxTimes)
+            .append(brokerId, other.brokerId)
+            .append(whichBrokerWhenConsumeSlowly, other.whichBrokerWhenConsumeSlowly)
+            .append(notifyConsumerIdsChangedEnable, other.notifyConsumerIdsChangedEnable)
+            .append(groupSysFlag, other.groupSysFlag)
+            .isEquals();
     }
 
     @Override
@@ -167,6 +168,6 @@ public class SubscriptionGroupConfig {
             + consumeBroadcastEnable + ", retryQueueNums=" + retryQueueNums + ", retryMaxTimes="
             + retryMaxTimes + ", brokerId=" + brokerId + ", whichBrokerWhenConsumeSlowly="
             + whichBrokerWhenConsumeSlowly + ", notifyConsumerIdsChangedEnable="
-            + notifyConsumerIdsChangedEnable + "]";
+            + notifyConsumerIdsChangedEnable + ", groupSysFlag=" + groupSysFlag + "]";
     }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/topic/TopicValidator.java b/common/src/main/java/org/apache/rocketmq/common/topic/TopicValidator.java
index d4a9d80..540f53a 100644
--- a/common/src/main/java/org/apache/rocketmq/common/topic/TopicValidator.java
+++ b/common/src/main/java/org/apache/rocketmq/common/topic/TopicValidator.java
@@ -36,6 +36,8 @@ public class TopicValidator {
     public static final String RMQ_SYS_OFFSET_MOVED_EVENT = "OFFSET_MOVED_EVENT";
 
     public static final String SYSTEM_TOPIC_PREFIX = "rmq_sys_";
+    public static final String SYNC_BROKER_MEMBER_GROUP_PREFIX = SYSTEM_TOPIC_PREFIX + "SYNC_BROKER_MEMBER_";
+
     public static final boolean[] VALID_CHAR_BIT_MAP = new boolean[128];
     private static final int TOPIC_MAX_LENGTH = 127;
 
diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/MessageUtils.java b/common/src/main/java/org/apache/rocketmq/common/utils/MessageUtils.java
new file mode 100644
index 0000000..a2affc7
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/MessageUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.google.common.hash.Hashing;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageExt;
+
+public class MessageUtils {
+
+    public static int getShardingKeyIndex(String shardingKey, int indexSize) {
+        return Math.abs(Hashing.murmur3_32().hashBytes(shardingKey.getBytes()).asInt() % indexSize);
+    }
+
+    public static int getShardingKeyIndexByMsg(MessageExt msg, int indexSize) {
+        String shardingKey = msg.getProperty(MessageConst.PROPERTY_SHARDING_KEY);
+        if (shardingKey == null) {
+            shardingKey = "";
+        }
+
+        return getShardingKeyIndex(shardingKey, indexSize);
+    }
+
+    public static Set<Integer> getShardingKeyIndexes(Collection<MessageExt> msgs, int indexSize) {
+        Set<Integer> indexSet = new HashSet<Integer>(indexSize);
+        for (MessageExt msg : msgs) {
+            indexSet.add(getShardingKeyIndexByMsg(msg, indexSize));
+        }
+        return indexSet;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java b/common/src/main/java/org/apache/rocketmq/common/utils/PositiveAtomicCounter.java
similarity index 62%
copy from common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
copy to common/src/main/java/org/apache/rocketmq/common/utils/PositiveAtomicCounter.java
index 08091f4..105b88c 100644
--- a/common/src/main/java/org/apache/rocketmq/common/message/MessageType.java
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/PositiveAtomicCounter.java
@@ -14,12 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.rocketmq.common.utils;
 
-package org.apache.rocketmq.common.message;
+import java.util.concurrent.atomic.AtomicInteger;
 
-public enum MessageType {
-    Normal_Msg,
-    Trans_Msg_Half,
-    Trans_msg_Commit,
-    Delay_Msg,
+public class PositiveAtomicCounter {
+    private static final int MASK = 0x7FFFFFFF;
+    private final AtomicInteger atom;
+
+
+    public PositiveAtomicCounter() {
+        atom = new AtomicInteger(0);
+    }
+
+
+    public final int incrementAndGet() {
+        final int rt = atom.incrementAndGet();
+        return rt & MASK;
+    }
+
+
+    public int intValue() {
+        return atom.intValue();
+    }
 }
diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/ServiceProvider.java b/common/src/main/java/org/apache/rocketmq/common/utils/ServiceProvider.java
new file mode 100644
index 0000000..72857bc
--- /dev/null
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/ServiceProvider.java
@@ -0,0 +1,211 @@
+/*
+ * 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.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ServiceProvider {
+
+    private static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.COMMON_LOGGER_NAME);
+    /**
+     * A reference to the classloader that loaded this class. It's more efficient to compute it once and cache it here.
+     */
+    private static ClassLoader thisClassLoader;
+
+    /**
+     * JDK1.3+ <a href= "http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider" > 'Service Provider' specification</a>.
+     */
+    public static final String TRANSACTION_SERVICE_ID = "META-INF/service/org.apache.rocketmq.broker.transaction.TransactionalMessageService";
+
+    public static final String TRANSACTION_LISTENER_ID = "META-INF/service/org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener";
+
+    public static final String HA_SERVICE_ID = "META-INF/service/org.apache.rocketmq.store.ha.HAService";
+
+
+    public static final String RPC_HOOK_ID = "META-INF/service/org.apache.rocketmq.remoting.RPCHook";
+
+
+    public static final String ACL_VALIDATOR_ID = "META-INF/service/org.apache.rocketmq.acl.AccessValidator";
+
+
+
+    static {
+        thisClassLoader = getClassLoader(ServiceProvider.class);
+    }
+
+    /**
+     * Returns a string that uniquely identifies the specified object, including its class.
+     * <p>
+     * The returned string is of form "classname@hashcode", ie is the same as the return value of the Object.toString() method, but works even when the specified object's class has overidden the toString method.
+     *
+     * @param o may be null.
+     * @return a string of form classname@hashcode, or "null" if param o is null.
+     */
+    protected static String objectId(Object o) {
+        if (o == null) {
+            return "null";
+        } else {
+            return o.getClass().getName() + "@" + System.identityHashCode(o);
+        }
+    }
+
+    protected static ClassLoader getClassLoader(Class<?> clazz) {
+        try {
+            return clazz.getClassLoader();
+        } catch (SecurityException e) {
+            LOG.error("Unable to get classloader for class {} due to security restrictions !",
+                clazz, e.getMessage());
+            throw e;
+        }
+    }
+
+    protected static ClassLoader getContextClassLoader() {
+        ClassLoader classLoader = null;
+        try {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        } catch (SecurityException ex) {
+            /**
+             * The getContextClassLoader() method throws SecurityException when the context
+             * class loader isn't an ancestor of the calling class's class
+             * loader, or if security permissions are restricted.
+             */
+        }
+        return classLoader;
+    }
+
+    protected static InputStream getResourceAsStream(ClassLoader loader, String name) {
+        if (loader != null) {
+            return loader.getResourceAsStream(name);
+        } else {
+            return ClassLoader.getSystemResourceAsStream(name);
+        }
+    }
+
+    public static <T> List<T> load(String name, Class<?> clazz) {
+        LOG.info("Looking for a resource file of name [{}] ...", name);
+        List<T> services = new ArrayList<T>();
+        try {
+            ArrayList<String> names = new ArrayList<String>();
+            final InputStream is = getResourceAsStream(getContextClassLoader(), name);
+            if (is != null) {
+                BufferedReader reader;
+                try {
+                    reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                } catch (java.io.UnsupportedEncodingException e) {
+                    reader = new BufferedReader(new InputStreamReader(is));
+                }
+                String serviceName = reader.readLine();
+                while (serviceName != null && !"".equals(serviceName)) {
+                    LOG.info(
+                        "Creating an instance as specified by file {} which was present in the path of the context classloader.",
+                        name);
+                    if (!names.contains(serviceName)) {
+                        names.add(serviceName);
+                    }
+
+                    services.add((T)initService(getContextClassLoader(), serviceName, clazz));
+
+                    serviceName = reader.readLine();
+                }
+                reader.close();
+            } else {
+                // is == null
+                LOG.warn("No resource file with name [{}] found.", name);
+            }
+        } catch (Exception e) {
+            LOG.error("Error occured when looking for resource file " + name, e);
+        }
+        return services;
+    }
+
+    public static <T> T loadClass(String name, Class<?> clazz) {
+        final InputStream is = getResourceAsStream(getContextClassLoader(), name);
+        if (is != null) {
+            BufferedReader reader;
+            try {
+                try {
+                    reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                } catch (java.io.UnsupportedEncodingException e) {
+                    reader = new BufferedReader(new InputStreamReader(is));
+                }
+                String serviceName = reader.readLine();
+                reader.close();
+                if (serviceName != null && !"".equals(serviceName)) {
+                    return initService(getContextClassLoader(), serviceName, clazz);
+                } else {
+                    LOG.warn("ServiceName is empty!");
+                    return null;
+                }
+            } catch (Exception e) {
+                LOG.warn("Error occurred when looking for resource file " + name, e);
+            }
+        }
+        return null;
+    }
+
+    protected static <T> T initService(ClassLoader classLoader, String serviceName, Class<?> clazz) {
+        Class<?> serviceClazz = null;
+        try {
+            if (classLoader != null) {
+                try {
+                    // Warning: must typecast here & allow exception to be generated/caught & recast properly
+                    serviceClazz = classLoader.loadClass(serviceName);
+                    if (clazz.isAssignableFrom(serviceClazz)) {
+                        LOG.info("Loaded class {} from classloader {}", serviceClazz.getName(),
+                            objectId(classLoader));
+                    } else {
+                        // This indicates a problem with the ClassLoader tree. An incompatible ClassLoader was used to load the implementation.
+                        LOG.error(
+                            "Class {} loaded from classloader {} does not extend {} as loaded by this classloader.",
+                            new Object[] {serviceClazz.getName(),
+                                objectId(serviceClazz.getClassLoader()), clazz.getName()});
+                    }
+                    return (T)serviceClazz.newInstance();
+                } catch (ClassNotFoundException ex) {
+                    if (classLoader == thisClassLoader) {
+                        // Nothing more to try, onwards.
+                        LOG.warn("Unable to locate any class {} via classloader", serviceName,
+                            objectId(classLoader));
+                        throw ex;
+                    }
+                    // Ignore exception, continue
+                } catch (NoClassDefFoundError e) {
+                    if (classLoader == thisClassLoader) {
+                        // Nothing more to try, onwards.
+                        LOG.warn(
+                            "Class {} cannot be loaded via classloader {}.it depends on some other class that cannot be found.",
+                            serviceClazz, objectId(classLoader));
+                        throw e;
+                    }
+                    // Ignore exception, continue
+                }
+            }
+        } catch (Exception e) {
+            LOG.error("Unable to init service.", e);
+        }
+        return (T)serviceClazz;
+    }
+}
diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java b/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
index 13c0293..61ca4a8 100644
--- a/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
+++ b/common/src/main/java/org/apache/rocketmq/common/utils/ThreadUtils.java
@@ -30,7 +30,7 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 
 public final class ThreadUtils {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.TOOLS_LOGGER_NAME);
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.TOOLS_LOGGER_NAME);
 
     public static ExecutorService newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
         TimeUnit unit, BlockingQueue<Runnable> workQueue, String processName, boolean isDaemon) {
@@ -102,7 +102,7 @@ public final class ThreadUtils {
         thread.setDaemon(daemon);
         thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
             public void uncaughtException(Thread t, Throwable e) {
-                log.error("Uncaught exception in thread '" + t.getName() + "':", e);
+                LOGGER.error("Uncaught exception in thread '" + t.getName() + "':", e);
             }
         });
         return thread;
@@ -153,7 +153,7 @@ public final class ThreadUtils {
                 executor.shutdownNow();
                 // Wait a while for tasks to respond to being cancelled.
                 if (!executor.awaitTermination(timeout, timeUnit)) {
-                    log.warn(String.format("%s didn't terminate!", executor));
+                    LOGGER.warn(String.format("%s didn't terminate!", executor));
                 }
             }
         } catch (InterruptedException ie) {
@@ -165,6 +165,17 @@ public final class ThreadUtils {
     }
 
     /**
+     * Shutdown the specific ExecutorService
+     *
+     * @param executorService the executor
+     */
+    public static void shutdown(ExecutorService executorService) {
+        if (executorService != null) {
+            executorService.shutdown();
+        }
+    }
+
+    /**
      * A constructor to stop this class being constructed.
      */
     private ThreadUtils() {
diff --git a/common/src/test/java/org/apache/rocketmq/common/DataVersionTest.java b/common/src/test/java/org/apache/rocketmq/common/DataVersionTest.java
index f4d14e5..d6cc4b0 100644
--- a/common/src/test/java/org/apache/rocketmq/common/DataVersionTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/DataVersionTest.java
@@ -67,4 +67,11 @@ public class DataVersionTest {
         other.setTimestamp(dataVersion.getTimestamp());
         Assert.assertTrue(dataVersion.equals(other));
     }
+
+    @Test
+    public void testEncode() {
+        DataVersion dataVersion = new DataVersion();
+        Assert.assertTrue(dataVersion.encode().length > 0);
+        Assert.assertNotNull(dataVersion.toJson());
+    }
 }
\ No newline at end of file
diff --git a/common/src/test/java/org/apache/rocketmq/common/RegisterBrokerBodyTest.java b/common/src/test/java/org/apache/rocketmq/common/RegisterBrokerBodyTest.java
index 428a928..73703c0 100644
--- a/common/src/test/java/org/apache/rocketmq/common/RegisterBrokerBodyTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/RegisterBrokerBodyTest.java
@@ -22,7 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import org.apache.rocketmq.common.protocol.body.RegisterBrokerBody;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
-import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
+
 import static org.junit.Assert.assertEquals;
 import org.junit.Test;
 
diff --git a/common/src/test/java/org/apache/rocketmq/common/UtilAllTest.java b/common/src/test/java/org/apache/rocketmq/common/UtilAllTest.java
index 7ef7eac..d1ed4bb 100644
--- a/common/src/test/java/org/apache/rocketmq/common/UtilAllTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/UtilAllTest.java
@@ -19,13 +19,11 @@ package org.apache.rocketmq.common;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
-
-import com.alibaba.fastjson.JSON;
-import com.google.common.collect.ImmutableList;
 import org.junit.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -174,4 +172,21 @@ public class UtilAllTest {
                 '}';
         }
     }
+
+    @Test
+    public void testCleanBuffer() {
+        UtilAll.cleanBuffer(null);
+        UtilAll.cleanBuffer(ByteBuffer.allocate(10));
+        UtilAll.cleanBuffer(ByteBuffer.allocate(0));
+    }
+
+    @Test(expected = NoSuchMethodException.class)
+    public void testMethod() throws NoSuchMethodException {
+        UtilAll.method(new Object(), "noMethod", null);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testInvoke() throws Exception {
+        UtilAll.invoke(new Object(), "noMethod");
+    }
 }
diff --git a/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
index 5af7345..c57c758 100644
--- a/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.rocketmq.common.message;
 
+import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.sysflag.MessageSysFlag;
 import org.junit.Test;
 
@@ -163,6 +164,8 @@ public class MessageDecoderTest {
         messageExt.putUserProperty("b", "hello");
         messageExt.putUserProperty("c", "3.14");
 
+        messageExt.setBodyCRC(UtilAll.crc32(messageExt.getBody()));
+
         byte[] msgBytes = new byte[0];
         try {
             msgBytes = MessageDecoder.encode(messageExt, false);
@@ -174,7 +177,7 @@ public class MessageDecoderTest {
         ByteBuffer byteBuffer = ByteBuffer.allocate(msgBytes.length);
         byteBuffer.put(msgBytes);
 
-        byteBuffer.clear();
+        byteBuffer.flip();
         MessageExt decodedMsg = MessageDecoder.decode(byteBuffer);
 
         assertThat(decodedMsg).isNotNull();
@@ -222,6 +225,8 @@ public class MessageDecoderTest {
         messageExt.putUserProperty("b", "hello");
         messageExt.putUserProperty("c", "3.14");
 
+        messageExt.setBodyCRC(UtilAll.crc32(messageExt.getBody()));
+
         byte[] msgBytes = new byte[0];
         try {
             msgBytes = MessageDecoder.encode(messageExt, false);
@@ -233,7 +238,7 @@ public class MessageDecoderTest {
         ByteBuffer byteBuffer = ByteBuffer.allocate(msgBytes.length);
         byteBuffer.put(msgBytes);
 
-        byteBuffer.clear();
+        byteBuffer.flip();
         MessageExt decodedMsg = MessageDecoder.decode(byteBuffer);
 
         assertThat(decodedMsg).isNotNull();
diff --git a/common/src/test/java/org/apache/rocketmq/common/message/MessageTest.java b/common/src/test/java/org/apache/rocketmq/common/message/MessageTest.java
index c867360..c950970 100644
--- a/common/src/test/java/org/apache/rocketmq/common/message/MessageTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/message/MessageTest.java
@@ -20,7 +20,6 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import static org.apache.rocketmq.common.message.MessageConst.PROPERTY_TRACE_SWITCH;
-import static org.junit.Assert.*;
 
 public class MessageTest {
     @Test(expected = RuntimeException.class)
diff --git a/common/src/test/java/org/apache/rocketmq/common/protocol/body/KVTableTest.java b/common/src/test/java/org/apache/rocketmq/common/protocol/body/KVTableTest.java
index 836733c..6a39cee 100644
--- a/common/src/test/java/org/apache/rocketmq/common/protocol/body/KVTableTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/protocol/body/KVTableTest.java
@@ -18,7 +18,6 @@
 package org.apache.rocketmq.common.protocol.body;
 
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
-import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.HashMap;
diff --git a/common/src/test/java/org/apache/rocketmq/common/statictopic/TopicQueueMappingTest.java b/common/src/test/java/org/apache/rocketmq/common/statictopic/TopicQueueMappingTest.java
index 4e93275..6296dd5 100644
--- a/common/src/test/java/org/apache/rocketmq/common/statictopic/TopicQueueMappingTest.java
+++ b/common/src/test/java/org/apache/rocketmq/common/statictopic/TopicQueueMappingTest.java
@@ -21,11 +21,9 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.google.common.collect.ImmutableList;
-import org.apache.rocketmq.common.protocol.header.GetMinOffsetRequestHeader;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 import org.junit.Assert;
 import org.junit.Test;
-import org.junit.experimental.theories.suppliers.TestedOn;
 
 import java.util.Map;
 
diff --git a/namesrv/pom.xml b/namesrv/pom.xml
index 11ddfb7..0b04d50 100644
--- a/namesrv/pom.xml
+++ b/namesrv/pom.xml
@@ -45,12 +45,23 @@
             <artifactId>logback-classic</artifactId>
         </dependency>
         <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>1.19</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>1.19</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvController.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvController.java
index a6654f2..d02e6c0 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvController.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvController.java
@@ -16,58 +16,92 @@
  */
 package org.apache.rocketmq.namesrv;
 
+import java.util.Arrays;
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RunnableFuture;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.apache.rocketmq.common.Configuration;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
 import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.future.FutureTaskExt;
+import org.apache.rocketmq.common.protocol.RequestCode;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.namesrv.NamesrvConfig;
 import org.apache.rocketmq.namesrv.kvconfig.KVConfigManager;
+import org.apache.rocketmq.namesrv.processor.ClientRequestProcessor;
 import org.apache.rocketmq.namesrv.processor.ClusterTestRequestProcessor;
 import org.apache.rocketmq.namesrv.processor.DefaultRequestProcessor;
 import org.apache.rocketmq.namesrv.routeinfo.BrokerHousekeepingService;
 import org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager;
+import org.apache.rocketmq.remoting.RemotingClient;
 import org.apache.rocketmq.remoting.RemotingServer;
+import org.apache.rocketmq.remoting.common.RemotingUtil;
 import org.apache.rocketmq.remoting.common.TlsMode;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
+import org.apache.rocketmq.remoting.netty.NettyRemotingClient;
 import org.apache.rocketmq.remoting.netty.NettyRemotingServer;
 import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.netty.RequestTask;
 import org.apache.rocketmq.remoting.netty.TlsSystemConfig;
 import org.apache.rocketmq.srvutil.FileWatchService;
 
 
 public class NamesrvController {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
+    private static final InternalLogger WATER_MARK_LOG = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_WATER_MARK_LOGGER_NAME);
 
     private final NamesrvConfig namesrvConfig;
 
     private final NettyServerConfig nettyServerConfig;
-
-    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
-        "NSScheduledThread"));
+    private final NettyClientConfig nettyClientConfig;
+
+    private final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
+        new BasicThreadFactory.Builder()
+            .namingPattern("NSScheduledThread")
+            .daemon(true)
+            .build());
+    private final ScheduledExecutorService scanExecutorService = new ScheduledThreadPoolExecutor(1,
+        new BasicThreadFactory.Builder()
+            .namingPattern("NSScanScheduledThread")
+            .daemon(true)
+            .build());
     private final KVConfigManager kvConfigManager;
     private final RouteInfoManager routeInfoManager;
 
+    private RemotingClient remotingClient;
     private RemotingServer remotingServer;
 
     private BrokerHousekeepingService brokerHousekeepingService;
 
-    private ExecutorService remotingExecutor;
+    private ExecutorService defaultExecutor;
+    private ExecutorService clientRequestExecutor;
+
+    private BlockingQueue<Runnable> defaultThreadPoolQueue;
+    private BlockingQueue<Runnable> clientRequestThreadPoolQueue;
 
     private Configuration configuration;
     private FileWatchService fileWatchService;
 
     public NamesrvController(NamesrvConfig namesrvConfig, NettyServerConfig nettyServerConfig) {
+        this(namesrvConfig, nettyServerConfig, new NettyClientConfig());
+    }
+
+    public NamesrvController(NamesrvConfig namesrvConfig, NettyServerConfig nettyServerConfig, NettyClientConfig nettyClientConfig) {
         this.namesrvConfig = namesrvConfig;
         this.nettyServerConfig = nettyServerConfig;
+        this.nettyClientConfig = nettyClientConfig;
         this.kvConfigManager = new KVConfigManager(this);
-        this.routeInfoManager = new RouteInfoManager();
         this.brokerHousekeepingService = new BrokerHousekeepingService(this);
+        this.routeInfoManager = new RouteInfoManager(namesrvConfig, this);
         this.configuration = new Configuration(
-            log,
+            LOGGER,
             this.namesrvConfig, this.nettyServerConfig
         );
         this.configuration.setStorePathFromConfig(this.namesrvConfig, "configStorePath");
@@ -79,26 +113,51 @@ public class NamesrvController {
 
         this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
 
-        this.remotingExecutor =
-            Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), new ThreadFactoryImpl("RemotingExecutorThread_"));
+        this.defaultThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getDefaultThreadPoolQueueCapacity());
+        this.defaultExecutor = new ThreadPoolExecutor(
+            this.namesrvConfig.getDefaultThreadPoolNums(),
+            this.namesrvConfig.getDefaultThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.defaultThreadPoolQueue,
+            new ThreadFactoryImpl("RemotingExecutorThread_")) {
+            @Override
+            protected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {
+                return new FutureTaskExt<T>(runnable, value);
+            }
+        };
+
+        this.clientRequestThreadPoolQueue = new LinkedBlockingQueue<>(this.namesrvConfig.getClientRequestThreadPoolQueueCapacity());
+        this.clientRequestExecutor = new ThreadPoolExecutor(
+            this.namesrvConfig.getClientRequestThreadPoolNums(),
+            this.namesrvConfig.getClientRequestThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.clientRequestThreadPoolQueue,
+            new ThreadFactoryImpl("ClientRequestExecutorThread_")) {
+            @Override
+            protected <T> RunnableFuture<T> newTaskFor(final Runnable runnable, final T value) {
+                return new FutureTaskExt<T>(runnable, value);
+            }
+        };
+        this.remotingClient = new NettyRemotingClient(this.nettyClientConfig);
+        this.remotingClient.updateNameServerAddressList(Arrays.asList(RemotingUtil.getLocalAddress() + ":" + this.nettyServerConfig.getListenPort()));
 
         this.registerProcessor();
 
-        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-
-            @Override
-            public void run() {
-                NamesrvController.this.routeInfoManager.scanNotActiveBroker();
-            }
-        }, 5, 10, TimeUnit.SECONDS);
+        this.scanExecutorService.scheduleAtFixedRate(NamesrvController.this.routeInfoManager::scanNotActiveBroker,
+            5, this.namesrvConfig.getScanNotActiveBrokerInterval(), TimeUnit.MILLISECONDS);
 
-        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+        this.scheduledExecutorService.scheduleAtFixedRate(NamesrvController.this.kvConfigManager::printAllPeriodically,
+            1, 10, TimeUnit.MINUTES);
 
-            @Override
-            public void run() {
-                NamesrvController.this.kvConfigManager.printAllPeriodically();
+        this.scheduledExecutorService.scheduleAtFixedRate(() -> {
+            try {
+                NamesrvController.this.printWaterMark();
+            } catch (Throwable e) {
+                LOGGER.error("printWaterMark error.", e);
             }
-        }, 1, 10, TimeUnit.MINUTES);
+        }, 10, 1, TimeUnit.SECONDS);
 
         if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
             // Register a listener to reload SslContext
@@ -111,10 +170,11 @@ public class NamesrvController {
                     },
                     new FileWatchService.Listener() {
                         boolean certChanged, keyChanged = false;
+
                         @Override
                         public void onChanged(String path) {
                             if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
-                                log.info("The trust certificate changed, reload the ssl context");
+                                LOGGER.info("The trust certificate changed, reload the ssl context");
                                 reloadServerSslContext();
                             }
                             if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
@@ -124,46 +184,82 @@ public class NamesrvController {
                                 keyChanged = true;
                             }
                             if (certChanged && keyChanged) {
-                                log.info("The certificate and private key changed, reload the ssl context");
+                                LOGGER.info("The certificate and private key changed, reload the ssl context");
                                 certChanged = keyChanged = false;
                                 reloadServerSslContext();
                             }
                         }
+
                         private void reloadServerSslContext() {
                             ((NettyRemotingServer) remotingServer).loadSslContext();
                         }
                     });
             } catch (Exception e) {
-                log.warn("FileWatchService created error, can't load the certificate dynamically");
+                LOGGER.warn("FileWatchService created error, can't load the certificate dynamically");
             }
         }
 
         return true;
     }
 
+    private void printWaterMark() {
+        WATER_MARK_LOG.info("[WATERMARK] ClientQueueSize:{} ClientQueueSlowTime:{} " +
+                "DefaultQueueSize:{} DefaultQueueSlowTime:{}",
+            this.clientRequestThreadPoolQueue.size(), headSlowTimeMills(this.clientRequestThreadPoolQueue),
+            this.defaultThreadPoolQueue.size(), headSlowTimeMills(this.defaultThreadPoolQueue));
+    }
+
+    private long headSlowTimeMills(BlockingQueue<Runnable> q) {
+        long slowTimeMills = 0;
+        final Runnable firstRunnable = q.peek();
+
+        if (firstRunnable instanceof FutureTaskExt) {
+            final Runnable inner = ((FutureTaskExt) firstRunnable).getRunnable();
+            if (inner instanceof RequestTask) {
+                slowTimeMills = System.currentTimeMillis() - ((RequestTask) inner).getCreateTimestamp();
+            }
+        }
+
+        if (slowTimeMills < 0) {
+            slowTimeMills = 0;
+        }
+
+        return slowTimeMills;
+    }
+
     private void registerProcessor() {
         if (namesrvConfig.isClusterTest()) {
 
             this.remotingServer.registerDefaultProcessor(new ClusterTestRequestProcessor(this, namesrvConfig.getProductEnvName()),
-                this.remotingExecutor);
+                this.defaultExecutor);
         } else {
+            // Support get route info only temporarily
+            ClientRequestProcessor clientRequestProcessor = new ClientRequestProcessor(this);
+            this.remotingServer.registerProcessor(RequestCode.GET_ROUTEINFO_BY_TOPIC, clientRequestProcessor, this.clientRequestExecutor);
 
-            this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.remotingExecutor);
+            this.remotingServer.registerDefaultProcessor(new DefaultRequestProcessor(this), this.defaultExecutor);
         }
     }
 
     public void start() throws Exception {
         this.remotingServer.start();
+        this.remotingClient.start();
 
         if (this.fileWatchService != null) {
             this.fileWatchService.start();
         }
+
+        this.routeInfoManager.start();
     }
 
     public void shutdown() {
+        this.remotingClient.shutdown();
         this.remotingServer.shutdown();
-        this.remotingExecutor.shutdown();
+        this.defaultExecutor.shutdown();
+        this.clientRequestExecutor.shutdown();
         this.scheduledExecutorService.shutdown();
+        this.scanExecutorService.shutdown();
+        this.routeInfoManager.shutdown();
 
         if (this.fileWatchService != null) {
             this.fileWatchService.shutdown();
@@ -190,6 +286,10 @@ public class NamesrvController {
         return remotingServer;
     }
 
+    public RemotingClient getRemotingClient() {
+        return remotingClient;
+    }
+
     public void setRemotingServer(RemotingServer remotingServer) {
         this.remotingServer = remotingServer;
     }
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
index d006128..715f238 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/NamesrvStartup.java
@@ -35,6 +35,7 @@ import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.namesrv.NamesrvConfig;
+import org.apache.rocketmq.remoting.netty.NettyClientConfig;
 import org.apache.rocketmq.remoting.netty.NettyServerConfig;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.srvutil.ServerUtil;
@@ -81,6 +82,7 @@ public class NamesrvStartup {
 
         final NamesrvConfig namesrvConfig = new NamesrvConfig();
         final NettyServerConfig nettyServerConfig = new NettyServerConfig();
+        final NettyClientConfig nettyClientConfig = new NettyClientConfig();
         nettyServerConfig.setListenPort(9876);
         if (commandLine.hasOption('c')) {
             String file = commandLine.getOptionValue('c');
@@ -90,6 +92,7 @@ public class NamesrvStartup {
                 properties.load(in);
                 MixAll.properties2Object(properties, namesrvConfig);
                 MixAll.properties2Object(properties, nettyServerConfig);
+                MixAll.properties2Object(properties, nettyClientConfig);
 
                 namesrvConfig.setConfigStorePath(file);
 
@@ -99,9 +102,9 @@ public class NamesrvStartup {
         }
 
         if (commandLine.hasOption('p')) {
-            InternalLogger console = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_CONSOLE_NAME);
-            MixAll.printObjectProperties(console, namesrvConfig);
-            MixAll.printObjectProperties(console, nettyServerConfig);
+            MixAll.printObjectProperties(null, namesrvConfig);
+            MixAll.printObjectProperties(null, nettyServerConfig);
+            MixAll.printObjectProperties(null, nettyClientConfig);
             System.exit(0);
         }
 
@@ -123,7 +126,7 @@ public class NamesrvStartup {
         MixAll.printObjectProperties(log, namesrvConfig);
         MixAll.printObjectProperties(log, nettyServerConfig);
 
-        final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);
+        final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig, nettyClientConfig);
 
         // remember all configs to prevent discard
         controller.getConfiguration().registerConfig(properties);
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java
new file mode 100644
index 0000000..6f624e3
--- /dev/null
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClientRequestProcessor.java
@@ -0,0 +1,106 @@
+/*
+ * 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.namesrv.processor;
+
+import io.netty.channel.ChannelHandlerContext;
+import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.namesrv.NamesrvUtil;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.header.namesrv.GetRouteInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.route.TopicRouteData;
+import org.apache.rocketmq.common.protocol.route.TopicRouteDatas;
+import org.apache.rocketmq.namesrv.NamesrvController;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+
+public class ClientRequestProcessor implements NettyRequestProcessor {
+    protected NamesrvController namesrvController;
+
+    public ClientRequestProcessor(final NamesrvController namesrvController) {
+        this.namesrvController = namesrvController;
+    }
+
+    @Override
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        final RemotingCommand request) throws Exception {
+        return this.getRouteInfoByTopic(ctx, request);
+    }
+
+    public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        final GetRouteInfoRequestHeader requestHeader =
+            (GetRouteInfoRequestHeader) request.decodeCommandCustomHeader(GetRouteInfoRequestHeader.class);
+
+        if (requestHeader.getTopic().indexOf(GetRouteInfoRequestHeader.split) < 0) {
+            TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic());
+
+            if (topicRouteData != null) {
+                if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
+                    String orderTopicConf =
+                        this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,
+                            requestHeader.getTopic());
+
+                    // Get a shallow clone of route data to modify order topic conf
+                    topicRouteData = topicRouteData.cloneTopicRouteData();
+                    topicRouteData.setOrderTopicConf(orderTopicConf);
+                }
+
+                byte[] content = topicRouteData.encode();
+                response.setBody(content);
+                response.setCode(ResponseCode.SUCCESS);
+                response.setRemark(null);
+                return response;
+            }
+
+            response.setCode(ResponseCode.TOPIC_NOT_EXIST);
+            response.setRemark("No topic route info in name server for the topic: " + requestHeader.getTopic()
+                + FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL));
+            return response;
+        }
+
+        String[] topics = requestHeader.getTopic().split(String.valueOf(GetRouteInfoRequestHeader.split));
+        TopicRouteDatas topicRouteDatas = new TopicRouteDatas();
+
+        for (String topic : topics) {
+            TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(topic);
+            if (topicRouteData == null) {
+                continue;
+            }
+
+            if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
+                String orderTopicConf =
+                    this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG, topic);
+                // Get a shallow clone of route data to modify order topic conf
+                topicRouteData = topicRouteData.cloneTopicRouteData();
+                topicRouteData.setOrderTopicConf(orderTopicConf);
+            }
+
+            topicRouteDatas.getTopics().put(topic, topicRouteData);
+        }
+        response.setBody(topicRouteDatas.encode());
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+        return response;
+    }
+
+    @Override public boolean rejectRequest() {
+        return false;
+    }
+}
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClusterTestRequestProcessor.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClusterTestRequestProcessor.java
index a58a3b9..e5ca2d3 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClusterTestRequestProcessor.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/ClusterTestRequestProcessor.java
@@ -31,7 +31,7 @@ import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
 
-public class ClusterTestRequestProcessor extends DefaultRequestProcessor {
+public class ClusterTestRequestProcessor extends ClientRequestProcessor {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
     private final DefaultMQAdminExt adminExt;
     private final String productEnvName;
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java
index bde0348..a293683 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/processor/DefaultRequestProcessor.java
@@ -19,6 +19,7 @@ package org.apache.rocketmq.namesrv.processor;
 import io.netty.channel.ChannelHandlerContext;
 import java.io.UnsupportedEncodingException;
 import java.util.Properties;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.rocketmq.common.DataVersion;
 import org.apache.rocketmq.common.MQVersion;
@@ -26,9 +27,14 @@ import org.apache.rocketmq.common.MQVersion.Version;
 import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.constant.LoggerName;
-import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
+import org.apache.rocketmq.common.protocol.body.GetBrokerMemberGroupResponseBody;
+import org.apache.rocketmq.common.protocol.body.GetRemoteClientConfigBody;
+import org.apache.rocketmq.common.protocol.header.GetBrokerMemberGroupRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.AddWritePermOfBrokerRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.AddWritePermOfBrokerResponseHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.BrokerHeartbeatRequestHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.RegisterTopicRequestHeader;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.namesrv.NamesrvUtil;
@@ -43,7 +49,6 @@ import org.apache.rocketmq.common.protocol.header.namesrv.DeleteTopicFromNamesrv
 import org.apache.rocketmq.common.protocol.header.namesrv.GetKVConfigRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.GetKVConfigResponseHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.GetKVListByNamespaceRequestHeader;
-import org.apache.rocketmq.common.protocol.header.namesrv.GetRouteInfoRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.PutKVConfigRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.QueryDataVersionRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.QueryDataVersionResponseHeader;
@@ -56,11 +61,11 @@ import org.apache.rocketmq.common.protocol.route.TopicRouteData;
 import org.apache.rocketmq.namesrv.NamesrvController;
 import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
-import org.apache.rocketmq.remoting.netty.AsyncNettyRequestProcessor;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode;
 
-public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
+public class DefaultRequestProcessor implements NettyRequestProcessor {
     private static InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
 
     protected final NamesrvController namesrvController;
@@ -80,7 +85,6 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
                 request);
         }
 
-
         switch (request.getCode()) {
             case RequestCode.PUT_KV_CONFIG:
                 return this.putKVConfig(ctx, request);
@@ -89,18 +93,15 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
             case RequestCode.DELETE_KV_CONFIG:
                 return this.deleteKVConfig(ctx, request);
             case RequestCode.QUERY_DATA_VERSION:
-                return queryBrokerTopicConfig(ctx, request);
+                return this.queryBrokerTopicConfig(ctx, request);
             case RequestCode.REGISTER_BROKER:
-                Version brokerVersion = MQVersion.value2Version(request.getVersion());
-                if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
-                    return this.registerBrokerWithFilterServer(ctx, request);
-                } else {
-                    return this.registerBroker(ctx, request);
-                }
+                return this.registerBroker(ctx, request);
             case RequestCode.UNREGISTER_BROKER:
                 return this.unregisterBroker(ctx, request);
-            case RequestCode.GET_ROUTEINFO_BY_TOPIC:
-                return this.getRouteInfoByTopic(ctx, request);
+            case RequestCode.BROKER_HEARTBEAT:
+                return this.brokerHeartbeat(ctx, request);
+            case RequestCode.GET_BROKER_MEMBER_GROUP:
+                return this.getBrokerMemberGroup(ctx, request);
             case RequestCode.GET_BROKER_CLUSTER_INFO:
                 return this.getBrokerClusterInfo(ctx, request);
             case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
@@ -108,9 +109,11 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
             case RequestCode.ADD_WRITE_PERM_OF_BROKER:
                 return this.addWritePermOfBroker(ctx, request);
             case RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:
-                return getAllTopicListFromNameserver(ctx, request);
+                return this.getAllTopicListFromNameserver(ctx, request);
             case RequestCode.DELETE_TOPIC_IN_NAMESRV:
-                return deleteTopicInNamesrv(ctx, request);
+                return this.deleteTopicInNamesrv(ctx, request);
+            case RequestCode.REGISTER_TOPIC_IN_NAMESRV:
+                return this.registerTopicToNamesrv(ctx, request);
             case RequestCode.GET_KVLIST_BY_NAMESPACE:
                 return this.getKVListByNamespace(ctx, request);
             case RequestCode.GET_TOPICS_BY_CLUSTER:
@@ -127,14 +130,15 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
                 return this.updateConfig(ctx, request);
             case RequestCode.GET_NAMESRV_CONFIG:
                 return this.getConfig(ctx, request);
+            case RequestCode.GET_CLIENT_CONFIG:
+                return this.getClientConfigs(ctx, request);
             default:
-                break;
+                String error = " request type " + request.getCode() + " not supported";
+                return RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);
         }
-        return null;
     }
 
-    @Override
-    public boolean rejectRequest() {
+    @Override public boolean rejectRequest() {
         return false;
     }
 
@@ -200,8 +204,8 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         return response;
     }
 
-    public RemotingCommand registerBrokerWithFilterServer(ChannelHandlerContext ctx, RemotingCommand request)
-        throws RemotingCommandException {
+    public RemotingCommand registerBroker(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class);
         final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader();
         final RegisterBrokerRequestHeader requestHeader =
@@ -213,17 +217,17 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
             return response;
         }
 
-        RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody();
+        TopicConfigSerializeWrapper topicConfigWrapper = null;
+        List<String> filterServerList = null;
 
-        if (request.getBody() != null) {
-            try {
-                registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed());
-            } catch (Exception e) {
-                throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e);
-            }
+        Version brokerVersion = MQVersion.value2Version(request.getVersion());
+        if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
+            final RegisterBrokerBody registerBrokerBody = extractRegisterBrokerBodyFromRequest(request, requestHeader);
+            topicConfigWrapper = registerBrokerBody.getTopicConfigSerializeWrapper();
+            filterServerList = registerBrokerBody.getFilterServerList();
         } else {
-            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0));
-            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0);
+            // RegisterBrokerBody of old version only contains TopicConfig.
+            topicConfigWrapper = extractRegisterTopicConfigFromRequest(request);
         }
 
         RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(
@@ -232,21 +236,73 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
             requestHeader.getBrokerName(),
             requestHeader.getBrokerId(),
             requestHeader.getHaServerAddr(),
-            registerBrokerBody.getTopicConfigSerializeWrapper(),
-            registerBrokerBody.getFilterServerList(),
-            ctx.channel());
+            requestHeader.getHeartbeatTimeoutMillis(),
+            requestHeader.getEnableActingMaster(),
+            topicConfigWrapper,
+            filterServerList,
+            ctx.channel()
+        );
 
         responseHeader.setHaServerAddr(result.getHaServerAddr());
         responseHeader.setMasterAddr(result.getMasterAddr());
 
-        byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
-        response.setBody(jsonValue);
+        if (this.namesrvController.getNamesrvConfig().isReturnOrderTopicConfigToBroker()) {
+            byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
+            response.setBody(jsonValue);
+        }
 
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
         return response;
     }
 
+    private TopicConfigSerializeWrapper extractRegisterTopicConfigFromRequest(final RemotingCommand request) {
+        TopicConfigSerializeWrapper topicConfigWrapper;
+        if (request.getBody() != null) {
+            topicConfigWrapper = TopicConfigSerializeWrapper.decode(request.getBody(), TopicConfigSerializeWrapper.class);
+        } else {
+            topicConfigWrapper = new TopicConfigSerializeWrapper();
+            topicConfigWrapper.getDataVersion().setCounter(new AtomicLong(0));
+            topicConfigWrapper.getDataVersion().setTimestamp(0L);
+            topicConfigWrapper.getDataVersion().setStateVersion(0L);
+        }
+        return topicConfigWrapper;
+    }
+
+    private RegisterBrokerBody extractRegisterBrokerBodyFromRequest(RemotingCommand request,
+        RegisterBrokerRequestHeader requestHeader) throws RemotingCommandException {
+        RegisterBrokerBody registerBrokerBody = new RegisterBrokerBody();
+
+        if (request.getBody() != null) {
+            try {
+                registerBrokerBody = RegisterBrokerBody.decode(request.getBody(), requestHeader.isCompressed());
+            } catch (Exception e) {
+                throw new RemotingCommandException("Failed to decode RegisterBrokerBody", e);
+            }
+        } else {
+            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setCounter(new AtomicLong(0));
+            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setTimestamp(0L);
+            registerBrokerBody.getTopicConfigSerializeWrapper().getDataVersion().setStateVersion(0L);
+        }
+        return registerBrokerBody;
+    }
+
+    private RemotingCommand getBrokerMemberGroup(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        GetBrokerMemberGroupRequestHeader requestHeader = (GetBrokerMemberGroupRequestHeader) request.decodeCommandCustomHeader(GetBrokerMemberGroupRequestHeader.class);
+
+        BrokerMemberGroup memberGroup = this.namesrvController.getRouteInfoManager()
+            .getBrokerMemberGroup(requestHeader.getClusterName(), requestHeader.getBrokerName());
+
+        GetBrokerMemberGroupResponseBody responseBody = new GetBrokerMemberGroupResponseBody();
+        responseBody.setBrokerMemberGroup(memberGroup);
+
+        RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        response.setCode(ResponseCode.SUCCESS);
+        response.setBody(responseBody.encode());
+        return response;
+    }
+
     private boolean checksum(ChannelHandlerContext ctx, RemotingCommand request,
         RegisterBrokerRequestHeader requestHeader) {
         if (requestHeader.getBodyCrc32() != 0) {
@@ -267,13 +323,13 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         final QueryDataVersionRequestHeader requestHeader =
             (QueryDataVersionRequestHeader) request.decodeCommandCustomHeader(QueryDataVersionRequestHeader.class);
         DataVersion dataVersion = DataVersion.decode(request.getBody(), DataVersion.class);
+        String clusterName = requestHeader.getClusterName();
+        String brokerAddr = requestHeader.getBrokerAddr();
 
-        Boolean changed = this.namesrvController.getRouteInfoManager().isBrokerTopicConfigChanged(requestHeader.getBrokerAddr(), dataVersion);
-        if (!changed) {
-            this.namesrvController.getRouteInfoManager().updateBrokerInfoUpdateTimestamp(requestHeader.getBrokerAddr());
-        }
+        Boolean changed = this.namesrvController.getRouteInfoManager().isBrokerTopicConfigChanged(clusterName, brokerAddr, dataVersion);
+        this.namesrvController.getRouteInfoManager().updateBrokerInfoUpdateTimestamp(clusterName, brokerAddr);
 
-        DataVersion nameSeverDataVersion = this.namesrvController.getRouteInfoManager().queryBrokerTopicConfig(requestHeader.getBrokerAddr());
+        DataVersion nameSeverDataVersion = this.namesrvController.getRouteInfoManager().queryBrokerTopicConfig(clusterName, brokerAddr);
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
 
@@ -284,92 +340,31 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         return response;
     }
 
-    public RemotingCommand registerBroker(ChannelHandlerContext ctx,
-        RemotingCommand request) throws RemotingCommandException {
-        final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterBrokerResponseHeader.class);
-        final RegisterBrokerResponseHeader responseHeader = (RegisterBrokerResponseHeader) response.readCustomHeader();
-        final RegisterBrokerRequestHeader requestHeader =
-            (RegisterBrokerRequestHeader) request.decodeCommandCustomHeader(RegisterBrokerRequestHeader.class);
-
-        if (!checksum(ctx, request, requestHeader)) {
-            response.setCode(ResponseCode.SYSTEM_ERROR);
-            response.setRemark("crc32 not match");
-            return response;
-        }
-
-        TopicConfigSerializeWrapper topicConfigWrapper;
-        if (request.getBody() != null) {
-            topicConfigWrapper = TopicConfigSerializeWrapper.decode(request.getBody(), TopicConfigSerializeWrapper.class);
-        } else {
-            topicConfigWrapper = new TopicConfigSerializeWrapper();
-            topicConfigWrapper.getDataVersion().setCounter(new AtomicLong(0));
-            topicConfigWrapper.getDataVersion().setTimestamp(0);
-        }
-
-        RegisterBrokerResult result = this.namesrvController.getRouteInfoManager().registerBroker(
-            requestHeader.getClusterName(),
-            requestHeader.getBrokerAddr(),
-            requestHeader.getBrokerName(),
-            requestHeader.getBrokerId(),
-            requestHeader.getHaServerAddr(),
-            topicConfigWrapper,
-            null,
-            ctx.channel()
-        );
-
-        responseHeader.setHaServerAddr(result.getHaServerAddr());
-        responseHeader.setMasterAddr(result.getMasterAddr());
-
-        byte[] jsonValue = this.namesrvController.getKvConfigManager().getKVListByNamespace(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG);
-        response.setBody(jsonValue);
-        response.setCode(ResponseCode.SUCCESS);
-        response.setRemark(null);
-        return response;
-    }
-
     public RemotingCommand unregisterBroker(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         final UnRegisterBrokerRequestHeader requestHeader =
             (UnRegisterBrokerRequestHeader) request.decodeCommandCustomHeader(UnRegisterBrokerRequestHeader.class);
 
-        this.namesrvController.getRouteInfoManager().unregisterBroker(
-            requestHeader.getClusterName(),
-            requestHeader.getBrokerAddr(),
-            requestHeader.getBrokerName(),
-            requestHeader.getBrokerId());
+        if (!this.namesrvController.getRouteInfoManager().submitUnRegisterBrokerRequest(requestHeader)) {
+            log.warn("Couldn't submit the unregister broker request to handler, broker info: {}", requestHeader);
+        }
 
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
         return response;
     }
 
-    public RemotingCommand getRouteInfoByTopic(ChannelHandlerContext ctx,
+    public RemotingCommand brokerHeartbeat(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
-        final GetRouteInfoRequestHeader requestHeader =
-            (GetRouteInfoRequestHeader) request.decodeCommandCustomHeader(GetRouteInfoRequestHeader.class);
+        final BrokerHeartbeatRequestHeader requestHeader =
+            (BrokerHeartbeatRequestHeader) request.decodeCommandCustomHeader(BrokerHeartbeatRequestHeader.class);
 
-        TopicRouteData topicRouteData = this.namesrvController.getRouteInfoManager().pickupTopicRouteData(requestHeader.getTopic());
-
-        if (topicRouteData != null) {
-            if (this.namesrvController.getNamesrvConfig().isOrderMessageEnable()) {
-                String orderTopicConf =
-                    this.namesrvController.getKvConfigManager().getKVConfig(NamesrvUtil.NAMESPACE_ORDER_TOPIC_CONFIG,
-                        requestHeader.getTopic());
-                topicRouteData.setOrderTopicConf(orderTopicConf);
-            }
+        this.namesrvController.getRouteInfoManager().updateBrokerInfoUpdateTimestamp(requestHeader.getClusterName(), requestHeader.getBrokerAddr());
 
-            byte[] content = topicRouteData.encode();
-            response.setBody(content);
-            response.setCode(ResponseCode.SUCCESS);
-            response.setRemark(null);
-            return response;
-        }
-
-        response.setCode(ResponseCode.TOPIC_NOT_EXIST);
-        response.setRemark("No topic route info in name server for the topic: " + requestHeader.getTopic()
-            + FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL));
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
         return response;
     }
 
@@ -395,9 +390,9 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
 
         if (ctx != null) {
             log.info("wipe write perm of broker[{}], client: {}, {}",
-                    requestHeader.getBrokerName(),
-                    RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
-                    wipeTopicCnt);
+                requestHeader.getBrokerName(),
+                RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
+                wipeTopicCnt);
         }
 
         responseHeader.setWipeTopicCount(wipeTopicCnt);
@@ -406,7 +401,8 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         return response;
     }
 
-    private RemotingCommand addWritePermOfBroker(ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
+    private RemotingCommand addWritePermOfBroker(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(AddWritePermOfBrokerResponseHeader.class);
         final AddWritePermOfBrokerResponseHeader responseHeader = (AddWritePermOfBrokerResponseHeader) response.readCustomHeader();
         final AddWritePermOfBrokerRequestHeader requestHeader = (AddWritePermOfBrokerRequestHeader) request.decodeCommandCustomHeader(AddWritePermOfBrokerRequestHeader.class);
@@ -414,9 +410,9 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         int addTopicCnt = this.namesrvController.getRouteInfoManager().addWritePermOfBrokerByLock(requestHeader.getBrokerName());
 
         log.info("add write perm of broker[{}], client: {}, {}",
-                requestHeader.getBrokerName(),
-                RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
-                addTopicCnt);
+            requestHeader.getBrokerName(),
+            RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
+            addTopicCnt);
 
         responseHeader.setAddTopicCount(addTopicCnt);
         response.setCode(ResponseCode.SUCCESS);
@@ -426,10 +422,33 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
 
     private RemotingCommand getAllTopicListFromNameserver(ChannelHandlerContext ctx, RemotingCommand request) {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        boolean enableAllTopicList = namesrvController.getNamesrvConfig().isEnableAllTopicList();
+        log.warn("getAllTopicListFromNameserver {} enable {}", ctx.channel().remoteAddress(), enableAllTopicList);
+        if (enableAllTopicList) {
+            byte[] body = this.namesrvController.getRouteInfoManager().getAllTopicList();
+            response.setBody(body);
+            response.setCode(ResponseCode.SUCCESS);
+            response.setRemark(null);
+        } else {
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("disable");
+        }
 
-        byte[] body = this.namesrvController.getRouteInfoManager().getAllTopicList();
+        return response;
+    }
+
+    private RemotingCommand registerTopicToNamesrv(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+        final RegisterTopicRequestHeader requestHeader =
+            (RegisterTopicRequestHeader) request.decodeCommandCustomHeader(RegisterTopicRequestHeader.class);
+
+        TopicRouteData topicRouteData = TopicRouteData.decode(request.getBody(), TopicRouteData.class);
+        if (topicRouteData != null && topicRouteData.getQueueDatas() != null && !topicRouteData.getQueueDatas().isEmpty()) {
+            this.namesrvController.getRouteInfoManager().registerTopic(requestHeader.getTopic(), topicRouteData.getQueueDatas());
+        }
 
-        response.setBody(body);
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
         return response;
@@ -441,7 +460,12 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         final DeleteTopicFromNamesrvRequestHeader requestHeader =
             (DeleteTopicFromNamesrvRequestHeader) request.decodeCommandCustomHeader(DeleteTopicFromNamesrvRequestHeader.class);
 
-        this.namesrvController.getRouteInfoManager().deleteTopic(requestHeader.getTopic());
+        if (requestHeader.getClusterName() != null
+            && !requestHeader.getClusterName().isEmpty()) {
+            this.namesrvController.getRouteInfoManager().deleteTopic(requestHeader.getTopic(), requestHeader.getClusterName());
+        } else {
+            this.namesrvController.getRouteInfoManager().deleteTopic(requestHeader.getTopic());
+        }
 
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
@@ -585,4 +609,25 @@ public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implemen
         return response;
     }
 
+    private RemotingCommand getClientConfigs(ChannelHandlerContext ctx, RemotingCommand request) {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        final GetRemoteClientConfigBody body = GetRemoteClientConfigBody.decode(request.getBody(), GetRemoteClientConfigBody.class);
+
+        String content = this.namesrvController.getConfiguration().getClientConfigsFormatString(body.getKeys());
+        if (content != null && content.length() > 0) {
+            try {
+                response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
+            } catch (UnsupportedEncodingException e) {
+                log.error("getConfig error, ", e);
+                response.setCode(ResponseCode.SYSTEM_ERROR);
+                response.setRemark("UnsupportedEncodingException " + e);
+                return response;
+            }
+        }
+
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+        return response;
+    }
+
 }
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BatchUnRegisterService.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BatchUnRegisterService.java
new file mode 100644
index 0000000..81460e7
--- /dev/null
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BatchUnRegisterService.java
@@ -0,0 +1,85 @@
+/*
+ * 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.namesrv.routeinfo;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.apache.rocketmq.common.ServiceThread;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.namesrv.NamesrvConfig;
+import org.apache.rocketmq.common.protocol.header.namesrv.UnRegisterBrokerRequestHeader;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+
+/**
+ * BatchUnRegisterProcessor provides a mechanism to unregister broker in batch manner, which can speed up broker offline
+ * process.
+ */
+public class BatchUnRegisterService extends ServiceThread {
+    private final RouteInfoManager routeInfoManager;
+    private BlockingQueue<UnRegisterBrokerRequestHeader> unRegisterQueue;
+    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
+
+    public BatchUnRegisterService(RouteInfoManager routeInfoManager, NamesrvConfig namesrvConfig) {
+        this.routeInfoManager = routeInfoManager;
+        this.unRegisterQueue = new LinkedBlockingQueue<>(namesrvConfig.getUnRegisterBrokerQueueCapacity());
+    }
+
+    /**
+     * Submits an unregister request to this queue.
+     *
+     * @param unRegisterRequest the request to submit
+     * @return {@code true} if the request was added to this queue, else {@code false}
+     */
+    public boolean submit(UnRegisterBrokerRequestHeader unRegisterRequest) {
+        return unRegisterQueue.offer(unRegisterRequest);
+    }
+
+    @Override
+    public String getServiceName() {
+        return BatchUnRegisterService.class.getName();
+    }
+
+    @Override
+    public void run() {
+        while (!this.isStopped()) {
+            try {
+                final UnRegisterBrokerRequestHeader request = unRegisterQueue.poll(3, TimeUnit.SECONDS);
+                if (request != null) {
+                    Set<UnRegisterBrokerRequestHeader> unRegisterRequests = new HashSet<>();
+                    unRegisterQueue.drainTo(unRegisterRequests);
+
+                    // Add polled request
+                    unRegisterRequests.add(request);
+
+                    this.routeInfoManager.unRegisterBroker(unRegisterRequests);
+                }
+            } catch (Throwable e) {
+                log.error("Handle unregister broker request failed", e);
+            }
+        }
+    }
+
+    // For test only
+    int queueLength() {
+        return this.unRegisterQueue.size();
+    }
+}
diff --git a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BrokerHousekeepingService.java b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BrokerHousekeepingService.java
index fedb4ae..dbf4786 100644
--- a/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BrokerHousekeepingService.java
+++ b/namesrv/src/main/java/org/apache/rocketmq/namesrv/routeinfo/BrokerHousekeepingService.java
@@ -37,16 +37,16 @@ public class BrokerHousekeepingService implements ChannelEventListener {
 
     @Override
     public void onChannelClose(String remoteAddr, Channel channel) {
-        this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
+        this.namesrvController.getRouteInfoManager().onChannelDestroy(channel);
     }
 
     @Override
     public void onChannelException(String remoteAddr, Channel channel) {
-        this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
+        this.namesrvController.getRouteInfoManager().onChannelDestroy(channel);
     }
 
     @Override
     public void onChannelIdle(String remoteAddr, Channel channel) {
-        this.namesrvController.getRouteInfoManager().onChannelDestroy(remoteAddr, channel);
+        this.namesrvController.getRouteInfoManager().onChannelDestroy(channel);
     }
 }
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 a02d3f1..4becdd6 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
@@ -16,26 +16,34 @@
  */
 package org.apache.rocketmq.namesrv.routeinfo;
 
+import com.google.common.collect.Sets;
 import io.netty.channel.Channel;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.rocketmq.common.DataVersion;
 import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.namesrv.NamesrvConfig;
+import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
+import org.apache.rocketmq.common.protocol.header.NotifyMinBrokerIdChangeRequestHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.UnRegisterBrokerRequestHeader;
 import org.apache.rocketmq.common.statictopic.TopicQueueMappingInfo;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.constant.PermName;
 import org.apache.rocketmq.common.protocol.RequestCode;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
+import org.apache.rocketmq.common.topic.TopicValidator;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
@@ -46,27 +54,61 @@ 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.sysflag.TopicSysFlag;
+import org.apache.rocketmq.namesrv.NamesrvController;
 import org.apache.rocketmq.remoting.common.RemotingUtil;
+import org.apache.rocketmq.remoting.exception.RemotingConnectException;
+import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
+import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
+import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
 public class RouteInfoManager {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);
-    private final static long BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2;
+    private final static long DEFAULT_BROKER_CHANNEL_EXPIRED_TIME = 1000 * 60 * 2;
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
-    private final HashMap<String/* topic */, List<QueueData>> topicQueueTable;
-    private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable;
-    private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
-    private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
-    private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
-    private final HashMap<String/* topic */, Map<String/*brokerName*/, TopicQueueMappingInfo>> topicQueueMappingInfoTable;
+    private final Map<String/* topic */, Map<String, QueueData>> topicQueueTable;
+    private final Map<String/* brokerName */, BrokerData> brokerAddrTable;
+    private final Map<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable;
+    private final Map<BrokerAddrInfo/* brokerAddr */, BrokerLiveInfo> brokerLiveTable;
+    private final Map<BrokerAddrInfo/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
+    private final Map<String/* topic */, Map<String/*brokerName*/, TopicQueueMappingInfo>> topicQueueMappingInfoTable;
+
+    // Contains topic route data needed by clients
+    private final Map<String /* topic */, TopicRouteData> topicRouteDataMap;
+
+    private final BatchUnRegisterService unRegisterService;
+
+    private final NamesrvController namesrvController;
+    private final NamesrvConfig namesrvConfig;
+
+    public RouteInfoManager(final NamesrvConfig namesrvConfig, NamesrvController namesrvController) {
+        this.topicQueueTable = new ConcurrentHashMap<String, Map<String, QueueData>>(1024);
+        this.brokerAddrTable = new ConcurrentHashMap<String, BrokerData>(128);
+        this.clusterAddrTable = new ConcurrentHashMap<String, Set<String>>(32);
+        this.brokerLiveTable = new ConcurrentHashMap<BrokerAddrInfo, BrokerLiveInfo>(256);
+        this.filterServerTable = new ConcurrentHashMap<BrokerAddrInfo, List<String>>(256);
+        this.topicQueueMappingInfoTable = new ConcurrentHashMap<String, Map<String, TopicQueueMappingInfo>>(1024);
+        this.topicRouteDataMap = new ConcurrentHashMap<>(1024);
+        this.unRegisterService = new BatchUnRegisterService(this, namesrvConfig);
+        this.namesrvConfig = namesrvConfig;
+        this.namesrvController = namesrvController;
+    }
+
+    public void start() {
+        this.unRegisterService.start();
+    }
+
+    public void shutdown() {
+        this.unRegisterService.shutdown(true);
+    }
 
+    public boolean submitUnRegisterBrokerRequest(UnRegisterBrokerRequestHeader unRegisterRequest) {
+        return this.unRegisterService.submit(unRegisterRequest);
+    }
 
-    public RouteInfoManager() {
-        this.topicQueueTable = new HashMap<String, List<QueueData>>(1024);
-        this.brokerAddrTable = new HashMap<String, BrokerData>(128);
-        this.clusterAddrTable = new HashMap<String, Set<String>>(32);
-        this.brokerLiveTable = new HashMap<String, BrokerLiveInfo>(256);
-        this.filterServerTable = new HashMap<String, List<String>>(256);
-        this.topicQueueMappingInfoTable = new HashMap<String, Map<String, TopicQueueMappingInfo>>(1024);
+    // For test only
+    int blockedUnRegisterRequests() {
+        return this.unRegisterService.queueLength();
     }
 
     public byte[] getAllClusterInfo() {
@@ -76,11 +118,76 @@ public class RouteInfoManager {
         return clusterInfoSerializeWrapper.encode();
     }
 
+    public void registerTopic(final String topic, List<QueueData> queueDatas) {
+        if (queueDatas == null || queueDatas.isEmpty()) {
+            return;
+        }
+        try {
+            try {
+                this.lock.writeLock().lockInterruptibly();
+                if (this.topicQueueTable.containsKey(topic)) {
+                    log.info("Topic route already exist.{}, {}", topic, this.topicQueueTable.get(topic));
+                } else {
+                    // check and construct queue data map
+                    Map<String, QueueData> queueDataMap = new HashMap<>();
+                    for (QueueData queueData : queueDatas) {
+                        if (!this.brokerAddrTable.containsKey(queueData.getBrokerName())) {
+                            log.warn("Register topic contains illegal broker, {}, {}", topic, queueData);
+                            return;
+                        }
+                        queueDataMap.put(queueData.getBrokerName(), queueData);
+                    }
+
+                    this.topicQueueTable.put(topic, queueDataMap);
+                    log.info("Register topic route:{}, {}", topic, queueDatas);
+
+                    updateTopicRouteData(Sets.newHashSet(topic));
+                }
+            } finally {
+                this.lock.writeLock().unlock();
+            }
+        } catch (Exception e) {
+            log.error("registerTopic Exception", e);
+        }
+    }
+
     public void deleteTopic(final String topic) {
         try {
             try {
                 this.lock.writeLock().lockInterruptibly();
                 this.topicQueueTable.remove(topic);
+                this.updateTopicRouteData(Sets.newHashSet(topic));
+            } finally {
+                this.lock.writeLock().unlock();
+            }
+        } catch (Exception e) {
+            log.error("deleteTopic Exception", e);
+        }
+    }
+
+    public void deleteTopic(final String topic, final String clusterName) {
+        try {
+            try {
+                this.lock.writeLock().lockInterruptibly();
+                Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
+                if (brokerNames != null
+                    && !brokerNames.isEmpty()) {
+                    Map<String, QueueData> queueDataMap = this.topicQueueTable.get(topic);
+                    for (String brokerName : brokerNames) {
+                        final QueueData removedQD = queueDataMap.remove(brokerName);
+                        if (removedQD != null) {
+                            log.info("deleteTopic, remove one broker's topic {} {} {}", brokerName, topic,
+                                removedQD);
+                        }
+
+                        if (queueDataMap.isEmpty()) {
+                            log.info("deleteTopic, remove the topic all queue {} {}", brokerName, topic);
+                            this.topicQueueTable.remove(topic);
+                            break;
+                        }
+                    }
+                    this.updateTopicRouteData(Sets.newHashSet(topic));
+                }
             } finally {
                 this.lock.writeLock().unlock();
             }
@@ -111,6 +218,21 @@ public class RouteInfoManager {
         final String brokerName,
         final long brokerId,
         final String haServerAddr,
+        final Long timeoutMillis,
+        final TopicConfigSerializeWrapper topicConfigWrapper,
+        final List<String> filterServerList,
+        final Channel channel) {
+        return registerBroker(clusterName, brokerAddr, brokerName, brokerId, haServerAddr, timeoutMillis, false, topicConfigWrapper, filterServerList, channel);
+    }
+
+    public RegisterBrokerResult registerBroker(
+        final String clusterName,
+        final String brokerAddr,
+        final String brokerName,
+        final long brokerId,
+        final String haServerAddr,
+        final Long timeoutMillis,
+        final Boolean enableActingMaster,
         final TopicConfigSerializeWrapper topicConfigWrapper,
         final List<String> filterServerList,
         final Channel channel) {
@@ -119,11 +241,7 @@ public class RouteInfoManager {
             try {
                 this.lock.writeLock().lockInterruptibly();
 
-                Set<String> brokerNames = this.clusterAddrTable.get(clusterName);
-                if (null == brokerNames) {
-                    brokerNames = new HashSet<String>();
-                    this.clusterAddrTable.put(clusterName, brokerNames);
-                }
+                Set<String> brokerNames = this.clusterAddrTable.computeIfAbsent(clusterName, k -> new HashSet<>());
                 brokerNames.add(brokerName);
 
                 boolean registerFirst = false;
@@ -131,37 +249,81 @@ public class RouteInfoManager {
                 BrokerData brokerData = this.brokerAddrTable.get(brokerName);
                 if (null == brokerData) {
                     registerFirst = true;
-                    brokerData = new BrokerData(clusterName, brokerName, new HashMap<Long, String>());
+                    brokerData = new BrokerData(clusterName, brokerName, new HashMap<>());
                     this.brokerAddrTable.put(brokerName, brokerData);
                 }
+
+                boolean isOldVersionBroker = enableActingMaster == null;
+                brokerData.setEnableActingMaster(!isOldVersionBroker && enableActingMaster);
+
                 Map<Long, String> brokerAddrsMap = brokerData.getBrokerAddrs();
+
+                boolean isMinBrokerIdChanged = false;
+                long prevMinBrokerId = 0;
+                if (!brokerAddrsMap.isEmpty()) {
+                    prevMinBrokerId = Collections.min(brokerAddrsMap.keySet());
+                }
+
+                if (brokerId < prevMinBrokerId) {
+                    isMinBrokerIdChanged = true;
+                }
+
                 //Switch slave to master: first remove <1, IP:PORT> in namesrv, then add <0, IP:PORT>
                 //The same IP:PORT must only have one record in brokerAddrTable
-                Iterator<Entry<Long, String>> it = brokerAddrsMap.entrySet().iterator();
-                while (it.hasNext()) {
-                    Entry<Long, String> item = it.next();
-                    if (null != brokerAddr && brokerAddr.equals(item.getValue()) && brokerId != item.getKey()) {
-                        it.remove();
+                brokerAddrsMap.entrySet().removeIf(item -> null != brokerAddr && brokerAddr.equals(item.getValue()) && brokerId != item.getKey());
+
+                //If Local brokerId stateVersion bigger than the registering one,
+                String oldBrokerAddr = brokerAddrsMap.get(brokerId);
+                if (null != oldBrokerAddr && !oldBrokerAddr.equals(brokerAddr)) {
+                    BrokerLiveInfo oldBrokerInfo = brokerLiveTable.get(new BrokerAddrInfo(clusterName, oldBrokerAddr));
+
+                    if (null != oldBrokerInfo) {
+                        long oldStateVersion = oldBrokerInfo.getDataVersion().getStateVersion();
+                        long newStateVersion = topicConfigWrapper.getDataVersion().getStateVersion();
+                        if (oldStateVersion > newStateVersion) {
+                            log.warn("Registered Broker conflicts with the existed one, just ignore.: Cluster:{}, BrokerName:{}, BrokerId:{}, " +
+                                    "Old BrokerAddr:{}, Old Version:{}, New BrokerAddr:{}, New Version:{}.",
+                                clusterName, brokerName, brokerId, oldBrokerAddr, oldStateVersion, brokerAddr, newStateVersion);
+                            //Remove the rejected brokerAddr from brokerLiveTable.
+                            brokerLiveTable.remove(new BrokerAddrInfo(clusterName, brokerAddr));
+                            return result;
+                        }
                     }
                 }
 
-                String oldAddr = brokerData.getBrokerAddrs().put(brokerId, brokerAddr);
+                String oldAddr = brokerAddrsMap.put(brokerId, brokerAddr);
                 registerFirst = registerFirst || (null == oldAddr);
 
-                if (null != topicConfigWrapper
-                    && MixAll.MASTER_ID == brokerId) {
-                    if (this.isBrokerTopicConfigChanged(brokerAddr, topicConfigWrapper.getDataVersion())
-                        || registerFirst) {
-                        ConcurrentMap<String, TopicConfig> tcTable =
-                            topicConfigWrapper.getTopicConfigTable();
-                        if (tcTable != null) {
-                            for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
-                                this.createAndUpdateQueueData(brokerName, entry.getValue());
+                Set<String> configChangedTopics = new HashSet<>();
+
+                boolean isMaster = MixAll.MASTER_ID == brokerId;
+                boolean isPrimeSlave = !isOldVersionBroker && !isMaster
+                    && brokerId == Collections.min(brokerAddrsMap.keySet());
+
+                if (null != topicConfigWrapper && (isMaster || isPrimeSlave)) {
+
+                    ConcurrentMap<String, TopicConfig> tcTable =
+                        topicConfigWrapper.getTopicConfigTable();
+                    if (tcTable != null) {
+                        for (Map.Entry<String, TopicConfig> entry : tcTable.entrySet()) {
+                            final TopicConfig topicConfig = entry.getValue();
+                            String topicName = topicConfig.getTopicName();
+                            if (registerFirst || this.isTopicConfigChanged(clusterName, brokerAddr,
+                                topicConfigWrapper.getDataVersion(), brokerName, topicName)) {
+                                configChangedTopics.add(topicName);
+
+                                if (isPrimeSlave) {
+                                    // Wipe the write perm for prime slave
+                                    topicConfig.setPerm(topicConfig.getPerm() & (~PermName.PERM_WRITE));
+                                }
+
+                                this.createAndUpdateQueueData(brokerName, topicConfig);
                             }
                         }
+                    }
 
+                    if (this.isBrokerTopicConfigChanged(clusterName, brokerAddr, topicConfigWrapper.getDataVersion()) || registerFirst) {
                         TopicConfigAndMappingSerializeWrapper mappingSerializeWrapper = TopicConfigAndMappingSerializeWrapper.from(topicConfigWrapper);
-
                         Map<String, TopicQueueMappingInfo> topicQueueMappingInfoMap = mappingSerializeWrapper.getTopicQueueMappingInfoMap();
                         //the topicQueueMappingInfoMap should never be null, but can be empty
                         for (Map.Entry<String, TopicQueueMappingInfo> entry : topicQueueMappingInfoMap.entrySet()) {
@@ -175,34 +337,49 @@ public class RouteInfoManager {
                     }
                 }
 
-                BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddr,
+                BrokerAddrInfo brokerAddrInfo = new BrokerAddrInfo(clusterName, brokerAddr);
+                BrokerLiveInfo prevBrokerLiveInfo = this.brokerLiveTable.put(brokerAddrInfo,
                     new BrokerLiveInfo(
                         System.currentTimeMillis(),
-                        topicConfigWrapper != null ? topicConfigWrapper.getDataVersion() : new DataVersion(),
+                        timeoutMillis == null ? DEFAULT_BROKER_CHANNEL_EXPIRED_TIME : timeoutMillis,
+                        topicConfigWrapper == null ? new DataVersion() : topicConfigWrapper.getDataVersion(),
                         channel,
                         haServerAddr));
                 if (null == prevBrokerLiveInfo) {
-                    log.info("new broker registered, {} HAServer: {}", brokerAddr, haServerAddr);
+                    log.info("new broker registered, {} HAService: {}", brokerAddrInfo, haServerAddr);
                 }
 
                 if (filterServerList != null) {
                     if (filterServerList.isEmpty()) {
-                        this.filterServerTable.remove(brokerAddr);
+                        this.filterServerTable.remove(brokerAddrInfo);
                     } else {
-                        this.filterServerTable.put(brokerAddr, filterServerList);
+                        this.filterServerTable.put(brokerAddrInfo, filterServerList);
                     }
                 }
 
                 if (MixAll.MASTER_ID != brokerId) {
                     String masterAddr = brokerData.getBrokerAddrs().get(MixAll.MASTER_ID);
                     if (masterAddr != null) {
-                        BrokerLiveInfo brokerLiveInfo = this.brokerLiveTable.get(masterAddr);
-                        if (brokerLiveInfo != null) {
-                            result.setHaServerAddr(brokerLiveInfo.getHaServerAddr());
+                        BrokerAddrInfo masterAddrInfo = new BrokerAddrInfo(clusterName, masterAddr);
+                        BrokerLiveInfo masterLiveInfo = this.brokerLiveTable.get(masterAddrInfo);
+                        if (masterLiveInfo != null) {
+                            result.setHaServerAddr(masterLiveInfo.getHaServerAddr());
                             result.setMasterAddr(masterAddr);
                         }
                     }
                 }
+
+                if (registerFirst && MixAll.MASTER_ID != brokerId) {
+                    configChangedTopics = this.topicSetOfBrokerName(brokerName);
+                }
+
+                // Update topicRouteDataMap
+                updateTopicRouteData(configChangedTopics);
+
+                if (isMinBrokerIdChanged && brokerData.isEnableActingMaster()) {
+                    notifyMinBrokerIdChanged(brokerAddrsMap, null,
+                        this.brokerLiveTable.get(brokerAddrInfo).getHaServerAddr());
+                }
             } finally {
                 this.lock.writeLock().unlock();
             }
@@ -213,21 +390,109 @@ public class RouteInfoManager {
         return result;
     }
 
-    public boolean isBrokerTopicConfigChanged(final String brokerAddr, final DataVersion dataVersion) {
-        DataVersion prev = queryBrokerTopicConfig(brokerAddr);
+    private void updateTopicRouteData(final Set<String> changedTopics) {
+        for (final String changedTopic : changedTopics) {
+            if (!this.topicQueueTable.containsKey(changedTopic)) {
+                // This topic doesn't have any queue data route info
+                // Just remove it from topicRouteDataMap
+                this.topicRouteDataMap.remove(changedTopic);
+                continue;
+            }
+
+            TopicRouteData topicRouteData = new TopicRouteData();
+            boolean foundBrokerData = false;
+            List<BrokerData> brokerDataList = topicRouteData.getBrokerDatas();
+            HashMap<String, List<String>> filterServerMap = topicRouteData.getFilterServerTable();
+
+            Map<String, QueueData> queueDataMap = this.topicQueueTable.get(changedTopic);
+            if (queueDataMap != null) {
+                // Deep copy the queue data list
+                List<QueueData> queueDataList = topicRouteData.getQueueDatas();
+                for (final QueueData queueData : queueDataMap.values()) {
+                    queueDataList.add(new QueueData(queueData));
+                }
+
+                Set<String> brokerNameSet = new HashSet<String>(queueDataMap.keySet());
+
+                for (String brokerName : brokerNameSet) {
+                    BrokerData brokerData = this.brokerAddrTable.get(brokerName);
+                    if (null != brokerData) {
+                        BrokerData brokerDataClone = new BrokerData(brokerData);
+                        brokerDataList.add(brokerDataClone);
+                        foundBrokerData = true;
+
+                        for (final String brokerAddr : brokerDataClone.getBrokerAddrs().values()) {
+                            BrokerAddrInfo brokerAddrInfo = new BrokerAddrInfo(brokerDataClone.getCluster(), brokerAddr);
+                            List<String> filterServerList = this.filterServerTable.get(brokerAddrInfo);
+                            if (filterServerList != null) {
+                                filterServerMap.put(brokerAddr, new ArrayList<String>(filterServerList));
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (foundBrokerData) {
+                this.topicRouteDataMap.put(changedTopic, topicRouteData);
+            }
+        }
+    }
+
+    public BrokerMemberGroup getBrokerMemberGroup(String clusterName, String brokerName) {
+        BrokerMemberGroup groupMember = new BrokerMemberGroup(clusterName, brokerName);
+        try {
+            try {
+                this.lock.readLock().lockInterruptibly();
+                final BrokerData brokerData = this.brokerAddrTable.get(brokerName);
+                if (brokerData != null) {
+                    groupMember.getBrokerAddrs().putAll(brokerData.getBrokerAddrs());
+                }
+            } finally {
+                this.lock.readLock().unlock();
+            }
+        } catch (Exception e) {
+            log.error("Get broker member group exception", e);
+        }
+        return groupMember;
+    }
+
+    public boolean isBrokerTopicConfigChanged(final String clusterName, final String brokerAddr,
+        final DataVersion dataVersion) {
+        DataVersion prev = queryBrokerTopicConfig(clusterName, brokerAddr);
         return null == prev || !prev.equals(dataVersion);
     }
 
-    public DataVersion queryBrokerTopicConfig(final String brokerAddr) {
-        BrokerLiveInfo prev = this.brokerLiveTable.get(brokerAddr);
+    public boolean isTopicConfigChanged(final String clusterName, final String brokerAddr,
+        final DataVersion dataVersion, String brokerName, String topic) {
+        boolean isChange = isBrokerTopicConfigChanged(clusterName, brokerAddr, dataVersion);
+        if (isChange) {
+            return true;
+        }
+        final Map<String, QueueData> queueDataMap = this.topicQueueTable.get(topic);
+        if (queueDataMap == null || queueDataMap.isEmpty()) {
+            return true;
+        }
+
+        if (queueDataMap.containsKey(brokerName)) {
+            // The topicQueueTable already contains the broker
+            return false;
+        }
+
+        return true;
+    }
+
+    public DataVersion queryBrokerTopicConfig(final String clusterName, final String brokerAddr) {
+        BrokerAddrInfo addrInfo = new BrokerAddrInfo(clusterName, brokerAddr);
+        BrokerLiveInfo prev = this.brokerLiveTable.get(addrInfo);
         if (prev != null) {
             return prev.getDataVersion();
         }
         return null;
     }
 
-    public void updateBrokerInfoUpdateTimestamp(final String brokerAddr) {
-        BrokerLiveInfo prev = this.brokerLiveTable.get(brokerAddr);
+    public void updateBrokerInfoUpdateTimestamp(final String clusterName, final String brokerAddr) {
+        BrokerAddrInfo addrInfo = new BrokerAddrInfo(clusterName, brokerAddr);
+        BrokerLiveInfo prev = this.brokerLiveTable.get(addrInfo);
         if (prev != null) {
             prev.setLastUpdateTimestamp(System.currentTimeMillis());
         }
@@ -241,81 +506,79 @@ public class RouteInfoManager {
         queueData.setPerm(topicConfig.getPerm());
         queueData.setTopicSysFlag(topicConfig.getTopicSysFlag());
 
-        List<QueueData> queueDataList = this.topicQueueTable.get(topicConfig.getTopicName());
-        if (null == queueDataList) {
-            queueDataList = new LinkedList<QueueData>();
-            queueDataList.add(queueData);
-            this.topicQueueTable.put(topicConfig.getTopicName(), queueDataList);
+        Map<String, QueueData> queueDataMap = this.topicQueueTable.get(topicConfig.getTopicName());
+        if (null == queueDataMap) {
+            queueDataMap = new HashMap<>();
+            queueDataMap.put(brokerName, queueData);
+            this.topicQueueTable.put(topicConfig.getTopicName(), queueDataMap);
             log.info("new topic registered, {} {}", topicConfig.getTopicName(), queueData);
         } else {
-            boolean addNewOne = true;
-
-            Iterator<QueueData> it = queueDataList.iterator();
-            while (it.hasNext()) {
-                QueueData qd = it.next();
-                if (qd.getBrokerName().equals(brokerName)) {
-                    if (qd.equals(queueData)) {
-                        addNewOne = false;
-                    } else {
-                        log.info("topic changed, {} OLD: {} NEW: {}", topicConfig.getTopicName(), qd,
-                            queueData);
-                        it.remove();
-                    }
-                }
-            }
-
-            if (addNewOne) {
-                queueDataList.add(queueData);
+            final QueueData existedQD = queueDataMap.get(brokerName);
+            if (existedQD == null) {
+                queueDataMap.put(brokerName, queueData);
+            } else if (!existedQD.equals(queueData)) {
+                log.info("topic changed, {} OLD: {} NEW: {}", topicConfig.getTopicName(), existedQD,
+                    queueData);
+                queueDataMap.put(brokerName, queueData);
             }
         }
     }
 
     public int wipeWritePermOfBrokerByLock(final String brokerName) {
-        return operateWritePermOfBrokerByLock(brokerName, RequestCode.WIPE_WRITE_PERM_OF_BROKER);
-    }
+        try {
+            try {
+                this.lock.writeLock().lockInterruptibly();
+                return operateWritePermOfBroker(brokerName, RequestCode.WIPE_WRITE_PERM_OF_BROKER);
+            } finally {
+                this.lock.writeLock().unlock();
+            }
+        } catch (Exception e) {
+            log.error("wipeWritePermOfBrokerByLock Exception", e);
+        }
 
-    public int addWritePermOfBrokerByLock(final String brokerName) {
-        return operateWritePermOfBrokerByLock(brokerName, RequestCode.ADD_WRITE_PERM_OF_BROKER);
+        return 0;
     }
 
-    private int operateWritePermOfBrokerByLock(final String brokerName, final int requestCode) {
+    public int addWritePermOfBrokerByLock(final String brokerName) {
         try {
             try {
                 this.lock.writeLock().lockInterruptibly();
-                return operateWritePermOfBroker(brokerName, requestCode);
+                return operateWritePermOfBroker(brokerName, RequestCode.ADD_WRITE_PERM_OF_BROKER);
             } finally {
                 this.lock.writeLock().unlock();
             }
         } catch (Exception e) {
-            log.error("operateWritePermOfBrokerByLock Exception", e);
+            log.error("wipeWritePermOfBrokerByLock Exception", e);
         }
-
         return 0;
     }
 
-
     private int operateWritePermOfBroker(final String brokerName, final int requestCode) {
+        Set<String> changedTopics = new HashSet<>();
         int topicCnt = 0;
-        for (Entry<String, List<QueueData>> entry : this.topicQueueTable.entrySet()) {
-            List<QueueData> qdList = entry.getValue();
-
-            for (QueueData qd : qdList) {
-                if (qd.getBrokerName().equals(brokerName)) {
-                    int perm = qd.getPerm();
-                    switch (requestCode) {
-                        case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
-                            perm &= ~PermName.PERM_WRITE;
-                            break;
-                        case RequestCode.ADD_WRITE_PERM_OF_BROKER:
-                            perm = PermName.PERM_READ | PermName.PERM_WRITE;
-                            break;
-                    }
-                    qd.setPerm(perm);
-                    topicCnt++;
+
+        Iterator<Entry<String, Map<String, QueueData>>> itTopic = this.topicQueueTable.entrySet().iterator();
+        while (itTopic.hasNext()) {
+            Entry<String, Map<String, QueueData>> entry = itTopic.next();
+            Map<String, QueueData> qdMap = entry.getValue();
+
+            final QueueData qd = qdMap.get(brokerName);
+            if (qd != null) {
+                changedTopics.add(entry.getKey());
+                int perm = qd.getPerm();
+                switch (requestCode) {
+                    case RequestCode.WIPE_WRITE_PERM_OF_BROKER:
+                        perm &= ~PermName.PERM_WRITE;
+                        break;
+                    case RequestCode.ADD_WRITE_PERM_OF_BROKER:
+                        perm = PermName.PERM_READ | PermName.PERM_WRITE;
+                        break;
                 }
+                qd.setPerm(perm);
+                topicCnt++;
             }
         }
-
+        this.updateTopicRouteData(changedTopics);
         return topicCnt;
     }
 
@@ -324,52 +587,89 @@ public class RouteInfoManager {
         final String brokerAddr,
         final String brokerName,
         final long brokerId) {
+        UnRegisterBrokerRequestHeader unRegisterBrokerRequest = new UnRegisterBrokerRequestHeader();
+        unRegisterBrokerRequest.setClusterName(clusterName);
+        unRegisterBrokerRequest.setBrokerAddr(brokerAddr);
+        unRegisterBrokerRequest.setBrokerName(brokerName);
+        unRegisterBrokerRequest.setBrokerId(brokerId);
+
+        unRegisterBroker(Sets.newHashSet(unRegisterBrokerRequest));
+    }
+
+    public void unRegisterBroker(Set<UnRegisterBrokerRequestHeader> unRegisterRequests) {
         try {
             try {
+                Set<String> removedBroker = new HashSet<>();
+                Set<String> reducedBroker = new HashSet<>();
+                Map<String, BrokerStatusChangeInfo> needNotifyBrokerMap = new HashMap<>();
+
                 this.lock.writeLock().lockInterruptibly();
-                BrokerLiveInfo brokerLiveInfo = this.brokerLiveTable.remove(brokerAddr);
-                log.info("unregisterBroker, remove from brokerLiveTable {}, {}",
-                    brokerLiveInfo != null ? "OK" : "Failed",
-                    brokerAddr
-                );
+                for (final UnRegisterBrokerRequestHeader unRegisterRequest : unRegisterRequests) {
+                    final String brokerName = unRegisterRequest.getBrokerName();
+                    final String clusterName = unRegisterRequest.getClusterName();
 
-                this.filterServerTable.remove(brokerAddr);
+                    BrokerAddrInfo brokerAddrInfo = new BrokerAddrInfo(clusterName, unRegisterRequest.getBrokerAddr());
 
-                boolean removeBrokerName = false;
-                BrokerData brokerData = this.brokerAddrTable.get(brokerName);
-                if (null != brokerData) {
-                    String addr = brokerData.getBrokerAddrs().remove(brokerId);
-                    log.info("unregisterBroker, remove addr from brokerAddrTable {}, {}",
-                        addr != null ? "OK" : "Failed",
-                        brokerAddr
+                    BrokerLiveInfo brokerLiveInfo = this.brokerLiveTable.remove(brokerAddrInfo);
+                    log.info("unregisterBroker, remove from brokerLiveTable {}, {}",
+                        brokerLiveInfo != null ? "OK" : "Failed",
+                        brokerAddrInfo
                     );
 
-                    if (brokerData.getBrokerAddrs().isEmpty()) {
-                        this.brokerAddrTable.remove(brokerName);
-                        log.info("unregisterBroker, remove name from brokerAddrTable OK, {}",
-                            brokerName
+                    this.filterServerTable.remove(brokerAddrInfo);
+
+                    boolean removeBrokerName = false;
+                    boolean isMinBrokerIdChanged = false;
+                    BrokerData brokerData = this.brokerAddrTable.get(brokerName);
+                    if (null != brokerData) {
+                        if (!brokerData.getBrokerAddrs().isEmpty() &&
+                            unRegisterRequest.getBrokerId().equals(Collections.min(brokerData.getBrokerAddrs().keySet()))) {
+                            isMinBrokerIdChanged = true;
+                        }
+                        String addr = brokerData.getBrokerAddrs().remove(unRegisterRequest.getBrokerId());
+                        log.info("unregisterBroker, remove addr from brokerAddrTable {}, {}",
+                            addr != null ? "OK" : "Failed",
+                            brokerAddrInfo
                         );
+                        if (brokerData.getBrokerAddrs().isEmpty()) {
+                            this.brokerAddrTable.remove(brokerName);
+                            log.info("unregisterBroker, remove name from brokerAddrTable OK, {}",
+                                brokerName
+                            );
 
-                        removeBrokerName = true;
+                            removeBrokerName = true;
+                        } else if (isMinBrokerIdChanged) {
+                            needNotifyBrokerMap.put(brokerName, new BrokerStatusChangeInfo(
+                                brokerData.getBrokerAddrs(), addr, null));
+                        }
                     }
-                }
-
-                if (removeBrokerName) {
-                    Set<String> nameSet = this.clusterAddrTable.get(clusterName);
-                    if (nameSet != null) {
-                        boolean removed = nameSet.remove(brokerName);
-                        log.info("unregisterBroker, remove name from clusterAddrTable {}, {}",
-                            removed ? "OK" : "Failed",
-                            brokerName);
 
-                        if (nameSet.isEmpty()) {
-                            this.clusterAddrTable.remove(clusterName);
-                            log.info("unregisterBroker, remove cluster from clusterAddrTable {}",
-                                clusterName
-                            );
+                    if (removeBrokerName) {
+                        Set<String> nameSet = this.clusterAddrTable.get(clusterName);
+                        if (nameSet != null) {
+                            boolean removed = nameSet.remove(brokerName);
+                            log.info("unregisterBroker, remove name from clusterAddrTable {}, {}",
+                                removed ? "OK" : "Failed",
+                                brokerName);
+
+                            if (nameSet.isEmpty()) {
+                                this.clusterAddrTable.remove(clusterName);
+                                log.info("unregisterBroker, remove cluster from clusterAddrTable {}",
+                                    clusterName
+                                );
+                            }
                         }
+                        removedBroker.add(brokerName);
+                    } else {
+                        reducedBroker.add(brokerName);
                     }
-                    this.removeTopicByBrokerName(brokerName);
+                }
+
+                Set<String> changedTopics = cleanTopicByUnRegisterRequests(removedBroker, reducedBroker);
+                this.updateTopicRouteData(changedTopics);
+
+                if (!needNotifyBrokerMap.isEmpty()) {
+                    notifyMinBrokerIdChanged(needNotifyBrokerMap);
                 }
             } finally {
                 this.lock.writeLock().unlock();
@@ -379,114 +679,189 @@ public class RouteInfoManager {
         }
     }
 
-    private void removeTopicByBrokerName(final String brokerName) {
-        Iterator<Entry<String, List<QueueData>>> itMap = this.topicQueueTable.entrySet().iterator();
+    private Set<String> cleanTopicByUnRegisterRequests(Set<String> removedBroker, Set<String> reducedBroker) {
+        Set<String> changedTopics = new HashSet<>();
+        Iterator<Entry<String, Map<String, QueueData>>> itMap = this.topicQueueTable.entrySet().iterator();
         while (itMap.hasNext()) {
-            Entry<String, List<QueueData>> entry = itMap.next();
+            Entry<String, Map<String, QueueData>> entry = itMap.next();
 
             String topic = entry.getKey();
-            List<QueueData> queueDataList = entry.getValue();
-            Iterator<QueueData> it = queueDataList.iterator();
-            while (it.hasNext()) {
-                QueueData qd = it.next();
-                if (qd.getBrokerName().equals(brokerName)) {
-                    log.info("removeTopicByBrokerName, remove one broker's topic {} {}", topic, qd);
-                    it.remove();
+            Map<String, QueueData> queueDataMap = entry.getValue();
+
+            for (final String brokerName : removedBroker) {
+                final QueueData removedQD = queueDataMap.remove(brokerName);
+                if (removedQD != null) {
+                    changedTopics.add(topic);
+                    log.info("removeTopicByBrokerName, remove one broker's topic {} {}", topic, removedQD);
                 }
             }
 
-            if (queueDataList.isEmpty()) {
+            if (queueDataMap.isEmpty()) {
                 log.info("removeTopicByBrokerName, remove the topic all queue {}", topic);
                 itMap.remove();
             }
+
+            for (final String brokerName : reducedBroker) {
+                final QueueData queueData = queueDataMap.get(brokerName);
+
+                if (queueData != null) {
+                    changedTopics.add(topic);
+                    if (this.brokerAddrTable.get(brokerName).isEnableActingMaster()) {
+                        // Master has been unregistered, wipe the write perm
+                        if (isNoMasterExists(brokerName)) {
+                            queueData.setPerm(queueData.getPerm() & (~PermName.PERM_WRITE));
+                        }
+                    }
+                }
+            }
         }
+
+        return changedTopics;
+    }
+
+    private boolean isNoMasterExists(String brokerName) {
+        final BrokerData brokerData = this.brokerAddrTable.get(brokerName);
+        if (brokerData == null) {
+            return true;
+        }
+
+        if (brokerData.getBrokerAddrs().size() == 0) {
+            return true;
+        }
+
+        return Collections.min(brokerData.getBrokerAddrs().keySet()) > 0;
+    }
+
+    private Set<String> topicSetOfBrokerName(final String brokerName) {
+        Set<String> topicOfBroker = new HashSet<>();
+        for (final Entry<String, Map<String, QueueData>> entry : this.topicQueueTable.entrySet()) {
+            if (entry.getValue().containsKey(brokerName)) {
+                topicOfBroker.add(entry.getKey());
+            }
+        }
+        return topicOfBroker;
     }
 
     public TopicRouteData pickupTopicRouteData(final String topic) {
-        TopicRouteData topicRouteData = new TopicRouteData();
-        boolean foundQueueData = false;
-        boolean foundBrokerData = false;
-        Set<String> brokerNameSet = new HashSet<String>();
-        List<BrokerData> brokerDataList = new LinkedList<BrokerData>();
-        topicRouteData.setBrokerDatas(brokerDataList);
+        if (topic != null) {
+            final TopicRouteData routeData = this.topicRouteDataMap.get(topic);
+            if (routeData == null) {
+                return null;
+            }
+            routeData.setTopicQueueMappingByBroker(this.topicQueueMappingInfoTable.get(topic));
+            log.debug("pickupTopicRouteData {} {}", topic, routeData);
 
-        HashMap<String, List<String>> filterServerMap = new HashMap<String, List<String>>();
-        topicRouteData.setFilterServerTable(filterServerMap);
-        topicRouteData.setTopicQueueMappingByBroker(this.topicQueueMappingInfoTable.get(topic));
+            if (topic.startsWith(TopicValidator.SYNC_BROKER_MEMBER_GROUP_PREFIX)) {
+                return routeData;
+            }
 
-        try {
-            try {
-                this.lock.readLock().lockInterruptibly();
-                List<QueueData> queueDataList = this.topicQueueTable.get(topic);
-                if (queueDataList != null) {
-                    topicRouteData.setQueueDatas(queueDataList);
-                    foundQueueData = true;
+            if (!namesrvConfig.isSupportActingMaster()) {
+                return routeData;
+            }
 
-                    Iterator<QueueData> it = queueDataList.iterator();
-                    while (it.hasNext()) {
-                        QueueData qd = it.next();
-                        brokerNameSet.add(qd.getBrokerName());
-                    }
+            if (routeData.getBrokerDatas().size() == 0 || routeData.getQueueDatas().size() == 0) {
+                return routeData;
+            }
 
-                    for (String brokerName : brokerNameSet) {
-                        BrokerData brokerData = this.brokerAddrTable.get(brokerName);
-                        if (null != brokerData) {
-                            BrokerData brokerDataClone = new BrokerData(brokerData.getCluster(), brokerData.getBrokerName(), (HashMap<Long, String>) brokerData
-                                .getBrokerAddrs().clone());
-                            brokerDataList.add(brokerDataClone);
-                            foundBrokerData = true;
-                            for (final String brokerAddr : brokerDataClone.getBrokerAddrs().values()) {
-                                List<String> filterServerList = this.filterServerTable.get(brokerAddr);
-                                filterServerMap.put(brokerAddr, filterServerList);
-                            }
+            boolean needActingMaster = false;
+
+            for (final BrokerData brokerData : routeData.getBrokerDatas()) {
+                if (brokerData.getBrokerAddrs().size() != 0
+                    && !brokerData.getBrokerAddrs().containsKey(MixAll.MASTER_ID)) {
+                    needActingMaster = true;
+                    break;
+                }
+            }
+
+            if (!needActingMaster) {
+                return routeData;
+            }
+
+            final TopicRouteData cloneTopicRouteData = routeData.deepCloneTopicRouteData();
+
+            for (final BrokerData brokerData : cloneTopicRouteData.getBrokerDatas()) {
+                final HashMap<Long, String> brokerAddrs = brokerData.getBrokerAddrs();
+                if (brokerAddrs.size() == 0 || brokerAddrs.containsKey(MixAll.MASTER_ID) || !brokerData.isEnableActingMaster()) {
+                    continue;
+                }
+
+                // No master
+                for (final QueueData queueData : cloneTopicRouteData.getQueueDatas()) {
+                    if (queueData.getBrokerName().equals(brokerData.getBrokerName())) {
+                        if (!PermName.isWriteable(queueData.getPerm())) {
+                            final Long minBrokerId = Collections.min(brokerAddrs.keySet());
+                            final String actingMasterAddr = brokerAddrs.remove(minBrokerId);
+                            brokerAddrs.put(MixAll.MASTER_ID, actingMasterAddr);
                         }
+                        break;
                     }
                 }
-            } finally {
-                this.lock.readLock().unlock();
-            }
-        } catch (Exception e) {
-            log.error("pickupTopicRouteData Exception", e);
-        }
 
-        log.debug("pickupTopicRouteData {} {}", topic, topicRouteData);
+            }
 
-        if (foundBrokerData && foundQueueData) {
-            return topicRouteData;
+            return cloneTopicRouteData;
         }
-
         return null;
     }
 
     public void scanNotActiveBroker() {
-        Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
-        while (it.hasNext()) {
-            Entry<String, BrokerLiveInfo> next = it.next();
-            long last = next.getValue().getLastUpdateTimestamp();
-            if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
-                RemotingUtil.closeChannel(next.getValue().getChannel());
-                it.remove();
-                log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
-                this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
+        try {
+            log.info("start scanNotActiveBroker");
+            for (Entry<BrokerAddrInfo, BrokerLiveInfo> next : this.brokerLiveTable.entrySet()) {
+                long last = next.getValue().getLastUpdateTimestamp();
+                long timeoutMillis = next.getValue().getHeartbeatTimeoutMillis();
+                if ((last + timeoutMillis) < System.currentTimeMillis()) {
+                    RemotingUtil.closeChannel(next.getValue().getChannel());
+                    log.warn("The broker channel expired, {} {}ms", next.getKey(), timeoutMillis);
+                    this.onChannelDestroy(next.getKey());
+                }
+            }
+        } catch (Exception e) {
+            log.error("scanNotActiveBroker exception", e);
+        }
+    }
+
+    public void onChannelDestroy(BrokerAddrInfo brokerAddrInfo) {
+        UnRegisterBrokerRequestHeader unRegisterRequest = new UnRegisterBrokerRequestHeader();
+        boolean needUnRegister = false;
+        if (brokerAddrInfo != null) {
+            try {
+                try {
+                    this.lock.readLock().lockInterruptibly();
+                    needUnRegister = setupUnRegisterRequest(unRegisterRequest, brokerAddrInfo);
+                } finally {
+                    this.lock.readLock().unlock();
+                }
+            } catch (Exception e) {
+                log.error("onChannelDestroy Exception", e);
             }
         }
+
+        if (needUnRegister) {
+            boolean result = this.submitUnRegisterBrokerRequest(unRegisterRequest);
+            log.info("the broker's channel destroyed, submit the unregister request at once, " +
+                "broker info: {}, submit result: {}", unRegisterRequest, result);
+        }
     }
 
-    public void onChannelDestroy(String remoteAddr, Channel channel) {
-        String brokerAddrFound = null;
+    public void onChannelDestroy(Channel channel) {
+        UnRegisterBrokerRequestHeader unRegisterRequest = new UnRegisterBrokerRequestHeader();
+        BrokerAddrInfo brokerAddrFound = null;
+        boolean needUnRegister = false;
         if (channel != null) {
             try {
                 try {
                     this.lock.readLock().lockInterruptibly();
-                    Iterator<Entry<String, BrokerLiveInfo>> itBrokerLiveTable =
-                        this.brokerLiveTable.entrySet().iterator();
-                    while (itBrokerLiveTable.hasNext()) {
-                        Entry<String, BrokerLiveInfo> entry = itBrokerLiveTable.next();
+                    for (Entry<BrokerAddrInfo, BrokerLiveInfo> entry : this.brokerLiveTable.entrySet()) {
                         if (entry.getValue().getChannel() == channel) {
                             brokerAddrFound = entry.getKey();
                             break;
                         }
                     }
+
+                    if (brokerAddrFound != null) {
+                        needUnRegister = setupUnRegisterRequest(unRegisterRequest, brokerAddrFound);
+                    }
                 } finally {
                     this.lock.readLock().unlock();
                 }
@@ -495,104 +870,93 @@ public class RouteInfoManager {
             }
         }
 
-        if (null == brokerAddrFound) {
-            brokerAddrFound = remoteAddr;
-        } else {
-            log.info("the broker's channel destroyed, {}, clean it's data structure at once", brokerAddrFound);
+        if (needUnRegister) {
+            boolean result = this.submitUnRegisterBrokerRequest(unRegisterRequest);
+            log.info("the broker's channel destroyed, submit the unregister request at once, " +
+                "broker info: {}, submit result: {}", unRegisterRequest, result);
         }
+    }
 
-        if (brokerAddrFound != null && brokerAddrFound.length() > 0) {
+    private boolean setupUnRegisterRequest(UnRegisterBrokerRequestHeader unRegisterRequest,
+        BrokerAddrInfo brokerAddrInfo) {
+        unRegisterRequest.setClusterName(brokerAddrInfo.getClusterName());
+        unRegisterRequest.setBrokerAddr(brokerAddrInfo.getBrokerAddr());
 
-            try {
-                try {
-                    this.lock.writeLock().lockInterruptibly();
-                    this.brokerLiveTable.remove(brokerAddrFound);
-                    this.filterServerTable.remove(brokerAddrFound);
-                    String brokerNameFound = null;
-                    boolean removeBrokerName = false;
-                    Iterator<Entry<String, BrokerData>> itBrokerAddrTable =
-                        this.brokerAddrTable.entrySet().iterator();
-                    while (itBrokerAddrTable.hasNext() && (null == brokerNameFound)) {
-                        BrokerData brokerData = itBrokerAddrTable.next().getValue();
-
-                        Iterator<Entry<Long, String>> it = brokerData.getBrokerAddrs().entrySet().iterator();
-                        while (it.hasNext()) {
-                            Entry<Long, String> entry = it.next();
-                            Long brokerId = entry.getKey();
-                            String brokerAddr = entry.getValue();
-                            if (brokerAddr.equals(brokerAddrFound)) {
-                                brokerNameFound = brokerData.getBrokerName();
-                                it.remove();
-                                log.info("remove brokerAddr[{}, {}] from brokerAddrTable, because channel destroyed",
-                                    brokerId, brokerAddr);
-                                break;
-                            }
-                        }
+        for (Entry<String, BrokerData> stringBrokerDataEntry : this.brokerAddrTable.entrySet()) {
+            BrokerData brokerData = stringBrokerDataEntry.getValue();
+            if (!brokerAddrInfo.getClusterName().equals(brokerData.getCluster())) {
+                continue;
+            }
 
-                        if (brokerData.getBrokerAddrs().isEmpty()) {
-                            removeBrokerName = true;
-                            itBrokerAddrTable.remove();
-                            log.info("remove brokerName[{}] from brokerAddrTable, because channel destroyed",
-                                brokerData.getBrokerName());
-                        }
-                    }
+            for (Entry<Long, String> entry : brokerData.getBrokerAddrs().entrySet()) {
+                Long brokerId = entry.getKey();
+                String brokerAddr = entry.getValue();
+                if (brokerAddr.equals(brokerAddrInfo.getBrokerAddr())) {
+                    unRegisterRequest.setBrokerName(brokerData.getBrokerName());
+                    unRegisterRequest.setBrokerId(brokerId);
+                    return true;
+                }
+            }
+        }
 
-                    if (brokerNameFound != null && removeBrokerName) {
-                        Iterator<Entry<String, Set<String>>> it = this.clusterAddrTable.entrySet().iterator();
-                        while (it.hasNext()) {
-                            Entry<String, Set<String>> entry = it.next();
-                            String clusterName = entry.getKey();
-                            Set<String> brokerNames = entry.getValue();
-                            boolean removed = brokerNames.remove(brokerNameFound);
-                            if (removed) {
-                                log.info("remove brokerName[{}], clusterName[{}] from clusterAddrTable, because channel destroyed",
-                                    brokerNameFound, clusterName);
-
-                                if (brokerNames.isEmpty()) {
-                                    log.info("remove the clusterName[{}] from clusterAddrTable, because channel destroyed and no broker in this cluster",
-                                        clusterName);
-                                    it.remove();
-                                }
+        return false;
+    }
 
-                                break;
-                            }
-                        }
-                    }
+    private void notifyMinBrokerIdChanged(Map<String, BrokerStatusChangeInfo> needNotifyBrokerMap)
+        throws InterruptedException, RemotingConnectException, RemotingTimeoutException, RemotingSendRequestException,
+        RemotingTooMuchRequestException {
+        for (String brokerName : needNotifyBrokerMap.keySet()) {
+            BrokerStatusChangeInfo brokerStatusChangeInfo = needNotifyBrokerMap.get(brokerName);
+            BrokerData brokerData = brokerAddrTable.get(brokerName);
+            if (brokerData != null && brokerData.isEnableActingMaster()) {
+                notifyMinBrokerIdChanged(brokerStatusChangeInfo.getBrokerAddrs(),
+                    brokerStatusChangeInfo.getOfflineBrokerAddr(), brokerStatusChangeInfo.getHaBrokerAddr());
+            }
+        }
+    }
 
-                    if (removeBrokerName) {
-                        Iterator<Entry<String, List<QueueData>>> itTopicQueueTable =
-                            this.topicQueueTable.entrySet().iterator();
-                        while (itTopicQueueTable.hasNext()) {
-                            Entry<String, List<QueueData>> entry = itTopicQueueTable.next();
-                            String topic = entry.getKey();
-                            List<QueueData> queueDataList = entry.getValue();
-
-                            Iterator<QueueData> itQueueData = queueDataList.iterator();
-                            while (itQueueData.hasNext()) {
-                                QueueData queueData = itQueueData.next();
-                                if (queueData.getBrokerName().equals(brokerNameFound)) {
-                                    itQueueData.remove();
-                                    log.info("remove topic[{} {}], from topicQueueTable, because channel destroyed",
-                                        topic, queueData);
-                                }
-                            }
+    private void notifyMinBrokerIdChanged(Map<Long, String> brokerAddrMap, String offlineBrokerAddr,
+        String haBrokerAddr)
+        throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException,
+        RemotingTooMuchRequestException, RemotingConnectException {
+        if (brokerAddrMap == null || brokerAddrMap.isEmpty() || this.namesrvController == null) {
+            return;
+        }
 
-                            if (queueDataList.isEmpty()) {
-                                itTopicQueueTable.remove();
-                                log.info("remove topic[{}] all queue, from topicQueueTable, because channel destroyed",
-                                    topic);
-                            }
-                        }
-                    }
-                } finally {
-                    this.lock.writeLock().unlock();
-                }
-            } catch (Exception e) {
-                log.error("onChannelDestroy Exception", e);
+        NotifyMinBrokerIdChangeRequestHeader requestHeader = new NotifyMinBrokerIdChangeRequestHeader();
+        long minBrokerId = Collections.min(brokerAddrMap.keySet());
+        requestHeader.setMinBrokerId(minBrokerId);
+        requestHeader.setMinBrokerAddr(brokerAddrMap.get(minBrokerId));
+        requestHeader.setOfflineBrokerAddr(offlineBrokerAddr);
+        requestHeader.setHaBrokerAddr(haBrokerAddr);
+
+        List<String> brokerAddrsNotify = chooseBrokerAddrsToNotify(brokerAddrMap, offlineBrokerAddr);
+        log.info("min broker id changed to {}, notify {}, offline broker addr {}", minBrokerId, brokerAddrsNotify, offlineBrokerAddr);
+        RemotingCommand request =
+            RemotingCommand.createRequestCommand(RequestCode.NOTIFY_MIN_BROKER_ID_CHANGE, requestHeader);
+        for (String brokerAddr : brokerAddrsNotify) {
+            this.namesrvController.getRemotingClient().invokeOneway(brokerAddr, request, 300);
+        }
+    }
+
+    private List<String> chooseBrokerAddrsToNotify(Map<Long, String> brokerAddrMap, String offlineBrokerAddr) {
+        if (offlineBrokerAddr != null || brokerAddrMap.size() == 1) {
+            // notify the reset brokers.
+            return new ArrayList<>(brokerAddrMap.values());
+        }
+
+        // new broker registered, notify previous brokers.
+        long minBrokerId = Collections.min(brokerAddrMap.keySet());
+        List<String> brokerAddrList = new ArrayList<>();
+        for (Long brokerId : brokerAddrMap.keySet()) {
+            if (brokerId != minBrokerId) {
+                brokerAddrList.add(brokerAddrMap.get(brokerId));
             }
         }
+        return brokerAddrList;
     }
 
+    // For test only
     public void printAllPeriodically() {
         try {
             try {
@@ -600,9 +964,9 @@ public class RouteInfoManager {
                 log.info("--------------------------------------------------------");
                 {
                     log.info("topicQueueTable SIZE: {}", this.topicQueueTable.size());
-                    Iterator<Entry<String, List<QueueData>>> it = this.topicQueueTable.entrySet().iterator();
+                    Iterator<Entry<String, Map<String, QueueData>>> it = this.topicQueueTable.entrySet().iterator();
                     while (it.hasNext()) {
-                        Entry<String, List<QueueData>> next = it.next();
+                        Entry<String, Map<String, QueueData>> next = it.next();
                         log.info("topicQueueTable Topic: {} {}", next.getKey(), next.getValue());
                     }
                 }
@@ -618,9 +982,9 @@ public class RouteInfoManager {
 
                 {
                     log.info("brokerLiveTable SIZE: {}", this.brokerLiveTable.size());
-                    Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
+                    Iterator<Entry<BrokerAddrInfo, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
                     while (it.hasNext()) {
-                        Entry<String, BrokerLiveInfo> next = it.next();
+                        Entry<BrokerAddrInfo, BrokerLiveInfo> next = it.next();
                         log.info("brokerLiveTable brokerAddr: {} {}", next.getKey(), next.getValue());
                     }
                 }
@@ -680,17 +1044,15 @@ public class RouteInfoManager {
                 this.lock.readLock().lockInterruptibly();
                 Set<String> brokerNameSet = this.clusterAddrTable.get(cluster);
                 for (String brokerName : brokerNameSet) {
-                    Iterator<Entry<String, List<QueueData>>> topicTableIt =
+                    Iterator<Entry<String, Map<String, QueueData>>> topicTableIt =
                         this.topicQueueTable.entrySet().iterator();
                     while (topicTableIt.hasNext()) {
-                        Entry<String, List<QueueData>> topicEntry = topicTableIt.next();
+                        Entry<String, Map<String, QueueData>> topicEntry = topicTableIt.next();
                         String topic = topicEntry.getKey();
-                        List<QueueData> queueDatas = topicEntry.getValue();
-                        for (QueueData queueData : queueDatas) {
-                            if (brokerName.equals(queueData.getBrokerName())) {
-                                topicList.getTopicList().add(topic);
-                                break;
-                            }
+                        Map<String, QueueData> queueDataMap = topicEntry.getValue();
+                        final QueueData qd = queueDataMap.get(brokerName);
+                        if (qd != null) {
+                            topicList.getTopicList().add(topic);
                         }
                     }
                 }
@@ -709,14 +1071,14 @@ public class RouteInfoManager {
         try {
             try {
                 this.lock.readLock().lockInterruptibly();
-                Iterator<Entry<String, List<QueueData>>> topicTableIt =
+                Iterator<Entry<String, Map<String, QueueData>>> topicTableIt =
                     this.topicQueueTable.entrySet().iterator();
                 while (topicTableIt.hasNext()) {
-                    Entry<String, List<QueueData>> topicEntry = topicTableIt.next();
+                    Entry<String, Map<String, QueueData>> topicEntry = topicTableIt.next();
                     String topic = topicEntry.getKey();
-                    List<QueueData> queueDatas = topicEntry.getValue();
+                    Map<String, QueueData> queueDatas = topicEntry.getValue();
                     if (queueDatas != null && queueDatas.size() > 0
-                        && TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSysFlag())) {
+                        && TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                         topicList.getTopicList().add(topic);
                     }
                 }
@@ -735,14 +1097,14 @@ public class RouteInfoManager {
         try {
             try {
                 this.lock.readLock().lockInterruptibly();
-                Iterator<Entry<String, List<QueueData>>> topicTableIt =
+                Iterator<Entry<String, Map<String, QueueData>>> topicTableIt =
                     this.topicQueueTable.entrySet().iterator();
                 while (topicTableIt.hasNext()) {
-                    Entry<String, List<QueueData>> topicEntry = topicTableIt.next();
+                    Entry<String, Map<String, QueueData>> topicEntry = topicTableIt.next();
                     String topic = topicEntry.getKey();
-                    List<QueueData> queueDatas = topicEntry.getValue();
+                    Map<String, QueueData> queueDatas = topicEntry.getValue();
                     if (queueDatas != null && queueDatas.size() > 0
-                        && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSysFlag())) {
+                        && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                         topicList.getTopicList().add(topic);
                     }
                 }
@@ -761,15 +1123,15 @@ public class RouteInfoManager {
         try {
             try {
                 this.lock.readLock().lockInterruptibly();
-                Iterator<Entry<String, List<QueueData>>> topicTableIt =
+                Iterator<Entry<String, Map<String, QueueData>>> topicTableIt =
                     this.topicQueueTable.entrySet().iterator();
                 while (topicTableIt.hasNext()) {
-                    Entry<String, List<QueueData>> topicEntry = topicTableIt.next();
+                    Entry<String, Map<String, QueueData>> topicEntry = topicTableIt.next();
                     String topic = topicEntry.getKey();
-                    List<QueueData> queueDatas = topicEntry.getValue();
+                    Map<String, QueueData> queueDatas = topicEntry.getValue();
                     if (queueDatas != null && queueDatas.size() > 0
-                        && !TopicSysFlag.hasUnitFlag(queueDatas.get(0).getTopicSysFlag())
-                        && TopicSysFlag.hasUnitSubFlag(queueDatas.get(0).getTopicSysFlag())) {
+                        && !TopicSysFlag.hasUnitFlag(queueDatas.values().iterator().next().getTopicSysFlag())
+                        && TopicSysFlag.hasUnitSubFlag(queueDatas.values().iterator().next().getTopicSysFlag())) {
                         topicList.getTopicList().add(topic);
                     }
                 }
@@ -784,15 +1146,82 @@ public class RouteInfoManager {
     }
 }
 
+/**
+ * broker address information
+ */
+class BrokerAddrInfo {
+    private String clusterName;
+    private String brokerAddr;
+
+    private int hash;
+
+    public BrokerAddrInfo(String clusterName, String brokerAddr) {
+        this.clusterName = clusterName;
+        this.brokerAddr = brokerAddr;
+    }
+
+    public String getClusterName() {
+        return clusterName;
+    }
+
+    public String getBrokerAddr() {
+        return brokerAddr;
+    }
+
+    public boolean isEmpty() {
+        return clusterName.isEmpty() && brokerAddr.isEmpty();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+
+        if (obj instanceof BrokerAddrInfo) {
+            BrokerAddrInfo addr = (BrokerAddrInfo) obj;
+            return clusterName.equals(addr.clusterName) && brokerAddr.equals(addr.brokerAddr);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        int h = hash;
+        if (h == 0 && clusterName.length() + brokerAddr.length() > 0) {
+            for (int i = 0; i < clusterName.length(); i++) {
+                h = 31 * h + clusterName.charAt(i);
+            }
+            h = 31 * h + '_';
+            for (int i = 0; i < brokerAddr.length(); i++) {
+                h = 31 * h + brokerAddr.charAt(i);
+            }
+            hash = h;
+        }
+        return h;
+    }
+
+    @Override
+    public String toString() {
+        return "BrokerAddrInfo [clusterName=" + clusterName + ", brokerAddr=" + brokerAddr + "]";
+    }
+}
+
 class BrokerLiveInfo {
     private long lastUpdateTimestamp;
+    private long heartbeatTimeoutMillis;
     private DataVersion dataVersion;
     private Channel channel;
     private String haServerAddr;
 
-    public BrokerLiveInfo(long lastUpdateTimestamp, DataVersion dataVersion, Channel channel,
+    public BrokerLiveInfo(long lastUpdateTimestamp, long heartbeatTimeoutMillis, DataVersion dataVersion,
+        Channel channel,
         String haServerAddr) {
         this.lastUpdateTimestamp = lastUpdateTimestamp;
+        this.heartbeatTimeoutMillis = heartbeatTimeoutMillis;
         this.dataVersion = dataVersion;
         this.channel = channel;
         this.haServerAddr = haServerAddr;
@@ -806,6 +1235,14 @@ class BrokerLiveInfo {
         this.lastUpdateTimestamp = lastUpdateTimestamp;
     }
 
+    public long getHeartbeatTimeoutMillis() {
+        return heartbeatTimeoutMillis;
+    }
+
+    public void setHeartbeatTimeoutMillis(long heartbeatTimeoutMillis) {
+        this.heartbeatTimeoutMillis = heartbeatTimeoutMillis;
+    }
+
     public DataVersion getDataVersion() {
         return dataVersion;
     }
@@ -836,3 +1273,39 @@ class BrokerLiveInfo {
             + ", channel=" + channel + ", haServerAddr=" + haServerAddr + "]";
     }
 }
+
+class BrokerStatusChangeInfo {
+    Map<Long, String> brokerAddrs;
+    String offlineBrokerAddr;
+    String haBrokerAddr;
+
+    public BrokerStatusChangeInfo(Map<Long, String> brokerAddrs, String offlineBrokerAddr, String haBrokerAddr) {
+        this.brokerAddrs = brokerAddrs;
+        this.offlineBrokerAddr = offlineBrokerAddr;
+        this.haBrokerAddr = haBrokerAddr;
+    }
+
+    public Map<Long, String> getBrokerAddrs() {
+        return brokerAddrs;
+    }
+
+    public void setBrokerAddrs(Map<Long, String> brokerAddrs) {
+        this.brokerAddrs = brokerAddrs;
+    }
+
+    public String getOfflineBrokerAddr() {
+        return offlineBrokerAddr;
+    }
+
+    public void setOfflineBrokerAddr(String offlineBrokerAddr) {
+        this.offlineBrokerAddr = offlineBrokerAddr;
+    }
+
+    public String getHaBrokerAddr() {
+        return haBrokerAddr;
+    }
+
+    public void setHaBrokerAddr(String haBrokerAddr) {
+        this.haBrokerAddr = haBrokerAddr;
+    }
+}
diff --git a/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/RequestProcessorTest.java b/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/RequestProcessorTest.java
new file mode 100644
index 0000000..8f91d5b
--- /dev/null
+++ b/namesrv/src/test/java/org/apache/rocketmq/namesrv/processor/RequestProcessorTest.java
@@ -0,0 +1,563 @@
+/*
+ * 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.namesrv.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.namesrv.NamesrvConfig;
+import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
+import org.apache.rocketmq.common.protocol.RequestCode;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
+import org.apache.rocketmq.common.protocol.header.namesrv.DeleteKVConfigRequestHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.GetKVConfigRequestHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.GetKVConfigResponseHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.PutKVConfigRequestHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.RegisterBrokerRequestHeader;
+import org.apache.rocketmq.common.protocol.route.BrokerData;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.namesrv.NamesrvController;
+import org.apache.rocketmq.namesrv.routeinfo.RouteInfoManager;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyServerConfig;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.assertj.core.util.Maps;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RequestProcessorTest {
+    private DefaultRequestProcessor defaultRequestProcessor;
+
+    private ClientRequestProcessor clientRequestProcessor;
+
+    private NamesrvController namesrvController;
+
+    private NamesrvConfig namesrvConfig;
+
+    private NettyServerConfig nettyServerConfig;
+
+    private RouteInfoManager routeInfoManager;
+
+    private InternalLogger logger;
+
+    @Before
+    public void init() throws Exception {
+        namesrvConfig = new NamesrvConfig();
+        namesrvConfig.setEnableAllTopicList(true);
+        nettyServerConfig = new NettyServerConfig();
+        routeInfoManager = new RouteInfoManager(new NamesrvConfig(), null);
+
+        namesrvController = new NamesrvController(namesrvConfig, nettyServerConfig);
+
+        Field field = NamesrvController.class.getDeclaredField("routeInfoManager");
+        field.setAccessible(true);
+        field.set(namesrvController, routeInfoManager);
+        defaultRequestProcessor = new DefaultRequestProcessor(namesrvController);
+
+        clientRequestProcessor = new ClientRequestProcessor(namesrvController);
+
+
+        registerRouteInfoManager();
+
+        logger = mock(InternalLogger.class);
+        setFinalStatic(DefaultRequestProcessor.class.getDeclaredField("log"), logger);
+    }
+
+    @Test
+    public void testProcessRequest_PutKVConfig() throws RemotingCommandException {
+        PutKVConfigRequestHeader header = new PutKVConfigRequestHeader();
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.PUT_KV_CONFIG,
+            header);
+        request.addExtField("namespace", "namespace");
+        request.addExtField("key", "key");
+        request.addExtField("value", "value");
+
+        RemotingCommand response = defaultRequestProcessor.processRequest(null, request);
+
+        assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+        assertThat(response.getRemark()).isNull();
+
+        assertThat(namesrvController.getKvConfigManager().getKVConfig("namespace", "key"))
+            .isEqualTo("value");
+    }
+
+    @Test
+    public void testProcessRequest_GetKVConfigReturnNotNull() throws RemotingCommandException {
+        namesrvController.getKvConfigManager().putKVConfig("namespace", "key", "value");
+
+        GetKVConfigRequestHeader header = new GetKVConfigRequestHeader();
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_KV_CONFIG,
+            header);
+        request.addExtField("namespace", "namespace");
+        request.addExtField("key", "key");
+
+        RemotingCommand response = defaultRequestProcessor.processRequest(null, request);
+
+        assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+        assertThat(response.getRemark()).isNull();
+
+        GetKVConfigResponseHeader responseHeader = (GetKVConfigResponseHeader) response
+            .readCustomHeader();
+
+        assertThat(responseHeader.getValue()).isEqualTo("value");
+    }
+
+    @Test
+    public void testProcessRequest_GetKVConfigReturnNull() throws RemotingCommandException {
+        GetKVConfigRequestHeader header = new GetKVConfigRequestHeader();
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_KV_CONFIG,
+            header);
+        request.addExtField("namespace", "namespace");
+        request.addExtField("key", "key");
+
+        RemotingCommand response = defaultRequestProcessor.processRequest(null, request);
+
+        assertThat(response.getCode()).isEqualTo(ResponseCode.QUERY_NOT_FOUND);
+        assertThat(response.getRemark()).isEqualTo("No config item, Namespace: namespace Key: key");
+
+        GetKVConfigResponseHeader responseHeader = (GetKVConfigResponseHeader) response
+            .readCustomHeader();
+
+        assertThat(responseHeader.getValue()).isNull();
+    }
+
+    @Test
+    public void testProcessRequest_DeleteKVConfig() throws RemotingCommandException {
+        namesrvController.getKvConfigManager().putKVConfig("namespace", "key", "value");
+
+        DeleteKVConfigRequestHeader header = new DeleteKVConfigRequestHeader();
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.DELETE_KV_CONFIG,
+            header);
+        request.addExtField("namespace", "namespace");
+        request.addExtField("key", "key");
+
+        RemotingCommand response = defaultRequestProcessor.processRequest(null, request);
+
+        assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+        assertThat(response.getRemark()).isNull();
+
+        assertThat(namesrvController.getKvConfigManager().getKVConfig("namespace", "key"))
+            .isNull();
+    }
+
+    @Test
+    public void testProcessRequest_UnSupportedRequest() throws RemotingCommandException {
+        final RemotingCommand unSupportedRequest = RemotingCommand.createRequestCommand(99999, null);
+        final RemotingCommand response = defaultRequestProcessor.processRequest(null, unSupportedRequest);
+
+        assertThat(response).isNotNull();
+        assertThat(response.getCode()).isEqualTo(ResponseCode.REQUEST_CODE_NOT_SUPPORTED);
+    }
+
+    @Test
+    public void testProcessRequest_RegisterBroker() throws RemotingCommandException,
+        NoSuchFieldException, IllegalAccessException {
+        RemotingCommand request = genSampleRegisterCmd(true);
+
+        ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+        when(ctx.channel()).thenReturn(null);
+
+        RemotingCommand response = defaultRequestProcessor.processRequest(ctx, request);
+
+        assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+        assertThat(response.getRemark()).isNull();
+
+        RouteInfoManager routes = namesrvController.getRouteInfoManager();
+        Field brokerAddrTable = RouteInfoManager.class.getDeclaredField("brokerAddrTable");
+        brokerAddrTable.setAccessible(true);
+
+        BrokerData broker = new BrokerData();
+        broker.setBrokerName("broker");
+        broker.setBrokerAddrs((HashMap) Maps.newHashMap(new Long(2333), "10.10.1.1"));
+
+        assertThat((Map) brokerAddrTable.get(routes))
+            .contains(new HashMap.SimpleEntry("broker", broker));
+    }
+
+    /*@Test
+    public void testProcessRequest_RegisterBrokerLogicalQueue() throws Exception {
+        String cluster = "cluster";
+        String broker1Name = "broker1";
+        String broker1Addr = "10.10.1.1";
+        String broker2Name = "broker2";
+        String broker2Addr = "10.10.1.2";
+        String topic = "foobar";
+
+        LogicalQueueRouteData queueRouteData1 = new LogicalQueueRouteData(0, 0, new MessageQueue(topic, broker1Name, 0), MessageQueueRouteState.ReadOnly, 0, 10, 100, 100, broker1Addr);
+        {
+            RegisterBrokerRequestHeader header = new RegisterBrokerRequestHeader();
+            header.setBrokerName(broker1Name);
+            RemotingCommand request = RemotingCommand.createRequestCommand(
+                RequestCode.REGISTER_BROKER, header);
+            request.addExtField("brokerName", broker1Name);
+            request.addExtField("brokerAddr", broker1Addr);
+            request.addExtField("clusterName", cluster);
+            request.addExtField("haServerAddr", "10.10.2.1");
+            request.addExtField("brokerId", String.valueOf(MixAll.MASTER_ID));
+            request.setVersion(MQVersion.CURRENT_VERSION);
+            TopicConfigSerializeWrapper topicConfigSerializeWrapper = new TopicConfigSerializeWrapper();
+            topicConfigSerializeWrapper.setTopicConfigTable(new ConcurrentHashMap<>(Collections.singletonMap(topic, new TopicConfig(topic))));
+            topicConfigSerializeWrapper.setLogicalQueuesInfoMap(Maps.newHashMap(topic, new LogicalQueuesInfo(Collections.singletonMap(0, Lists.newArrayList(
+                queueRouteData1
+            )))));
+            topicConfigSerializeWrapper.setDataVersion(new DataVersion());
+            RegisterBrokerBody requestBody = new RegisterBrokerBody();
+            requestBody.setTopicConfigSerializeWrapper(topicConfigSerializeWrapper);
+            requestBody.setFilterServerList(Lists.<String>newArrayList());
+            request.setBody(requestBody.encode());
+
+            ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+            when(ctx.channel()).thenReturn(null);
+
+            RemotingCommand response = defaultRequestProcessor.processRequest(ctx, request);
+
+            assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+            assertThat(response.getRemark()).isNull();
+        }
+        LogicalQueueRouteData queueRouteData2 = new LogicalQueueRouteData(0, 100, new MessageQueue(topic, broker2Name, 0), MessageQueueRouteState.Normal, 0, -1, -1, -1, broker2Addr);
+        LogicalQueueRouteData queueRouteData3 = new LogicalQueueRouteData(1, 100, new MessageQueue(topic, broker2Name, 0), MessageQueueRouteState.Normal, 0, -1, -1, -1, broker2Addr);
+        {
+            RegisterBrokerRequestHeader header = new RegisterBrokerRequestHeader();
+            header.setBrokerName(broker2Name);
+            RemotingCommand request = RemotingCommand.createRequestCommand(
+                RequestCode.REGISTER_BROKER, header);
+            request.addExtField("brokerName", broker2Name);
+            request.addExtField("brokerAddr", broker2Addr);
+            request.addExtField("clusterName", cluster);
+            request.addExtField("haServerAddr", "10.10.2.1");
+            request.addExtField("brokerId", String.valueOf(MixAll.MASTER_ID));
+            request.setVersion(MQVersion.CURRENT_VERSION);
+            TopicConfigSerializeWrapper topicConfigSerializeWrapper = new TopicConfigSerializeWrapper();
+            topicConfigSerializeWrapper.setTopicConfigTable(new ConcurrentHashMap<>(Collections.singletonMap(topic, new TopicConfig(topic))));
+            topicConfigSerializeWrapper.setLogicalQueuesInfoMap(Maps.newHashMap(topic, new LogicalQueuesInfo(ImmutableMap.of(
+                0, Collections.singletonList(queueRouteData2),
+                1, Collections.singletonList(queueRouteData3)
+            ))));
+            topicConfigSerializeWrapper.setDataVersion(new DataVersion());
+            RegisterBrokerBody requestBody = new RegisterBrokerBody();
+            requestBody.setTopicConfigSerializeWrapper(topicConfigSerializeWrapper);
+            requestBody.setFilterServerList(Lists.<String>newArrayList());
+            request.setBody(requestBody.encode());
+
+            ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
+            when(ctx.channel()).thenReturn(null);
+
+            RemotingCommand response = defaultRequestProcessor.processRequest(ctx, request);
+
+            assertThat(response.getCode()).isEqualTo(ResponseCode.SUCCESS);
+            assertThat(response.getRemark()).isNull();
... 1575 lines suppressed ...