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:06:52 UTC

[rocketmq] branch 5.0.0-beta-tmp updated: feature(broker & acl & client):[RIP-32]Support Slave Acting Master mode (#3978)

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 503d141  feature(broker & acl & client):[RIP-32]Support Slave Acting Master mode (#3978)
503d141 is described below

commit 503d141e508113a6b63e3b4bea5ec7cdad1d2ca5
Author: rongtong <ji...@163.com>
AuthorDate: Mon Mar 14 14:06:45 2022 +0800

    feature(broker & acl & client):[RIP-32]Support Slave Acting Master mode (#3978)
    
    Co-authored-by: rongtong.jrt <ro...@alibaba-inc.com>
---
 .../rocketmq/acl/common/AclClientRPCHook.java      |    4 +
 acl/src/test/resources/conf/plain_acl.yml          |   17 -
 broker/pom.xml                                     |   12 +-
 .../apache/rocketmq/broker/BrokerController.java   | 1407 +++++++++++++-------
 .../org/apache/rocketmq/broker/BrokerStartup.java  |   58 +-
 ...merIdsChangeListener.java => ShutdownHook.java} |   12 +-
 .../broker/client/ClientHousekeepingService.java   |   12 +-
 .../rocketmq/broker/client/ConsumerGroupInfo.java  |   25 +-
 .../broker/client/ConsumerIdsChangeListener.java   |    2 +
 .../rocketmq/broker/client/ConsumerManager.java    |   42 +-
 .../client/DefaultConsumerIdsChangeListener.java   |   68 +-
 .../rocketmq/broker/client/ProducerManager.java    |   23 +-
 .../rocketmq/broker/client/net/Broker2Client.java  |    8 +-
 .../client/rebalance/RebalanceLockManager.java     |   90 +-
 .../broker/dledger/DLedgerRoleChangeHandler.java   |   86 +-
 .../rocketmq/broker/failover/EscapeBridge.java     |  268 ++++
 .../broker/filtersrv/FilterServerManager.java      |    6 +-
 .../rocketmq/broker/latency/BrokerFastFailure.java |   44 +-
 .../broker/loadbalance/AssignmentManager.java      |   27 +-
 .../broker/longpolling/ManyPullRequest.java        |    4 +
 .../{PopRequest.java => NotificationRequest.java}  |   42 +-
 .../longpolling/NotifyMessageArrivingListener.java |   11 +-
 .../rocketmq/broker/longpolling/PopRequest.java    |    4 +-
 .../broker/longpolling/PullRequestHoldService.java |   36 +-
 .../broker/mqtrace/AbortProcessException.java      |   69 +
 .../broker/mqtrace/ConsumeMessageContext.java      |   77 +-
 .../broker/mqtrace/SendMessageContext.java         |  102 +-
 .../broker/offset/ConsumerOffsetManager.java       |   85 +-
 .../apache/rocketmq/broker/out/BrokerOuterAPI.java |  566 +++++++-
 .../broker/pagecache/OneMessageTransfer.java       |    1 +
 .../broker/plugin/AbstractPluginMessageStore.java  |  286 +++-
 .../broker/plugin/BrokerAttachedPlugin.java        |   74 +
 .../broker/plugin/MessageStoreFactory.java         |   15 +-
 .../broker/plugin/MessageStorePluginContext.java   |   16 +-
 .../broker/plugin/PullMessageResultHandler.java    |   53 +
 .../processor/AbstractSendMessageProcessor.java    |  378 +++++-
 .../broker/processor/AckMessageProcessor.java      |   74 +-
 .../broker/processor/AdminBrokerProcessor.java     |  635 +++++++--
 .../processor/ChangeInvisibleTimeProcessor.java    |   31 +-
 .../broker/processor/ClientManageProcessor.java    |   51 +-
 .../broker/processor/ConsumerManageProcessor.java  |   26 +-
 .../processor/DefaultPullMessageResultHandler.java |  244 ++++
 .../broker/processor/EndTransactionProcessor.java  |    9 +-
 .../broker/processor/ForwardRequestProcessor.java  |    3 +-
 .../broker/processor/NotificationProcessor.java    |  318 +++++
 .../broker/processor/PeekMessageProcessor.java     |  271 ++++
 .../broker/processor/PollingInfoProcessor.java     |  119 ++
 .../broker/processor/PopBufferMergeService.java    |  111 +-
 .../broker/processor/PopMessageProcessor.java      |  227 ++--
 .../broker/processor/PopReviveService.java         |  136 +-
 .../broker/processor/PullMessageProcessor.java     |  400 +++---
 .../broker/processor/QueryMessageProcessor.java    |   14 +-
 .../broker/processor/ReplyMessageProcessor.java    |   13 +-
 .../SendMessageCallback.java}                      |   15 +-
 .../broker/processor/SendMessageProcessor.java     |  593 +++------
 .../schedule/DelayOffsetSerializeWrapper.java      |   45 +
 .../broker/schedule/ScheduleMessageService.java    |  851 ++++++++++++
 .../rocketmq/broker/slave/SlaveSynchronize.java    |   28 +-
 .../subscription/SubscriptionGroupManager.java     |  112 +-
 .../rocketmq/broker/topic/TopicConfigManager.java  |  150 ++-
 .../topic/TopicQueueMappingCleanService.java       |    3 +
 .../broker/topic/TopicQueueMappingManager.java     |    4 +-
 .../AbstractTransactionalMessageCheckListener.java |   45 +-
 .../broker/transaction/OperationResult.java        |    8 +-
 .../TransactionalMessageCheckService.java          |    3 +
 .../transaction/TransactionalMessageService.java   |    2 +-
 .../DefaultTransactionalMessageCheckListener.java  |    2 +-
 .../queue/TransactionalMessageBridge.java          |    2 +-
 .../queue/TransactionalMessageServiceImpl.java     |    2 +-
 .../org/apache/rocketmq/broker/util/HookUtils.java |  164 +++
 .../org/apache/rocketmq/broker/util/MsgUtil.java   |    4 +-
 .../rocketmq/broker/BrokerControllerTest.java      |    3 +-
 .../apache/rocketmq/broker/BrokerOuterAPITest.java |   33 +-
 .../broker/filter/MessageStoreWithFilterTest.java  |   56 +-
 .../broker/offset/ConsumerOffsetManagerTest.java   |   38 +
 .../AbstractSendMessageProcessorTest.java          |    2 -
 .../broker/processor/AckMessageProcessorTest.java  |   15 +-
 .../broker/processor/AdminBrokerProcessorTest.java |   43 +-
 .../ChangeInvisibleTimeProcessorTest.java          |   11 +-
 .../processor/ClientManageProcessorTest.java       |    1 -
 .../processor/ConsumerManageProcessorTest.java     |   91 ++
 .../processor/EndTransactionProcessorTest.java     |    2 +-
 .../processor/PopBufferMergeServiceTest.java       |    8 +-
 .../broker/processor/PopMessageProcessorTest.java  |    9 +-
 .../broker/processor/PullMessageProcessorTest.java |   18 +-
 .../processor/QueryAssignmentProcessorTest.java    |    1 -
 .../processor/ReplyMessageProcessorTest.java       |    2 +-
 .../broker/processor/SendMessageProcessorTest.java |  206 ++-
 .../schedule/ScheduleMessageServiceTest.java       |  287 ++++
 .../broker/substription/ForbiddenTest.java         |   64 +
 .../broker/topic/TopicConfigManagerTest.java       |    7 +-
 ...faultTransactionalMessageCheckListenerTest.java |    2 +-
 .../queue/TransactionalMessageBridgeTest.java      |    2 +-
 .../queue/TransactionalMessageServiceImplTest.java |    2 +-
 .../rocketmq/broker/util/ServiceProviderTest.java  |    7 +-
 .../util/TransactionalMessageServiceImpl.java      |    2 +-
 .../rocketmq/client/impl/BaseInvokeCallback.java   |    1 +
 .../client/impl/ClientRemotingProcessor.java       |    3 +-
 .../apache/rocketmq/client/impl/MQAdminImpl.java   |   44 +-
 .../rocketmq/client/impl/MQClientAPIImpl.java      |  289 +++-
 .../client/impl/factory/MQClientInstance.java      |   16 +-
 .../impl/producer/DefaultMQProducerImpl.java       |    1 -
 .../client/producer/DefaultMQProducer.java         |    6 +-
 .../consumer/DefaultLitePullConsumerTest.java      |    2 +-
 .../rocketmq/client/impl/MQClientAPIImplTest.java  |    1 -
 .../consumer/DefaultMQPushConsumerImplTest.java    |    6 -
 .../client/producer/DefaultMQProducerTest.java     |    2 +-
 .../selector/SelectMessageQueueRetryTest.java      |    1 -
 .../trace/TransactionMQProducerWithTraceTest.java  |   11 +-
 109 files changed, 7872 insertions(+), 2168 deletions(-)

diff --git a/acl/src/main/java/org/apache/rocketmq/acl/common/AclClientRPCHook.java b/acl/src/main/java/org/apache/rocketmq/acl/common/AclClientRPCHook.java
index 9e5bf1f..50c73ca 100644
--- a/acl/src/main/java/org/apache/rocketmq/acl/common/AclClientRPCHook.java
+++ b/acl/src/main/java/org/apache/rocketmq/acl/common/AclClientRPCHook.java
@@ -56,6 +56,10 @@ public class AclClientRPCHook implements RPCHook {
 
     }
 
+    @Override public void doAfterRpcFailure(String remoteAddr, RemotingCommand request, Boolean remoteTimeout) {
+
+    }
+
     protected SortedMap<String, String> parseRequestContent(RemotingCommand request, String ak, String securityToken) {
         CommandCustomHeader header = request.readCustomHeader();
         // Sort property
diff --git a/acl/src/test/resources/conf/plain_acl.yml b/acl/src/test/resources/conf/plain_acl.yml
index 59bd6d4..40d66d9 100644
--- a/acl/src/test/resources/conf/plain_acl.yml
+++ b/acl/src/test/resources/conf/plain_acl.yml
@@ -1,20 +1,3 @@
-# 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.
-
-## suggested format
-
 globalWhiteRemoteAddresses:
 - 10.10.103.*
 - 192.168.0.*
diff --git a/broker/pom.xml b/broker/pom.xml
index 31a10cc..8e52c19 100644
--- a/broker/pom.xml
+++ b/broker/pom.xml
@@ -47,14 +47,18 @@
             <artifactId>rocketmq-filter</artifactId>
         </dependency>
         <dependency>
-            <groupId>${project.groupId}</groupId>
-            <artifactId>rocketmq-acl</artifactId>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
         </dependency>
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
         </dependency>
         <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
         </dependency>
@@ -74,6 +78,10 @@
             <groupId>com.googlecode.concurrentlinkedhashmap</groupId>
             <artifactId>concurrentlinkedhashmap-lru</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.rocketmq</groupId>
+            <artifactId>rocketmq-acl</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
index 210e180..2d06f19 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
@@ -16,7 +16,29 @@
  */
 package org.apache.rocketmq.broker;
 
-import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.io.FilenameUtils;
 import org.apache.rocketmq.acl.AccessValidator;
 import org.apache.rocketmq.broker.client.ClientHousekeepingService;
 import org.apache.rocketmq.broker.client.ConsumerIdsChangeListener;
@@ -26,6 +48,7 @@ import org.apache.rocketmq.broker.client.ProducerManager;
 import org.apache.rocketmq.broker.client.net.Broker2Client;
 import org.apache.rocketmq.broker.client.rebalance.RebalanceLockManager;
 import org.apache.rocketmq.broker.dledger.DLedgerRoleChangeHandler;
+import org.apache.rocketmq.broker.failover.EscapeBridge;
 import org.apache.rocketmq.broker.filter.CommitLogDispatcherCalcBitMap;
 import org.apache.rocketmq.broker.filter.ConsumerFilterManager;
 import org.apache.rocketmq.broker.filtersrv.FilterServerManager;
@@ -41,6 +64,7 @@ import org.apache.rocketmq.broker.offset.ConsumerOffsetManager;
 import org.apache.rocketmq.broker.offset.ConsumerOrderInfoManager;
 import org.apache.rocketmq.broker.offset.LmqConsumerOffsetManager;
 import org.apache.rocketmq.broker.out.BrokerOuterAPI;
+import org.apache.rocketmq.broker.plugin.BrokerAttachedPlugin;
 import org.apache.rocketmq.broker.plugin.MessageStoreFactory;
 import org.apache.rocketmq.broker.plugin.MessageStorePluginContext;
 import org.apache.rocketmq.broker.processor.AckMessageProcessor;
@@ -49,12 +73,16 @@ import org.apache.rocketmq.broker.processor.ChangeInvisibleTimeProcessor;
 import org.apache.rocketmq.broker.processor.ClientManageProcessor;
 import org.apache.rocketmq.broker.processor.ConsumerManageProcessor;
 import org.apache.rocketmq.broker.processor.EndTransactionProcessor;
+import org.apache.rocketmq.broker.processor.NotificationProcessor;
+import org.apache.rocketmq.broker.processor.PeekMessageProcessor;
+import org.apache.rocketmq.broker.processor.PollingInfoProcessor;
 import org.apache.rocketmq.broker.processor.PopMessageProcessor;
 import org.apache.rocketmq.broker.processor.PullMessageProcessor;
 import org.apache.rocketmq.broker.processor.QueryAssignmentProcessor;
 import org.apache.rocketmq.broker.processor.QueryMessageProcessor;
 import org.apache.rocketmq.broker.processor.ReplyMessageProcessor;
 import org.apache.rocketmq.broker.processor.SendMessageProcessor;
+import org.apache.rocketmq.broker.schedule.ScheduleMessageService;
 import org.apache.rocketmq.broker.slave.SlaveSynchronize;
 import org.apache.rocketmq.broker.subscription.LmqSubscriptionGroupManager;
 import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
@@ -68,22 +96,27 @@ import org.apache.rocketmq.broker.transaction.TransactionalMessageService;
 import org.apache.rocketmq.broker.transaction.queue.DefaultTransactionalMessageCheckListener;
 import org.apache.rocketmq.broker.transaction.queue.TransactionalMessageBridge;
 import org.apache.rocketmq.broker.transaction.queue.TransactionalMessageServiceImpl;
-import org.apache.rocketmq.broker.util.ServiceProvider;
+import org.apache.rocketmq.broker.util.HookUtils;
 import org.apache.rocketmq.common.BrokerConfig;
 import org.apache.rocketmq.common.Configuration;
 import org.apache.rocketmq.common.DataVersion;
+import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
 import org.apache.rocketmq.common.TopicConfig;
 import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.message.MessageExt;
 import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
+import org.apache.rocketmq.common.protocol.NamespaceUtil;
 import org.apache.rocketmq.common.protocol.RequestCode;
+import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
 import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
 import org.apache.rocketmq.common.statictopic.TopicQueueMappingDetail;
 import org.apache.rocketmq.common.statictopic.TopicQueueMappingInfo;
 import org.apache.rocketmq.common.stats.MomentStatsItem;
+import org.apache.rocketmq.common.utils.ServiceProvider;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.RPCHook;
@@ -99,116 +132,129 @@ import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.srvutil.FileWatchService;
 import org.apache.rocketmq.store.DefaultMessageStore;
 import org.apache.rocketmq.store.MessageArrivingListener;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.config.BrokerRole;
 import org.apache.rocketmq.store.config.MessageStoreConfig;
 import org.apache.rocketmq.store.dledger.DLedgerCommitLog;
+import org.apache.rocketmq.store.hook.PutMessageHook;
+import org.apache.rocketmq.store.hook.SendMessageBackHook;
 import org.apache.rocketmq.store.stats.BrokerStats;
 import org.apache.rocketmq.store.stats.BrokerStatsManager;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 import org.apache.rocketmq.store.stats.LmqBrokerStatsManager;
 
 public class BrokerController {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    protected static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private static final InternalLogger LOG_PROTECTION = InternalLoggerFactory.getLogger(LoggerName.PROTECTION_LOGGER_NAME);
     private static final InternalLogger LOG_WATER_MARK = InternalLoggerFactory.getLogger(LoggerName.WATER_MARK_LOGGER_NAME);
-    private final BrokerConfig brokerConfig;
+    protected static final int HA_ADDRESS_MIN_LENGTH = 6;
+
+    protected final BrokerConfig brokerConfig;
     private final NettyServerConfig nettyServerConfig;
     private final NettyClientConfig nettyClientConfig;
-    private final MessageStoreConfig messageStoreConfig;
-    private final ConsumerOffsetManager consumerOffsetManager;
-    private final ConsumerManager consumerManager;
-    private final ConsumerFilterManager consumerFilterManager;
-    private final ConsumerOrderInfoManager consumerOrderInfoManager;
-    private final ProducerManager producerManager;
-    private final AssignmentManager assignmentManager;
-    private final ClientHousekeepingService clientHousekeepingService;
-
-    private final PullMessageProcessor pullMessageProcessor;
-    private final PopMessageProcessor popMessageProcessor;
-    private final AckMessageProcessor ackMessageProcessor;
-    private final ChangeInvisibleTimeProcessor changeInvisibleTimeProcessor;
-    private final QueryAssignmentProcessor queryAssignmentProcessor;
-    private final ClientManageProcessor clientManageProcessor;
-    private final SendMessageProcessor sendMessageProcessor;
-    private final PullRequestHoldService pullRequestHoldService;
-    private final MessageArrivingListener messageArrivingListener;
-    private final Broker2Client broker2Client;
-    private final SubscriptionGroupManager subscriptionGroupManager;
-    private final ConsumerIdsChangeListener consumerIdsChangeListener;
+    protected final MessageStoreConfig messageStoreConfig;
+    protected final ConsumerOffsetManager consumerOffsetManager;
+    protected final ConsumerManager consumerManager;
+    protected final ConsumerFilterManager consumerFilterManager;
+    protected final ConsumerOrderInfoManager consumerOrderInfoManager;
+    protected final ProducerManager producerManager;
+    protected final ScheduleMessageService scheduleMessageService;
+    protected final AssignmentManager assignmentManager;
+    protected final ClientHousekeepingService clientHousekeepingService;
+    protected final PullMessageProcessor pullMessageProcessor;
+    protected final PeekMessageProcessor peekMessageProcessor;
+    protected final PopMessageProcessor popMessageProcessor;
+    protected final AckMessageProcessor ackMessageProcessor;
+    protected final ChangeInvisibleTimeProcessor changeInvisibleTimeProcessor;
+    protected final NotificationProcessor notificationProcessor;
+    protected final PollingInfoProcessor pollingInfoProcessor;
+    protected final QueryAssignmentProcessor queryAssignmentProcessor;
+    protected final ClientManageProcessor clientManageProcessor;
+    protected final SendMessageProcessor sendMessageProcessor;
+    protected final ReplyMessageProcessor replyMessageProcessor;
+    protected final PullRequestHoldService pullRequestHoldService;
+    protected final MessageArrivingListener messageArrivingListener;
+    protected final Broker2Client broker2Client;
+    protected final SubscriptionGroupManager subscriptionGroupManager;
+    protected final ConsumerIdsChangeListener consumerIdsChangeListener;
     private final RebalanceLockManager rebalanceLockManager = new RebalanceLockManager();
-    private final BrokerOuterAPI brokerOuterAPI;
-    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
-        "BrokerControllerScheduledThread"));
-    private final SlaveSynchronize slaveSynchronize;
-    private final BlockingQueue<Runnable> sendThreadPoolQueue;
-    private final BlockingQueue<Runnable> putThreadPoolQueue;
-    private final BlockingQueue<Runnable> ackThreadPoolQueue;
-    private final BlockingQueue<Runnable> pullThreadPoolQueue;
-    private final BlockingQueue<Runnable> replyThreadPoolQueue;
-    private final BlockingQueue<Runnable> queryThreadPoolQueue;
-    private final BlockingQueue<Runnable> clientManagerThreadPoolQueue;
-    private final BlockingQueue<Runnable> heartbeatThreadPoolQueue;
-    private final BlockingQueue<Runnable> consumerManagerThreadPoolQueue;
-    private final BlockingQueue<Runnable> endTransactionThreadPoolQueue;
-    private final FilterServerManager filterServerManager;
-    private final BrokerStatsManager brokerStatsManager;
-    private final List<SendMessageHook> sendMessageHookList = new ArrayList<SendMessageHook>();
-    private final List<ConsumeMessageHook> consumeMessageHookList = new ArrayList<ConsumeMessageHook>();
-    private final ConcurrentMap<String, String> brokerName2AddrMap = Maps.newConcurrentMap();
-    private MessageStore messageStore;
-    private RemotingServer remotingServer;
-    private RemotingServer fastRemotingServer;
-    private TopicConfigManager topicConfigManager;
-    private TopicQueueMappingManager topicQueueMappingManager;
-    private ExecutorService sendMessageExecutor;
-    private ExecutorService putMessageFutureExecutor;
-    private ExecutorService pullMessageExecutor;
-    private ExecutorService ackMessageExecutor;
-    private ExecutorService replyMessageExecutor;
-    private ExecutorService queryMessageExecutor;
-    private ExecutorService adminBrokerExecutor;
-    private ExecutorService clientManageExecutor;
-    private ExecutorService heartbeatExecutor;
-    private ExecutorService consumerManageExecutor;
-    private ExecutorService loadBalanceExecutor;
-    private ExecutorService endTransactionExecutor;
-    private boolean updateMasterHAServerAddrPeriodically = false;
+    protected BrokerOuterAPI brokerOuterAPI;
+    protected ScheduledExecutorService scheduledExecutorService;
+    protected final SlaveSynchronize slaveSynchronize;
+    protected final BlockingQueue<Runnable> sendThreadPoolQueue;
+    protected final BlockingQueue<Runnable> putThreadPoolQueue;
+    protected final BlockingQueue<Runnable> ackThreadPoolQueue;
+    protected final BlockingQueue<Runnable> pullThreadPoolQueue;
+    protected final BlockingQueue<Runnable> litePullThreadPoolQueue;
+    protected final BlockingQueue<Runnable> replyThreadPoolQueue;
+    protected final BlockingQueue<Runnable> queryThreadPoolQueue;
+    protected final BlockingQueue<Runnable> clientManagerThreadPoolQueue;
+    protected final BlockingQueue<Runnable> heartbeatThreadPoolQueue;
+    protected final BlockingQueue<Runnable> consumerManagerThreadPoolQueue;
+    protected final BlockingQueue<Runnable> endTransactionThreadPoolQueue;
+    protected final BlockingQueue<Runnable> adminBrokerThreadPoolQueue;
+    protected final BlockingQueue<Runnable> loadBalanceThreadPoolQueue;
+    protected final FilterServerManager filterServerManager;
+    protected final BrokerStatsManager brokerStatsManager;
+    protected final List<SendMessageHook> sendMessageHookList = new ArrayList<SendMessageHook>();
+    protected final List<ConsumeMessageHook> consumeMessageHookList = new ArrayList<ConsumeMessageHook>();
+    protected MessageStore messageStore;
+    protected RemotingServer remotingServer;
+    protected CountDownLatch remotingServerStartLatch;
+    protected RemotingServer fastRemotingServer;
+    protected TopicConfigManager topicConfigManager;
+    protected TopicQueueMappingManager topicQueueMappingManager;
+    protected ExecutorService sendMessageExecutor;
+    protected ExecutorService pullMessageExecutor;
+    protected ExecutorService litePullMessageExecutor;
+    protected ExecutorService putMessageFutureExecutor;
+    protected ExecutorService ackMessageExecutor;
+    protected ExecutorService replyMessageExecutor;
+    protected ExecutorService queryMessageExecutor;
+    protected ExecutorService adminBrokerExecutor;
+    protected ExecutorService clientManageExecutor;
+    protected ExecutorService heartbeatExecutor;
+    protected ExecutorService consumerManageExecutor;
+    protected ExecutorService loadBalanceExecutor;
+    protected ExecutorService endTransactionExecutor;
+    protected boolean updateMasterHAServerAddrPeriodically = false;
     private BrokerStats brokerStats;
     private InetSocketAddress storeHost;
-    private BrokerFastFailure brokerFastFailure;
+    protected BrokerFastFailure brokerFastFailure;
     private Configuration configuration;
-    private TopicQueueMappingCleanService topicQueueMappingCleanService;
-    private FileWatchService fileWatchService;
-    private TransactionalMessageCheckService transactionalMessageCheckService;
-    private TransactionalMessageService transactionalMessageService;
-    private AbstractTransactionalMessageCheckListener transactionalMessageCheckListener;
-    private Future<?> slaveSyncFuture;
-    private Map<Class, AccessValidator> accessValidatorMap = new HashMap<Class, AccessValidator>();
-    private long shouldStartTime;
+    protected TopicQueueMappingCleanService topicQueueMappingCleanService;
+    protected FileWatchService fileWatchService;
+    protected TransactionalMessageCheckService transactionalMessageCheckService;
+    protected TransactionalMessageService transactionalMessageService;
+    protected AbstractTransactionalMessageCheckListener transactionalMessageCheckListener;
+    protected Map<Class, AccessValidator> accessValidatorMap = new HashMap<Class, AccessValidator>();
+    protected volatile boolean shutdown = false;
+    protected ShutdownHook shutdownHook;
+    private volatile boolean isScheduleServiceStart = false;
+    private volatile boolean isTransactionCheckServiceStart = false;
+    protected volatile BrokerMemberGroup brokerMemberGroup;
+    protected EscapeBridge escapeBridge;
+    protected List<BrokerAttachedPlugin> brokerAttachedPlugins = new ArrayList<>();
+    protected volatile long shouldStartTime;
+
+    public BrokerController(
+        final BrokerConfig brokerConfig,
+        final NettyServerConfig nettyServerConfig,
+        final NettyClientConfig nettyClientConfig,
+        final MessageStoreConfig messageStoreConfig,
+        final ShutdownHook shutdownHook
+    ) {
+        this(brokerConfig, nettyServerConfig, nettyClientConfig, messageStoreConfig);
+        this.shutdownHook = shutdownHook;
+    }
+
+    public BrokerController(
+        final BrokerConfig brokerConfig,
+        final MessageStoreConfig messageStoreConfig
+    ) {
+        this(brokerConfig, null, null, messageStoreConfig);
+    }
 
     public BrokerController(
         final BrokerConfig brokerConfig,
@@ -220,26 +266,36 @@ public class BrokerController {
         this.nettyServerConfig = nettyServerConfig;
         this.nettyClientConfig = nettyClientConfig;
         this.messageStoreConfig = messageStoreConfig;
+        this.setStoreHost(new InetSocketAddress(this.getBrokerConfig().getBrokerIP1(), getListenPort()));
+        this.brokerStatsManager = messageStoreConfig.isEnableLmq() ? new LmqBrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat()) : new BrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat());
         this.consumerOffsetManager = messageStoreConfig.isEnableLmq() ? new LmqConsumerOffsetManager(this) : new ConsumerOffsetManager(this);
         this.topicConfigManager = messageStoreConfig.isEnableLmq() ? new LmqTopicConfigManager(this) : new TopicConfigManager(this);
         this.topicQueueMappingManager = new TopicQueueMappingManager(this);
         this.pullMessageProcessor = new PullMessageProcessor(this);
+        this.peekMessageProcessor = new PeekMessageProcessor(this);
         this.pullRequestHoldService = messageStoreConfig.isEnableLmq() ? new LmqPullRequestHoldService(this) : new PullRequestHoldService(this);
         this.popMessageProcessor = new PopMessageProcessor(this);
+        this.notificationProcessor = new NotificationProcessor(this);
+        this.pollingInfoProcessor = new PollingInfoProcessor(this);
         this.ackMessageProcessor = new AckMessageProcessor(this);
         this.changeInvisibleTimeProcessor = new ChangeInvisibleTimeProcessor(this);
         this.sendMessageProcessor = new SendMessageProcessor(this);
-        this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService,
-            this.popMessageProcessor);
+        this.replyMessageProcessor = new ReplyMessageProcessor(this);
+        this.messageArrivingListener = new NotifyMessageArrivingListener(this.pullRequestHoldService, this.popMessageProcessor, this.notificationProcessor);
         this.consumerIdsChangeListener = new DefaultConsumerIdsChangeListener(this);
-        this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener);
+        this.consumerManager = new ConsumerManager(this.consumerIdsChangeListener, this.brokerStatsManager);
+        this.producerManager = new ProducerManager(this.brokerStatsManager);
         this.consumerFilterManager = new ConsumerFilterManager(this);
         this.consumerOrderInfoManager = new ConsumerOrderInfoManager(this);
-        this.producerManager = new ProducerManager();
         this.clientHousekeepingService = new ClientHousekeepingService(this);
         this.broker2Client = new Broker2Client(this);
         this.subscriptionGroupManager = messageStoreConfig.isEnableLmq() ? new LmqSubscriptionGroupManager(this) : new SubscriptionGroupManager(this);
-        this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig, this);
+        this.scheduleMessageService = new ScheduleMessageService(this);
+
+        if (nettyClientConfig != null) {
+            this.brokerOuterAPI = new BrokerOuterAPI(nettyClientConfig);
+        }
+
         this.filterServerManager = new FilterServerManager(this);
 
         this.assignmentManager = new AssignmentManager(this);
@@ -250,6 +306,8 @@ public class BrokerController {
         this.sendThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getSendThreadPoolQueueCapacity());
         this.putThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getPutThreadPoolQueueCapacity());
         this.pullThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getPullThreadPoolQueueCapacity());
+        this.litePullThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getLitePullThreadPoolQueueCapacity());
+
         this.ackThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getAckThreadPoolQueueCapacity());
         this.replyThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getReplyThreadPoolQueueCapacity());
         this.queryThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getQueryThreadPoolQueueCapacity());
@@ -257,25 +315,51 @@ public class BrokerController {
         this.consumerManagerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getConsumerManagerThreadPoolQueueCapacity());
         this.heartbeatThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getHeartbeatThreadPoolQueueCapacity());
         this.endTransactionThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getEndTransactionPoolQueueCapacity());
-
-        this.brokerStatsManager = messageStoreConfig.isEnableLmq() ? new LmqBrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat()) : new BrokerStatsManager(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.isEnableDetailStat());
-
-        this.setStoreHost(new InetSocketAddress(this.getBrokerConfig().getBrokerIP1(), this.getNettyServerConfig().getListenPort()));
+        this.adminBrokerThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getAdminBrokerThreadPoolQueueCapacity());
+        this.loadBalanceThreadPoolQueue = new LinkedBlockingQueue<Runnable>(this.brokerConfig.getLoadBalanceThreadPoolQueueCapacity());
 
         this.brokerFastFailure = new BrokerFastFailure(this);
+
+        String brokerConfigPath;
+        if (brokerConfig.getBrokerConfigPath() != null && !brokerConfig.getBrokerConfigPath().isEmpty()) {
+            brokerConfigPath = brokerConfig.getBrokerConfigPath();
+        } else {
+            brokerConfigPath = FilenameUtils.concat(
+                FilenameUtils.getFullPathNoEndSeparator(BrokerPathConfigHelper.getBrokerConfigPath()),
+                this.brokerConfig.getCanonicalName() + ".properties");
+        }
         this.configuration = new Configuration(
-            log,
-            BrokerPathConfigHelper.getBrokerConfigPath(),
+            LOG,
+            brokerConfigPath,
             this.brokerConfig, this.nettyServerConfig, this.nettyClientConfig, this.messageStoreConfig
         );
-    }
 
-    public ConsumerIdsChangeListener getConsumerIdsChangeListener() {
-        return consumerIdsChangeListener;
-    }
+        this.brokerStatsManager.setProduerStateGetter(new BrokerStatsManager.StateGetter() {
+            @Override
+            public boolean online(String instanceId, String group, String topic) {
+                if (getTopicConfigManager().getTopicConfigTable().containsKey(NamespaceUtil.wrapNamespace(instanceId, topic))) {
+                    return getProducerManager().groupOnline(NamespaceUtil.wrapNamespace(instanceId, group));
+                } else {
+                    return getProducerManager().groupOnline(group);
+                }
+            }
+        });
+        this.brokerStatsManager.setConsumerStateGetter(new BrokerStatsManager.StateGetter() {
+            @Override
+            public boolean online(String instanceId, String group, String topic) {
+                String topicFullName = NamespaceUtil.wrapNamespace(instanceId, topic);
+                if (getTopicConfigManager().getTopicConfigTable().containsKey(topicFullName)) {
+                    return getConsumerManager().findSubscriptionData(NamespaceUtil.wrapNamespace(instanceId, group), topicFullName) != null;
+                } else {
+                    return getConsumerManager().findSubscriptionData(group, topic) != null;
+                }
+            }
+        });
+
+        this.brokerMemberGroup = new BrokerMemberGroup(this.brokerConfig.getBrokerClusterName(), this.brokerConfig.getBrokerName());
+        this.brokerMemberGroup.getBrokerAddrs().put(this.brokerConfig.getBrokerId(), this.getBrokerAddr());
 
-    public ClientManageProcessor getClientManageProcessor() {
-        return clientManageProcessor;
+        this.escapeBridge = new EscapeBridge(this);
     }
 
     public BrokerConfig getBrokerConfig() {
@@ -286,6 +370,10 @@ public class BrokerController {
         return nettyServerConfig;
     }
 
+    public NettyClientConfig getNettyClientConfig() {
+        return nettyClientConfig;
+    }
+
     public BlockingQueue<Runnable> getPullThreadPoolQueue() {
         return pullThreadPoolQueue;
     }
@@ -294,245 +382,342 @@ public class BrokerController {
         return queryThreadPoolQueue;
     }
 
-    public boolean initialize() throws CloneNotSupportedException {
-        boolean result = this.topicConfigManager.load();
+    protected void initializeRemotingServer() throws CloneNotSupportedException {
+        this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
+        NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
+        fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
+        this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
+    }
+
+    /**
+     * Initialize resources including remoting server and thread executors.
+     */
+    protected void initializeResources() {
+        this.scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
+            new ThreadFactoryImpl("BrokerControllerScheduledThread", true, brokerConfig));
+
+        this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getSendMessageThreadPoolNums(),
+            this.brokerConfig.getSendMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.sendThreadPoolQueue,
+            new ThreadFactoryImpl("SendMessageThread_", brokerConfig));
+
+        this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getPullMessageThreadPoolNums(),
+            this.brokerConfig.getPullMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.pullThreadPoolQueue,
+            new ThreadFactoryImpl("PullMessageThread_", brokerConfig));
+
+        this.litePullMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getLitePullMessageThreadPoolNums(),
+            this.brokerConfig.getLitePullMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.litePullThreadPoolQueue,
+            new ThreadFactoryImpl("LitePullMessageThread_", brokerConfig));
+
+        this.putMessageFutureExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getPutMessageFutureThreadPoolNums(),
+            this.brokerConfig.getPutMessageFutureThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.putThreadPoolQueue,
+            new ThreadFactoryImpl("SendMessageThread_", brokerConfig));
+
+        this.ackMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getAckMessageThreadPoolNums(),
+            this.brokerConfig.getAckMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.ackThreadPoolQueue,
+            new ThreadFactoryImpl("AckMessageThread_", brokerConfig));
+
+        this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getQueryMessageThreadPoolNums(),
+            this.brokerConfig.getQueryMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.queryThreadPoolQueue,
+            new ThreadFactoryImpl("QueryMessageThread_", brokerConfig));
+
+        this.adminBrokerExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getAdminBrokerThreadPoolNums(),
+            this.brokerConfig.getAdminBrokerThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.adminBrokerThreadPoolQueue,
+            new ThreadFactoryImpl("AdminBrokerThread_", brokerConfig));
+
+        this.clientManageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getClientManageThreadPoolNums(),
+            this.brokerConfig.getClientManageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.clientManagerThreadPoolQueue,
+            new ThreadFactoryImpl("ClientManageThread_", brokerConfig));
+
+        this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getHeartbeatThreadPoolNums(),
+            this.brokerConfig.getHeartbeatThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.heartbeatThreadPoolQueue,
+            new ThreadFactoryImpl("HeartbeatThread_", true, brokerConfig));
+
+        this.consumerManageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getConsumerManageThreadPoolNums(),
+            this.brokerConfig.getConsumerManageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.consumerManagerThreadPoolQueue,
+            new ThreadFactoryImpl("ConsumerManageThread_", true, brokerConfig));
+
+        this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
+            this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.replyThreadPoolQueue,
+            new ThreadFactoryImpl("ProcessReplyMessageThread_", brokerConfig));
+
+        this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getEndTransactionThreadPoolNums(),
+            this.brokerConfig.getEndTransactionThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.endTransactionThreadPoolQueue,
+            new ThreadFactoryImpl("EndTransactionThread_", brokerConfig));
+
+        this.loadBalanceExecutor = new BrokerFixedThreadPoolExecutor(
+            this.brokerConfig.getLoadBalanceProcessorThreadPoolNums(),
+            this.brokerConfig.getLoadBalanceProcessorThreadPoolNums(),
+            1000 * 60,
+            TimeUnit.MILLISECONDS,
+            this.loadBalanceThreadPoolQueue,
+            new ThreadFactoryImpl("LoadBalanceProcessorThread_", brokerConfig));
+
+        this.topicQueueMappingCleanService = new TopicQueueMappingCleanService(this);
+    }
+
+    protected void initializeBrokerScheduledTasks() {
+        final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();
+        final long period = 1000 * 60 * 60 * 24;
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.getBrokerStats().record();
+                } catch (Throwable e) {
+                    LOG.error("BrokerController: failed to record broker stats", e);
+                }
+            }
+        }, initialDelay, period, TimeUnit.MILLISECONDS);
 
-        result = result && this.topicQueueMappingManager.load();
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.consumerOffsetManager.persist();
+                } catch (Throwable e) {
+                    LOG.error(
+                        "BrokerController: failed to persist config file of consumerOffset", e);
+                }
+            }
+        }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
 
-        result = result && this.consumerOffsetManager.load();
-        result = result && this.subscriptionGroupManager.load();
-        result = result && this.consumerFilterManager.load();
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.consumerFilterManager.persist();
+                    BrokerController.this.consumerOrderInfoManager.persist();
+                } catch (Throwable e) {
+                    LOG.error(
+                        "BrokerController: failed to persist config file of consumerFilter or consumerOrderInfo",
+                        e);
+                }
+            }
+        }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
 
-        if (result) {
-            try {
-                this.messageStore = new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener, this.brokerConfig);
-                ((DefaultMessageStore) this.messageStore).setTopicConfigTable(topicConfigManager.getTopicConfigTable());
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.protectBroker();
+                } catch (Throwable e) {
+                    LOG.error("BrokerController: failed to protectBroker", e);
+                }
+            }
+        }, 3, 3, TimeUnit.MINUTES);
 
-                if (messageStoreConfig.isEnableDLegerCommitLog()) {
-                    DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, (DefaultMessageStore) messageStore);
-                    ((DLedgerCommitLog) messageStore.getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler);
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.printWaterMark();
+                } catch (Throwable e) {
+                    LOG.error("BrokerController: failed to print broker watermark", e);
                 }
-                this.brokerStats = new BrokerStats(this.messageStore);
-                //load plugin
-                MessageStorePluginContext context = new MessageStorePluginContext(messageStoreConfig, brokerStatsManager, messageArrivingListener, brokerConfig);
-                this.messageStore = MessageStoreFactory.build(context, this.messageStore);
-                this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));
-            } catch (IOException e) {
-                result = false;
-                log.error("Failed to initialize", e);
             }
-        }
+        }, 10, 1, TimeUnit.SECONDS);
 
-        result = result && this.messageStore.load();
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
 
-        if (result) {
-            this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.clientHousekeepingService);
-            NettyServerConfig fastConfig = (NettyServerConfig) this.nettyServerConfig.clone();
-            fastConfig.setListenPort(nettyServerConfig.getListenPort() - 2);
-            this.fastRemotingServer = new NettyRemotingServer(fastConfig, this.clientHousekeepingService);
-            this.sendMessageExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getSendMessageThreadPoolNums(),
-                this.brokerConfig.getSendMessageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.sendThreadPoolQueue,
-                new ThreadFactoryImpl("SendMessageThread_"));
-
-            this.putMessageFutureExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getPutMessageFutureThreadPoolNums(),
-                this.brokerConfig.getPutMessageFutureThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.putThreadPoolQueue,
-                new ThreadFactoryImpl("PutMessageThread_"));
-
-            this.pullMessageExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getPullMessageThreadPoolNums(),
-                this.brokerConfig.getPullMessageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.pullThreadPoolQueue,
-                new ThreadFactoryImpl("PullMessageThread_"));
-
-            this.ackMessageExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getAckMessageThreadPoolNums(),
-                this.brokerConfig.getAckMessageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.ackThreadPoolQueue,
-                new ThreadFactoryImpl("AckMessageThread_"));
-
-            this.replyMessageExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
-                this.brokerConfig.getProcessReplyMessageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.replyThreadPoolQueue,
-                new ThreadFactoryImpl("ProcessReplyMessageThread_"));
-
-            this.queryMessageExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getQueryMessageThreadPoolNums(),
-                this.brokerConfig.getQueryMessageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.queryThreadPoolQueue,
-                new ThreadFactoryImpl("QueryMessageThread_"));
-
-            this.adminBrokerExecutor =
-                Executors.newFixedThreadPool(this.brokerConfig.getAdminBrokerThreadPoolNums(), new ThreadFactoryImpl(
-                    "AdminBrokerThread_"));
-
-            this.clientManageExecutor = new ThreadPoolExecutor(
-                this.brokerConfig.getClientManageThreadPoolNums(),
-                this.brokerConfig.getClientManageThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.clientManagerThreadPoolQueue,
-                new ThreadFactoryImpl("ClientManageThread_"));
-
-            this.heartbeatExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getHeartbeatThreadPoolNums(),
-                this.brokerConfig.getHeartbeatThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.heartbeatThreadPoolQueue,
-                new ThreadFactoryImpl("HeartbeatThread_", true));
-
-            this.endTransactionExecutor = new BrokerFixedThreadPoolExecutor(
-                this.brokerConfig.getEndTransactionThreadPoolNums(),
-                this.brokerConfig.getEndTransactionThreadPoolNums(),
-                1000 * 60,
-                TimeUnit.MILLISECONDS,
-                this.endTransactionThreadPoolQueue,
-                new ThreadFactoryImpl("EndTransactionThread_"));
-
-            this.consumerManageExecutor =
-                Executors.newFixedThreadPool(this.brokerConfig.getConsumerManageThreadPoolNums(), new ThreadFactoryImpl(
-                    "ConsumerManageThread_"));
-
-            this.registerProcessor();
-
-            final long initialDelay = UtilAll.computeNextMorningTimeMillis() - System.currentTimeMillis();
-            final long period = 1000 * 60 * 60 * 24;
-            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        BrokerController.this.getBrokerStats().record();
-                    } catch (Throwable e) {
-                        log.error("schedule record error.", e);
-                    }
+            @Override
+            public void run() {
+                try {
+                    LOG.info("Dispatch task fall behind commit log {}bytes",
+                        BrokerController.this.getMessageStore().dispatchBehindBytes());
+                } catch (Throwable e) {
+                    LOG.error("Failed to print dispatchBehindBytes", e);
                 }
-            }, initialDelay, period, TimeUnit.MILLISECONDS);
+            }
+        }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
 
-            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        BrokerController.this.consumerOffsetManager.persist();
-                    } catch (Throwable e) {
-                        log.error("schedule persist consumerOffset error.", e);
-                    }
+        if (!messageStoreConfig.isEnableDLegerCommitLog() && !messageStoreConfig.isDuplicationEnable()) {
+            if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
+                if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= HA_ADDRESS_MIN_LENGTH) {
+                    this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
+                    this.updateMasterHAServerAddrPeriodically = false;
+                } else {
+                    this.updateMasterHAServerAddrPeriodically = true;
                 }
-            }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
 
-            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        BrokerController.this.consumerFilterManager.persist();
-                    } catch (Throwable e) {
-                        log.error("schedule persist consumer filter error.", e);
+                this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        try {
+                            BrokerController.this.slaveSynchronize.syncAll();
+                        } catch (Throwable e) {
+                            LOG.error("Failed to sync all config for slave.", e);
+                        }
                     }
-                }
-            }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
+                }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
 
-            this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        BrokerController.this.protectBroker();
-                    } catch (Throwable e) {
-                        log.error("protectBroker error.", e);
+            } else {
+                this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        try {
+                            BrokerController.this.printMasterAndSlaveDiff();
+                        } catch (Throwable e) {
+                            LOG.error("Failed to print diff of master and slave.", e);
+                        }
                     }
+                }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    protected void initializeScheduledTasks() {
+
+        initializeBrokerScheduledTasks();
+
+        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    BrokerController.this.brokerOuterAPI.refreshMetadata();
+                } catch (Exception e) {
+                    LOG.error("ScheduledTask refresh metadata exception", e);
                 }
-            }, 3, 3, TimeUnit.MINUTES);
+            }
+        }, 1, 5, TimeUnit.SECONDS);
 
+        if (this.brokerConfig.getNamesrvAddr() != null) {
+            this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
+            LOG.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());
+            // also auto update namesrv if specify
             this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                 @Override
                 public void run() {
                     try {
-                        BrokerController.this.printWaterMark();
+                        BrokerController.this.brokerOuterAPI.updateNameServerAddressList(BrokerController.this.brokerConfig.getNamesrvAddr());
                     } catch (Throwable e) {
-                        log.error("printWaterMark error.", e);
+                        LOG.error("Failed to update nameServer address list", e);
                     }
                 }
-            }, 10, 1, TimeUnit.SECONDS);
-
+            }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
+        } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
             this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
 
                 @Override
                 public void run() {
                     try {
-                        log.info("dispatch behind commit log {} bytes", BrokerController.this.getMessageStore().dispatchBehindBytes());
+                        BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
                     } catch (Throwable e) {
-                        log.error("schedule dispatchBehindBytes error.", e);
+                        LOG.error("Failed to fetch nameServer address", e);
                     }
                 }
-            }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
+            }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
+        }
+    }
 
-            this.loadBalanceExecutor =
-                Executors.newFixedThreadPool(this.brokerConfig.getLoadBalanceProcessorThreadPoolNums(), new ThreadFactoryImpl(
-                    "LoadBalanceProcessorThread_"));
+    public boolean initialize() throws CloneNotSupportedException {
 
-            if (this.brokerConfig.getNamesrvAddr() != null) {
-                this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
-                log.info("Set user specified name server address: {}", this.brokerConfig.getNamesrvAddr());
-            } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
-                this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+        boolean result = this.topicConfigManager.load();
+        result = result && this.topicQueueMappingManager.load();
+        result = result && this.consumerOffsetManager.load();
+        result = result && this.subscriptionGroupManager.load();
+        result = result && this.consumerFilterManager.load();
+        result = result && this.consumerOrderInfoManager.load();
 
-                    @Override
-                    public void run() {
-                        try {
-                            BrokerController.this.brokerOuterAPI.fetchNameServerAddr();
-                        } catch (Throwable e) {
-                            log.error("ScheduledTask fetchNameServerAddr exception", e);
-                        }
-                    }
-                }, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
-            }
+        if (result) {
+            try {
+                DefaultMessageStore defaultMessageStore = new DefaultMessageStore(this.messageStoreConfig, this.brokerStatsManager, this.messageArrivingListener, this.brokerConfig);
+                defaultMessageStore.setTopicConfigTable(topicConfigManager.getTopicConfigTable());
 
-            this.scheduledExecutorService.scheduleAtFixedRate(() -> {
-                try {
-                    BrokerController.this.brokerOuterAPI.refreshMetadata();
-                } catch (Exception e) {
-                    log.error("ScheduledTask refresh metadata exception", e);
-                }
-            }, 1, 5, TimeUnit.SECONDS);
-
-            if (!messageStoreConfig.isEnableDLegerCommitLog()) {
-                if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
-                    if (this.messageStoreConfig.getHaMasterAddress() != null && this.messageStoreConfig.getHaMasterAddress().length() >= 6) {
-                        this.messageStore.updateHaMasterAddress(this.messageStoreConfig.getHaMasterAddress());
-                        this.updateMasterHAServerAddrPeriodically = false;
-                    } else {
-                        this.updateMasterHAServerAddrPeriodically = true;
-                    }
-                } else {
-                    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                        @Override
-                        public void run() {
-                            try {
-                                BrokerController.this.printMasterAndSlaveDiff();
-                            } catch (Throwable e) {
-                                log.error("schedule printMasterAndSlaveDiff error.", e);
-                            }
-                        }
-                    }, 1000 * 10, 1000 * 60, TimeUnit.MILLISECONDS);
+                if (messageStoreConfig.isEnableDLegerCommitLog()) {
+                    DLedgerRoleChangeHandler roleChangeHandler = new DLedgerRoleChangeHandler(this, defaultMessageStore);
+                    ((DLedgerCommitLog) defaultMessageStore.getCommitLog()).getdLedgerServer().getdLedgerLeaderElector().addRoleChangeHandler(roleChangeHandler);
                 }
+                this.brokerStats = new BrokerStats(defaultMessageStore);
+                //load plugin
+                MessageStorePluginContext context = new MessageStorePluginContext(this, messageStoreConfig, brokerStatsManager, messageArrivingListener);
+                this.messageStore = MessageStoreFactory.build(context, defaultMessageStore);
+                this.messageStore.getDispatcherList().addFirst(new CommitLogDispatcherCalcBitMap(this.brokerConfig, this.consumerFilterManager));
+
+            } catch (IOException e) {
+                result = false;
+                LOG.error("BrokerController#initialize: unexpected error occurs", e);
+            }
+        }
+        if (messageStore != null) {
+            registerMessageStoreHook();
+        }
+
+        result = result && this.messageStore.load();
+
+        //scheduleMessageService load after messageStore load success
+        result = result && this.scheduleMessageService.load();
+
+        for (BrokerAttachedPlugin brokerAttachedPlugin : brokerAttachedPlugins) {
+            if (brokerAttachedPlugin != null) {
+                result = result && brokerAttachedPlugin.load();
             }
+        }
+
+        if (result) {
+
+            initializeRemotingServer();
+
+            initializeResources();
+
+            registerProcessor();
+
+            initializeScheduledTasks();
+
+            initialTransaction();
+
+            initialAcl();
 
-            this.topicQueueMappingCleanService = new TopicQueueMappingCleanService(this);
+            initialRpcHooks();
 
             if (TlsSystemConfig.tlsMode != TlsMode.DISABLED) {
                 // Register a listener to reload SslContext
@@ -549,7 +734,7 @@ public class BrokerController {
                             @Override
                             public void onChanged(String path) {
                                 if (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) {
-                                    log.info("The trust certificate changed, reload the ssl context");
+                                    LOG.info("The trust certificate changed, reload the ssl context");
                                     reloadServerSslContext();
                                 }
                                 if (path.equals(TlsSystemConfig.tlsServerCertPath)) {
@@ -559,7 +744,7 @@ public class BrokerController {
                                     keyChanged = true;
                                 }
                                 if (certChanged && keyChanged) {
-                                    log.info("The certificate and private key changed, reload the ssl context");
+                                    LOG.info("The certificate and private key changed, reload the ssl context");
                                     certChanged = keyChanged = false;
                                     reloadServerSslContext();
                                 }
@@ -571,26 +756,76 @@ public class BrokerController {
                             }
                         });
                 } catch (Exception e) {
-                    log.warn("FileWatchService created error, can't load the certificate dynamically");
+                    result = false;
+                    LOG.warn("FileWatchService created error, can't load the certificate dynamically");
                 }
             }
-            initialTransaction();
-            initialAcl();
-            initialRpcHooks();
         }
+
         return result;
     }
 
+    public void registerMessageStoreHook() {
+        List<PutMessageHook> putMessageHookList = messageStore.getPutMessageHookList();
+
+        putMessageHookList.add(new PutMessageHook() {
+            @Override public String hookName() {
+                return "checkBeforePutMessage";
+            }
+
+            @Override public PutMessageResult executeBeforePutMessage(MessageExt msg) {
+                return HookUtils.checkBeforePutMessage(BrokerController.this, msg);
+            }
+        });
+
+        putMessageHookList.add(new PutMessageHook() {
+            @Override public String hookName() {
+                return "innerBatchChecker";
+            }
+
+            @Override public PutMessageResult executeBeforePutMessage(MessageExt msg) {
+                if (msg instanceof MessageExtBrokerInner) {
+                    return HookUtils.checkInnerBatch(BrokerController.this, msg);
+                }
+                return null;
+            }
+        });
+
+        putMessageHookList.add(new PutMessageHook() {
+            @Override public String hookName() {
+                return "handleScheduleMessage";
+            }
+
+            @Override public PutMessageResult executeBeforePutMessage(MessageExt msg) {
+                if (msg instanceof MessageExtBrokerInner) {
+                    return HookUtils.handleScheduleMessage(BrokerController.this, (MessageExtBrokerInner) msg);
+                }
+                return null;
+            }
+        });
+
+        SendMessageBackHook sendMessageBackHook = new SendMessageBackHook() {
+            @Override
+            public boolean executeSendMessageBack(List<MessageExt> msgList, String brokerName, String brokerAddr) {
+                return HookUtils.sendMessageBack(BrokerController.this, msgList, brokerName, brokerAddr);
+            }
+        };
+
+        if (messageStore != null) {
+            messageStore.setSendMessageBackHook(sendMessageBackHook);
+        }
+    }
+
     private void initialTransaction() {
         this.transactionalMessageService = ServiceProvider.loadClass(ServiceProvider.TRANSACTION_SERVICE_ID, TransactionalMessageService.class);
         if (null == this.transactionalMessageService) {
             this.transactionalMessageService = new TransactionalMessageServiceImpl(new TransactionalMessageBridge(this, this.getMessageStore()));
-            log.warn("Load default transaction message hook service: {}", TransactionalMessageServiceImpl.class.getSimpleName());
+            LOG.warn("Load default transaction message hook service: {}", TransactionalMessageServiceImpl.class.getSimpleName());
         }
         this.transactionalMessageCheckListener = ServiceProvider.loadClass(ServiceProvider.TRANSACTION_LISTENER_ID, AbstractTransactionalMessageCheckListener.class);
         if (null == this.transactionalMessageCheckListener) {
             this.transactionalMessageCheckListener = new DefaultTransactionalMessageCheckListener();
-            log.warn("Load default discard message hook service: {}", DefaultTransactionalMessageCheckListener.class.getSimpleName());
+            LOG.warn("Load default discard message hook service: {}", DefaultTransactionalMessageCheckListener.class.getSimpleName());
         }
         this.transactionalMessageCheckListener.setBrokerController(this);
         this.transactionalMessageCheckService = new TransactionalMessageCheckService(this);
@@ -598,13 +833,13 @@ public class BrokerController {
 
     private void initialAcl() {
         if (!this.brokerConfig.isAclEnable()) {
-            log.info("The broker dose not enable acl");
+            LOG.info("The broker dose not enable acl");
             return;
         }
 
         List<AccessValidator> accessValidators = ServiceProvider.load(ServiceProvider.ACL_VALIDATOR_ID, AccessValidator.class);
-        if (accessValidators == null || accessValidators.isEmpty()) {
-            log.info("The broker dose not load the AccessValidator");
+        if (accessValidators.isEmpty()) {
+            LOG.info("The broker dose not load the AccessValidator");
             return;
         }
 
@@ -622,6 +857,11 @@ public class BrokerController {
                 @Override
                 public void doAfterResponse(String remoteAddr, RemotingCommand request, RemotingCommand response) {
                 }
+
+                @Override
+                public void doAfterRpcFailure(String remoteAddr, RemotingCommand request, Boolean remoteTimeout) {
+
+                }
             });
         }
     }
@@ -637,15 +877,10 @@ public class BrokerController {
         }
     }
 
-    public String getBrokerAddrByName(String brokerName) {
-        return this.brokerName2AddrMap.get(brokerName);
-    }
-
     public void registerProcessor() {
-        /**
+        /*
          * SendMessageProcessor
          */
-
         sendMessageProcessor.registerSendMessageHook(sendMessageHookList);
         sendMessageProcessor.registerConsumeMessageHook(consumeMessageHookList);
 
@@ -661,8 +896,13 @@ public class BrokerController {
          * PullMessageProcessor
          */
         this.remotingServer.registerProcessor(RequestCode.PULL_MESSAGE, this.pullMessageProcessor, this.pullMessageExecutor);
+        this.remotingServer.registerProcessor(RequestCode.LITE_PULL_MESSAGE, this.pullMessageProcessor, this.litePullMessageExecutor);
         this.pullMessageProcessor.registerConsumeMessageHook(consumeMessageHookList);
         /**
+         * PeekMessageProcessor
+         */
+        this.remotingServer.registerProcessor(RequestCode.PEEK_MESSAGE, this.peekMessageProcessor, this.pullMessageExecutor);
+        /**
          * PopMessageProcessor
          */
         this.remotingServer.registerProcessor(RequestCode.POP_MESSAGE, this.popMessageProcessor, this.pullMessageExecutor);
@@ -677,11 +917,20 @@ public class BrokerController {
          */
         this.remotingServer.registerProcessor(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, this.changeInvisibleTimeProcessor, this.ackMessageExecutor);
         this.fastRemotingServer.registerProcessor(RequestCode.CHANGE_MESSAGE_INVISIBLETIME, this.changeInvisibleTimeProcessor, this.ackMessageExecutor);
+        /**
+         * notificationProcessor
+         */
+        this.remotingServer.registerProcessor(RequestCode.NOTIFICATION, this.notificationProcessor, this.pullMessageExecutor);
+
+        /**
+         * pollingInfoProcessor
+         */
+        this.remotingServer.registerProcessor(RequestCode.POLLING_INFO, this.pollingInfoProcessor, this.pullMessageExecutor);
 
         /**
          * ReplyMessageProcessor
          */
-        ReplyMessageProcessor replyMessageProcessor = new ReplyMessageProcessor(this);
+
         replyMessageProcessor.registerSendMessageHook(sendMessageHookList);
 
         this.remotingServer.registerProcessor(RequestCode.SEND_REPLY_MESSAGE, replyMessageProcessor, replyMessageExecutor);
@@ -733,10 +982,10 @@ public class BrokerController {
         /**
          * EndTransactionProcessor
          */
-        this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);
-        this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.endTransactionExecutor);
+        this.remotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.sendMessageExecutor);
+        this.fastRemotingServer.registerProcessor(RequestCode.END_TRANSACTION, new EndTransactionProcessor(this), this.sendMessageExecutor);
 
-        /**
+        /*
          * Default
          */
         AdminBrokerProcessor adminProcessor = new AdminBrokerProcessor(this);
@@ -754,9 +1003,7 @@ public class BrokerController {
 
     public void protectBroker() {
         if (this.brokerConfig.isDisableConsumeIfConsumerReadSlowly()) {
-            final Iterator<Map.Entry<String, MomentStatsItem>> it = this.brokerStatsManager.getMomentStatsItemSetFallSize().getStatsItemTable().entrySet().iterator();
-            while (it.hasNext()) {
-                final Map.Entry<String, MomentStatsItem> next = it.next();
+            for (Map.Entry<String, MomentStatsItem> next : this.brokerStatsManager.getMomentStatsItemSetFallSize().getStatsItemTable().entrySet()) {
                 final long fallBehindBytes = next.getValue().getValue().get();
                 if (fallBehindBytes > this.brokerConfig.getConsumerFallbehindThreshold()) {
                     final String[] split = next.getValue().getStatsKey().split("@");
@@ -791,19 +1038,23 @@ public class BrokerController {
         return this.headSlowTimeMills(this.pullThreadPoolQueue);
     }
 
-    public long headSlowTimeMills4QueryThreadPoolQueue() {
-        return this.headSlowTimeMills(this.queryThreadPoolQueue);
+    public long headSlowTimeMills4LitePullThreadPoolQueue() {
+        return this.headSlowTimeMills(this.litePullThreadPoolQueue);
     }
 
-    public long headSlowTimeMills4EndTransactionThreadPoolQueue() {
-        return this.headSlowTimeMills(this.endTransactionThreadPoolQueue);
+    public long headSlowTimeMills4QueryThreadPoolQueue() {
+        return this.headSlowTimeMills(this.queryThreadPoolQueue);
     }
 
     public void printWaterMark() {
         LOG_WATER_MARK.info("[WATERMARK] Send Queue Size: {} SlowTimeMills: {}", this.sendThreadPoolQueue.size(), headSlowTimeMills4SendThreadPoolQueue());
         LOG_WATER_MARK.info("[WATERMARK] Pull Queue Size: {} SlowTimeMills: {}", this.pullThreadPoolQueue.size(), headSlowTimeMills4PullThreadPoolQueue());
         LOG_WATER_MARK.info("[WATERMARK] Query Queue Size: {} SlowTimeMills: {}", this.queryThreadPoolQueue.size(), headSlowTimeMills4QueryThreadPoolQueue());
-        LOG_WATER_MARK.info("[WATERMARK] Transaction Queue Size: {} SlowTimeMills: {}", this.endTransactionThreadPoolQueue.size(), headSlowTimeMills4EndTransactionThreadPoolQueue());
+        LOG_WATER_MARK.info("[WATERMARK] Lite Pull Queue Size: {} SlowTimeMills: {}", this.litePullThreadPoolQueue.size(), headSlowTimeMills4LitePullThreadPoolQueue());
+        LOG_WATER_MARK.info("[WATERMARK] Transaction Queue Size: {} SlowTimeMills: {}", this.endTransactionThreadPoolQueue.size(), headSlowTimeMills(this.endTransactionThreadPoolQueue));
+        LOG_WATER_MARK.info("[WATERMARK] ClientManager Queue Size: {} SlowTimeMills: {}", this.clientManagerThreadPoolQueue.size(), this.headSlowTimeMills(this.clientManagerThreadPoolQueue));
+        LOG_WATER_MARK.info("[WATERMARK] Heartbeat Queue Size: {} SlowTimeMills: {}", this.heartbeatThreadPoolQueue.size(), this.headSlowTimeMills(this.heartbeatThreadPoolQueue));
+        LOG_WATER_MARK.info("[WATERMARK] Ack Queue Size: {} SlowTimeMills: {}", this.ackThreadPoolQueue.size(), headSlowTimeMills(this.ackThreadPoolQueue));
     }
 
     public MessageStore getMessageStore() {
@@ -814,11 +1065,11 @@ public class BrokerController {
         this.messageStore = messageStore;
     }
 
-    private void printMasterAndSlaveDiff() {
-        long diff = this.messageStore.slaveFallBehindMuch();
-
-        // XXX: warn and notify me
-        log.info("Slave fall behind master: {} bytes", diff);
+    protected void printMasterAndSlaveDiff() {
+        if (messageStore.getHaService() != null && messageStore.getHaService().getConnectionCount().get() > 0) {
+            long diff = this.messageStore.slaveFallBehindMuch();
+            LOG.info("CommitLog: slave fall behind master {}bytes", diff);
+        }
     }
 
     public Broker2Client getBroker2Client() {
@@ -853,6 +1104,10 @@ public class BrokerController {
         this.fastRemotingServer = fastRemotingServer;
     }
 
+    public RemotingServer getFastRemotingServer() {
+        return fastRemotingServer;
+    }
+
     public PullMessageProcessor getPullMessageProcessor() {
         return pullMessageProcessor;
     }
@@ -869,7 +1124,24 @@ public class BrokerController {
         return popMessageProcessor;
     }
 
-    public void shutdown() {
+    protected void shutdownBasicService() {
+
+        shutdown = true;
+
+        this.unregisterBrokerAll();
+
+        if (this.shutdownHook != null) {
+            this.shutdownHook.beforeShutdown(this);
+        }
+
+        if (this.remotingServer != null) {
+            this.remotingServer.shutdown();
+        }
+
+        if (this.fastRemotingServer != null) {
+            this.fastRemotingServer.shutdown();
+        }
+
         if (this.brokerStatsManager != null) {
             this.brokerStatsManager.shutdown();
         }
@@ -882,12 +1154,26 @@ public class BrokerController {
             this.pullRequestHoldService.shutdown();
         }
 
-        if (this.remotingServer != null) {
-            this.remotingServer.shutdown();
+        {
+            this.popMessageProcessor.getPopLongPollingService().shutdown();
+            this.popMessageProcessor.getQueueLockManager().shutdown();
         }
 
-        if (this.fastRemotingServer != null) {
-            this.fastRemotingServer.shutdown();
+        {
+            this.popMessageProcessor.getPopBufferMergeService().shutdown();
+            this.ackMessageProcessor.shutdownPopReviveService();
+        }
+
+        if (this.assignmentManager != null) {
+            this.assignmentManager.shutdown();
+        }
+
+        if (this.notificationProcessor != null) {
+            this.notificationProcessor.shutdown();
+        }
+
+        if (this.consumerIdsChangeListener != null) {
+            this.consumerIdsChangeListener.shutdown();
         }
 
         if (this.topicQueueMappingCleanService != null) {
@@ -902,20 +1188,14 @@ public class BrokerController {
             this.messageStore.shutdown();
         }
 
-        this.scheduledExecutorService.shutdown();
-        try {
-            this.scheduledExecutorService.awaitTermination(5000, TimeUnit.MILLISECONDS);
-        } catch (InterruptedException e) {
-        }
-
-        this.unregisterBrokerAll();
+        shutdownScheduledExecutorService(this.scheduledExecutorService);
 
         if (this.sendMessageExecutor != null) {
             this.sendMessageExecutor.shutdown();
         }
 
-        if (this.putMessageFutureExecutor != null) {
-            this.putMessageFutureExecutor.shutdown();
+        if (this.litePullMessageExecutor != null) {
+            this.litePullMessageExecutor.shutdown();
         }
 
         if (this.pullMessageExecutor != null) {
@@ -926,12 +1206,16 @@ public class BrokerController {
             this.replyMessageExecutor.shutdown();
         }
 
-        if (this.adminBrokerExecutor != null) {
-            this.adminBrokerExecutor.shutdown();
+        if (this.putMessageFutureExecutor != null) {
+            this.putMessageFutureExecutor.shutdown();
         }
 
-        if (this.brokerOuterAPI != null) {
-            this.brokerOuterAPI.shutdown();
+        if (this.ackMessageExecutor != null) {
+            this.ackMessageExecutor.shutdown();
+        }
+
+        if (this.adminBrokerExecutor != null) {
+            this.adminBrokerExecutor.shutdown();
         }
 
         this.consumerOffsetManager.persist();
@@ -948,6 +1232,15 @@ public class BrokerController {
             this.consumerFilterManager.persist();
         }
 
+        if (this.consumerOrderInfoManager != null) {
+            this.consumerOrderInfoManager.persist();
+        }
+
+        if (this.scheduleMessageService != null) {
+            this.scheduleMessageService.persist();
+            this.scheduleMessageService.shutdown();
+        }
+
         if (this.clientManageExecutor != null) {
             this.clientManageExecutor.shutdown();
         }
@@ -956,13 +1249,12 @@ public class BrokerController {
             this.queryMessageExecutor.shutdown();
         }
 
-        if (this.consumerManageExecutor != null) {
-            this.consumerManageExecutor.shutdown();
+        if (this.heartbeatExecutor != null) {
+            this.heartbeatExecutor.shutdown();
         }
 
-        {
-            this.popMessageProcessor.getPopBufferMergeService().shutdown();
-            this.ackMessageProcessor.shutdownPopReviveService();
+        if (this.consumerManageExecutor != null) {
+            this.consumerManageExecutor.shutdown();
         }
 
         if (this.fileWatchService != null) {
@@ -975,9 +1267,42 @@ public class BrokerController {
         if (this.endTransactionExecutor != null) {
             this.endTransactionExecutor.shutdown();
         }
+
+        if (this.escapeBridge != null) {
+            escapeBridge.shutdown();
+        }
+
+        this.topicConfigManager.persist();
+        this.subscriptionGroupManager.persist();
+
+        for (BrokerAttachedPlugin brokerAttachedPlugin : brokerAttachedPlugins) {
+            if (brokerAttachedPlugin != null) {
+                brokerAttachedPlugin.shutdown();
+            }
+        }
+    }
+
+    public void shutdown() {
+
+        shutdownBasicService();
+
+        if (this.brokerOuterAPI != null) {
+            this.brokerOuterAPI.shutdown();
+        }
+    }
+
+    protected void shutdownScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
+        if (scheduledExecutorService == null) {
+            return;
+        }
+        scheduledExecutorService.shutdown();
+        try {
+            scheduledExecutorService.awaitTermination(5000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ignore) {
+        }
     }
 
-    private void unregisterBrokerAll() {
+    protected void unregisterBrokerAll() {
         this.brokerOuterAPI.unregisterBrokerAll(
             this.brokerConfig.getBrokerClusterName(),
             this.getBrokerAddr(),
@@ -989,30 +1314,42 @@ public class BrokerController {
         return this.brokerConfig.getBrokerIP1() + ":" + this.nettyServerConfig.getListenPort();
     }
 
-    public void start() throws Exception {
-        this.shouldStartTime = System.currentTimeMillis();
+    protected void startBasicService() throws Exception {
 
         if (this.messageStore != null) {
             this.messageStore.start();
         }
 
+        if (remotingServerStartLatch != null) {
+            remotingServerStartLatch.await();
+        }
+
         if (this.remotingServer != null) {
             this.remotingServer.start();
         }
 
-        {
+        if (this.fastRemotingServer != null) {
+            this.fastRemotingServer.start();
+        }
+
+        for (BrokerAttachedPlugin brokerAttachedPlugin : brokerAttachedPlugins) {
+            if (brokerAttachedPlugin != null) {
+                brokerAttachedPlugin.start();
+            }
+        }
+
+        if (this.popMessageProcessor != null) {
             this.popMessageProcessor.getPopLongPollingService().start();
             this.popMessageProcessor.getPopBufferMergeService().start();
             this.popMessageProcessor.getQueueLockManager().start();
-            this.ackMessageProcessor.startPopReviveService();
         }
 
-        {
-            assignmentManager.start();
+        if (this.ackMessageProcessor != null) {
+            this.ackMessageProcessor.startPopReviveService();
         }
 
-        if (this.fastRemotingServer != null) {
-            this.fastRemotingServer.start();
+        if (this.assignmentManager != null) {
+            this.assignmentManager.start();
         }
 
         if (this.topicQueueMappingCleanService != null) {
@@ -1023,10 +1360,6 @@ public class BrokerController {
             this.fileWatchService.start();
         }
 
-        if (this.brokerOuterAPI != null) {
-            this.brokerOuterAPI.start();
-        }
-
         if (this.pullRequestHoldService != null) {
             this.pullRequestHoldService.start();
         }
@@ -1039,9 +1372,34 @@ public class BrokerController {
             this.filterServerManager.start();
         }
 
-        if (!messageStoreConfig.isEnableDLegerCommitLog()) {
-            startProcessorByHa(messageStoreConfig.getBrokerRole());
-            handleSlaveSynchronize(messageStoreConfig.getBrokerRole());
+        if (this.brokerStatsManager != null) {
+            this.brokerStatsManager.start();
+        }
+
+        if (this.brokerFastFailure != null) {
+            this.brokerFastFailure.start();
+        }
+
+        if (this.escapeBridge != null) {
+            this.escapeBridge.start();
+        }
+
+        //Init state version after messageStore initialized.
+        this.topicConfigManager.initStateVersion();
+    }
+
+    public void start() throws Exception {
+
+        this.shouldStartTime = System.currentTimeMillis() + messageStoreConfig.getDisappearTimeAfterStart();
+
+        if (this.brokerOuterAPI != null) {
+            this.brokerOuterAPI.start();
+        }
+
+        startBasicService();
+
+        if (!this.messageStoreConfig.isEnableDLegerCommitLog() && !this.messageStoreConfig.isDuplicationEnable()) {
+            changeSpecialServiceStatus(this.brokerConfig.getBrokerId() == MixAll.MASTER_ID);
             this.registerBrokerAll(true, false, true);
         }
 
@@ -1050,21 +1408,17 @@ public class BrokerController {
             @Override
             public void run() {
                 try {
+                    if (System.currentTimeMillis() < shouldStartTime) {
+                        LOG.info("Register to namesrv after {}", shouldStartTime);
+                        return;
+                    }
                     BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());
                 } catch (Throwable e) {
-                    log.error("registerBrokerAll Exception", e);
+                    LOG.error("BrokerController#registerBrokerAll: unexpected error occurs.", e);
                 }
             }
         }, 1000 * 10, Math.max(10000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);
 
-        if (this.brokerStatsManager != null) {
-            this.brokerStatsManager.start();
-        }
-
-        if (this.brokerFastFailure != null) {
-            this.brokerFastFailure.start();
-        }
-
     }
 
     public synchronized void registerIncrementBrokerData(TopicConfig topicConfig, DataVersion dataVersion) {
@@ -1088,7 +1442,7 @@ public class BrokerController {
                         new TopicConfig(topicConfig.getTopicName(),
                             topicConfig.getReadQueueNums(),
                             topicConfig.getWriteQueueNums(),
-                            this.brokerConfig.getBrokerPermission());
+                            this.brokerConfig.getBrokerPermission(), topicConfig.getTopicSysFlag());
                 } else {
                     registerTopicConfig = new TopicConfig(topicConfig);
                 }
@@ -1128,7 +1482,7 @@ public class BrokerController {
             for (TopicConfig topicConfig : topicConfigWrapper.getTopicConfigTable().values()) {
                 TopicConfig tmp =
                     new TopicConfig(topicConfig.getTopicName(), topicConfig.getReadQueueNums(), topicConfig.getWriteQueueNums(),
-                        this.brokerConfig.getBrokerPermission());
+                        topicConfig.getPerm() & this.brokerConfig.getBrokerPermission(), topicConfig.getTopicSysFlag());
                 topicConfigTable.put(topicConfig.getTopicName(), tmp);
             }
             topicConfigWrapper.setTopicConfigTable(topicConfigTable);
@@ -1138,14 +1492,20 @@ public class BrokerController {
             this.getBrokerAddr(),
             this.brokerConfig.getBrokerName(),
             this.brokerConfig.getBrokerId(),
-            this.brokerConfig.getRegisterBrokerTimeoutMills())) {
+            this.brokerConfig.getRegisterBrokerTimeoutMills(),
+            this.brokerConfig.isInBrokerContainer())) {
             doRegisterBrokerAll(checkOrderConfig, oneway, topicConfigWrapper);
         }
     }
 
-    private void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway,
-        TopicConfigAndMappingSerializeWrapper topicConfigWrapper) {
-        List<RegisterBrokerResult> registerBrokerResultList = this.brokerOuterAPI.registerBrokerAll(
+    protected void doRegisterBrokerAll(boolean checkOrderConfig, boolean oneway,
+        TopicConfigSerializeWrapper topicConfigWrapper) {
+
+        if (shutdown) {
+            LOG.info("BrokerController#doResterBrokerAll: broker has shutdown, no need to register any more.");
+            return;
+        }
+        List<RegisterBrokerResult> registerBrokerResultList = this.getBrokerOuterAPI().registerBrokerAll(
             this.brokerConfig.getBrokerClusterName(),
             this.getBrokerAddr(),
             this.brokerConfig.getBrokerName(),
@@ -1155,10 +1515,16 @@ public class BrokerController {
             this.filterServerManager.buildNewFilterServerList(),
             oneway,
             this.brokerConfig.getRegisterBrokerTimeoutMills(),
-            this.brokerConfig.isCompressedRegister());
+            this.brokerConfig.isEnableSlaveActingMaster(),
+            this.brokerConfig.isCompressedRegister(),
+            this.brokerConfig.isInBrokerContainer());
+
+        handleRegisterBrokerResult(registerBrokerResultList, checkOrderConfig);
+    }
 
-        if (registerBrokerResultList.size() > 0) {
-            RegisterBrokerResult registerBrokerResult = registerBrokerResultList.get(0);
+    protected void handleRegisterBrokerResult(List<RegisterBrokerResult> registerBrokerResultList,
+        boolean checkOrderConfig) {
+        for (RegisterBrokerResult registerBrokerResult : registerBrokerResultList) {
             if (registerBrokerResult != null) {
                 if (this.updateMasterHAServerAddrPeriodically && registerBrokerResult.getHaServerAddr() != null) {
                     this.messageStore.updateHaMasterAddress(registerBrokerResult.getHaServerAddr());
@@ -1169,6 +1535,7 @@ public class BrokerController {
                 if (checkOrderConfig) {
                     this.getTopicConfigManager().updateOrderTopicConfig(registerBrokerResult.getKvTable());
                 }
+                break;
             }
         }
     }
@@ -1177,10 +1544,11 @@ public class BrokerController {
         final String brokerAddr,
         final String brokerName,
         final long brokerId,
-        final int timeoutMills) {
+        final int timeoutMills,
+        final boolean isInBrokerContainer) {
 
         TopicConfigSerializeWrapper topicConfigWrapper = this.getTopicConfigManager().buildTopicConfigSerializeWrapper();
-        List<Boolean> changeList = brokerOuterAPI.needRegister(clusterName, brokerAddr, brokerName, brokerId, topicConfigWrapper, timeoutMills);
+        List<Boolean> changeList = brokerOuterAPI.needRegister(clusterName, brokerAddr, brokerName, brokerId, topicConfigWrapper, timeoutMills, isInBrokerContainer);
         boolean needRegister = false;
         for (Boolean changed : changeList) {
             if (changed) {
@@ -1191,6 +1559,16 @@ public class BrokerController {
         return needRegister;
     }
 
+    public String getNameServerList() {
+        if (this.brokerConfig.getNamesrvAddr() != null) {
+            this.brokerOuterAPI.updateNameServerAddressList(this.brokerConfig.getNamesrvAddr());
+            return this.brokerConfig.getNamesrvAddr();
+        } else if (this.brokerConfig.isFetchNamesrvAddrByAddressServer()) {
+            return this.brokerOuterAPI.fetchNameServerAddr();
+        }
+        return null;
+    }
+
     public TopicConfigManager getTopicConfigManager() {
         return topicConfigManager;
     }
@@ -1215,10 +1593,18 @@ public class BrokerController {
         return slaveSynchronize;
     }
 
+    public ScheduledExecutorService getScheduledExecutorService() {
+        return scheduledExecutorService;
+    }
+
     public ExecutorService getPullMessageExecutor() {
         return pullMessageExecutor;
     }
 
+    public ExecutorService getPutMessageFutureExecutor() {
+        return putMessageFutureExecutor;
+    }
+
     public void setPullMessageExecutor(ExecutorService pullMessageExecutor) {
         this.pullMessageExecutor = pullMessageExecutor;
     }
@@ -1227,6 +1613,10 @@ public class BrokerController {
         return sendThreadPoolQueue;
     }
 
+    public BlockingQueue<Runnable> getAckThreadPoolQueue() {
+        return ackThreadPoolQueue;
+    }
+
     public FilterServerManager getFilterServerManager() {
         return filterServerManager;
     }
@@ -1241,7 +1631,7 @@ public class BrokerController {
 
     public void registerSendMessageHook(final SendMessageHook hook) {
         this.sendMessageHookList.add(hook);
-        log.info("register SendMessageHook Hook, {}", hook.hookName());
+        LOG.info("register SendMessageHook Hook, {}", hook.hookName());
     }
 
     public List<ConsumeMessageHook> getConsumeMessageHookList() {
@@ -1250,7 +1640,7 @@ public class BrokerController {
 
     public void registerConsumeMessageHook(final ConsumeMessageHook hook) {
         this.consumeMessageHookList.add(hook);
-        log.info("register ConsumeMessageHook Hook, {}", hook.hookName());
+        LOG.info("register ConsumeMessageHook Hook, {}", hook.hookName());
     }
 
     public void registerServerRPCHook(RPCHook rpcHook) {
@@ -1266,6 +1656,14 @@ public class BrokerController {
         this.remotingServer = remotingServer;
     }
 
+    public CountDownLatch getRemotingServerStartLatch() {
+        return remotingServerStartLatch;
+    }
+
+    public void setRemotingServerStartLatch(CountDownLatch remotingServerStartLatch) {
+        this.remotingServerStartLatch = remotingServerStartLatch;
+    }
+
     public void registerClientRPCHook(RPCHook rpcHook) {
         this.getBrokerOuterAPI().registerRPCHook(rpcHook);
     }
@@ -1325,133 +1723,140 @@ public class BrokerController {
         return accessValidatorMap;
     }
 
-    private void handleSlaveSynchronize(BrokerRole role) {
-        if (role == BrokerRole.SLAVE) {
-            if (null != slaveSyncFuture) {
-                slaveSyncFuture.cancel(false);
-            }
-            this.slaveSynchronize.setMasterAddr(null);
-            slaveSyncFuture = this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        BrokerController.this.slaveSynchronize.syncAll();
-                    } catch (Throwable e) {
-                        log.error("ScheduledTask SlaveSynchronize syncAll error.", e);
-                    }
-                }
-            }, 1000 * 3, 1000 * 10, TimeUnit.MILLISECONDS);
-        } else {
-            //handle the slave synchronise
-            if (null != slaveSyncFuture) {
-                slaveSyncFuture.cancel(false);
-            }
-            this.slaveSynchronize.setMasterAddr(null);
-        }
+    public ExecutorService getSendMessageExecutor() {
+        return sendMessageExecutor;
     }
 
-    public void changeToSlave(int brokerId) {
-        log.info("Begin to change to slave brokerName={} brokerId={}", brokerConfig.getBrokerName(), brokerId);
+    public AssignmentManager getAssignmentManager() {
+        return assignmentManager;
+    }
 
-        //change the role
-        brokerConfig.setBrokerId(brokerId == 0 ? 1 : brokerId); //TO DO check
-        messageStoreConfig.setBrokerRole(BrokerRole.SLAVE);
+    public SendMessageProcessor getSendMessageProcessor() {
+        return sendMessageProcessor;
+    }
 
-        //handle the scheduled service
-        try {
-            this.messageStore.handleScheduleMessageService(BrokerRole.SLAVE);
-        } catch (Throwable t) {
-            log.error("[MONITOR] handleScheduleMessageService failed when changing to slave", t);
-        }
+    public QueryAssignmentProcessor getQueryAssignmentProcessor() {
+        return queryAssignmentProcessor;
+    }
 
-        //handle the transactional service
-        try {
-            this.shutdownProcessorByHa();
-        } catch (Throwable t) {
-            log.error("[MONITOR] shutdownProcessorByHa failed when changing to slave", t);
-        }
+    public TopicQueueMappingCleanService getTopicQueueMappingCleanService() {
+        return topicQueueMappingCleanService;
+    }
 
-        //handle the slave synchronise
-        handleSlaveSynchronize(BrokerRole.SLAVE);
+    public ExecutorService getAdminBrokerExecutor() {
+        return adminBrokerExecutor;
+    }
 
-        try {
-            this.registerBrokerAll(true, true, true);
-        } catch (Throwable ignored) {
+    public BlockingQueue<Runnable> getLitePullThreadPoolQueue() {
+        return litePullThreadPoolQueue;
+    }
 
-        }
-        log.info("Finish to change to slave brokerName={} brokerId={}", brokerConfig.getBrokerName(), brokerId);
+    public ShutdownHook getShutdownHook() {
+        return shutdownHook;
     }
 
-    public void changeToMaster(BrokerRole role) {
-        if (role == BrokerRole.SLAVE) {
-            return;
-        }
-        log.info("Begin to change to master brokerName={}", brokerConfig.getBrokerName());
+    public void setShutdownHook(ShutdownHook shutdownHook) {
+        this.shutdownHook = shutdownHook;
+    }
 
-        //handle the slave synchronise
-        handleSlaveSynchronize(role);
+    public long getMinBrokerIdInGroup() {
+        return this.brokerConfig.getBrokerId();
+    }
 
-        //handle the scheduled service
-        try {
-            this.messageStore.handleScheduleMessageService(role);
-        } catch (Throwable t) {
-            log.error("[MONITOR] handleScheduleMessageService failed when changing to master", t);
-        }
+    public BrokerController peekMasterBroker() {
+        return brokerConfig.getBrokerId() == MixAll.MASTER_ID ? this : null;
+    }
 
-        //handle the transactional service
-        try {
-            this.startProcessorByHa(BrokerRole.SYNC_MASTER);
-        } catch (Throwable t) {
-            log.error("[MONITOR] startProcessorByHa failed when changing to master", t);
-        }
+    public BrokerMemberGroup getBrokerMemberGroup() {
+        return this.brokerMemberGroup;
+    }
 
-        //if the operations above are totally successful, we change to master
-        brokerConfig.setBrokerId(0); //TO DO check
-        messageStoreConfig.setBrokerRole(role);
+    public boolean isSpecialServiceRunning() {
+        return this.brokerConfig.getBrokerId() == MixAll.MASTER_ID;
+    }
 
-        try {
-            this.registerBrokerAll(true, true, true);
-        } catch (Throwable ignored) {
+    public void updateMinBroker(long minBrokerId, String minBrokerAddr) {
+        //do nothing
+    }
 
-        }
-        log.info("Finish to change to master brokerName={}", brokerConfig.getBrokerName());
+    public void updateMinBroker(long minBrokerId, String minBrokerAddr, String offlineBrokerAddr, String masterHaAddr) {
+        //do nothing
     }
 
-    private void startProcessorByHa(BrokerRole role) {
-        if (BrokerRole.SLAVE != role) {
-            if (this.transactionalMessageCheckService != null) {
-                this.transactionalMessageCheckService.start();
-            }
-        }
+    public int getListenPort() {
+        return this.nettyServerConfig.getListenPort();
     }
 
-    private void shutdownProcessorByHa() {
-        if (this.transactionalMessageCheckService != null) {
-            this.transactionalMessageCheckService.shutdown(true);
-        }
+    public List<BrokerAttachedPlugin> getBrokerAttachedPlugins() {
+        return brokerAttachedPlugins;
     }
 
-    public ExecutorService getPutMessageFutureExecutor() {
-        return putMessageFutureExecutor;
+    public EscapeBridge getEscapeBridge() {
+        return escapeBridge;
     }
 
     public long getShouldStartTime() {
         return shouldStartTime;
     }
 
-    public AssignmentManager getAssignmentManager() {
-        return assignmentManager;
+    public void changeSpecialServiceStatus(boolean shouldStart) {
+
+        for (BrokerAttachedPlugin brokerAttachedPlugin : brokerAttachedPlugins) {
+            if (brokerAttachedPlugin != null) {
+                brokerAttachedPlugin.statusChanged(shouldStart);
+            }
+        }
+
+        changeScheduleServiceStatus(shouldStart);
+
+        changeTransactionCheckServiceStatus(shouldStart);
+
+        if (this.ackMessageProcessor != null) {
+            LOG.info("Set PopReviveService Status to {}", shouldStart);
+            this.ackMessageProcessor.setPopReviveServiceStatus(shouldStart);
+        }
     }
 
-    public SendMessageProcessor getSendMessageProcessor() {
-        return sendMessageProcessor;
+    private synchronized void changeTransactionCheckServiceStatus(boolean shouldStart) {
+        if (isTransactionCheckServiceStart != shouldStart) {
+            LOG.info("TransactionCheckService status changed to {}", shouldStart);
+            if (shouldStart) {
+                this.transactionalMessageCheckService.start();
+            } else {
+                this.transactionalMessageCheckService.shutdown(true);
+            }
+            isTransactionCheckServiceStart = shouldStart;
+        }
     }
 
-    public QueryAssignmentProcessor getQueryAssignmentProcessor() {
-        return queryAssignmentProcessor;
+    public synchronized void changeScheduleServiceStatus(boolean shouldStart) {
+        if (isScheduleServiceStart != shouldStart) {
+            LOG.info("ScheduleServiceStatus changed to {}", shouldStart);
+            if (shouldStart) {
+                this.scheduleMessageService.start();
+            } else {
+                this.scheduleMessageService.stop();
+            }
+            isScheduleServiceStart = shouldStart;
+        }
     }
 
-    public TopicQueueMappingCleanService getTopicQueueMappingCleanService() {
-        return topicQueueMappingCleanService;
+    public boolean isScheduleServiceStart() {
+        return isScheduleServiceStart;
+    }
+
+    public boolean isTransactionCheckServiceStart() {
+        return isTransactionCheckServiceStart;
+    }
+
+    public ScheduleMessageService getScheduleMessageService() {
+        return scheduleMessageService;
+    }
+
+    public MessageStore getMessageStoreByBrokerName(String brokerName) {
+        if (this.brokerConfig.getBrokerName().equals(brokerName)) {
+            return this.getMessageStore();
+        }
+        return null;
     }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
index 19e618b..ca388b6 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/BrokerStartup.java
@@ -19,9 +19,9 @@ package org.apache.rocketmq.broker;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.joran.JoranConfigurator;
 import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
-import org.apache.commons.cli.PosixParser;
 import org.apache.rocketmq.common.BrokerConfig;
 import org.apache.rocketmq.common.MQVersion;
 import org.apache.rocketmq.common.MixAll;
@@ -37,6 +37,7 @@ import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.srvutil.ServerUtil;
 import org.apache.rocketmq.store.config.BrokerRole;
 import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.BufferedInputStream;
@@ -52,6 +53,7 @@ public class BrokerStartup {
     public static CommandLine commandLine = null;
     public static String configFile = null;
     public static InternalLogger log;
+    public static SystemConfigFileHelper configFileHelper = new SystemConfigFileHelper();
 
     public static void main(String[] args) {
         start(createBrokerController(args));
@@ -93,7 +95,7 @@ public class BrokerStartup {
             //PackageConflictDetect.detectFastjson();
             Options options = ServerUtil.buildCommandlineOptions(new Options());
             commandLine = ServerUtil.parseCmdLine("mqbroker", args, buildCommandlineOptions(options),
-                new PosixParser());
+                new DefaultParser());
             if (null == commandLine) {
                 System.exit(-1);
             }
@@ -115,22 +117,21 @@ public class BrokerStartup {
             if (commandLine.hasOption('c')) {
                 String file = commandLine.getOptionValue('c');
                 if (file != null) {
+                    configFileHelper.setFile(file);
                     configFile = file;
-                    InputStream in = new BufferedInputStream(new FileInputStream(file));
-                    properties = new Properties();
-                    properties.load(in);
-
-                    properties2SystemEnv(properties);
-                    MixAll.properties2Object(properties, brokerConfig);
-                    MixAll.properties2Object(properties, nettyServerConfig);
-                    MixAll.properties2Object(properties, nettyClientConfig);
-                    MixAll.properties2Object(properties, messageStoreConfig);
-
                     BrokerPathConfigHelper.setBrokerConfigPath(file);
-                    in.close();
                 }
             }
 
+            properties = configFileHelper.loadConfig();
+            if (properties != null) {
+                properties2SystemEnv(properties);
+                MixAll.properties2Object(properties, brokerConfig);
+                MixAll.properties2Object(properties, nettyServerConfig);
+                MixAll.properties2Object(properties, nettyClientConfig);
+                MixAll.properties2Object(properties, messageStoreConfig);
+            }
+
             MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), brokerConfig);
 
             if (null == brokerConfig.getRocketmqHome()) {
@@ -209,6 +210,8 @@ public class BrokerStartup {
             MixAll.printObjectProperties(log, nettyClientConfig);
             MixAll.printObjectProperties(log, messageStoreConfig);
 
+            brokerConfig.setInBrokerContainer(false);
+
             final BrokerController controller = new BrokerController(
                 brokerConfig,
                 nettyServerConfig,
@@ -276,4 +279,33 @@ public class BrokerStartup {
 
         return options;
     }
+
+    public static class SystemConfigFileHelper {
+        private static final Logger LOGGER = LoggerFactory.getLogger(SystemConfigFileHelper.class);
+
+        private String file;
+
+        public SystemConfigFileHelper() {
+        }
+
+        public Properties loadConfig() throws Exception {
+            InputStream in = new BufferedInputStream(new FileInputStream(file));
+            Properties properties = new Properties();
+            properties.load(in);
+            in.close();
+            return properties;
+        }
+
+        public void update(Properties properties) throws Exception {
+            LOGGER.error("[SystemConfigFileHelper] update no thing.");
+        }
+
+        public void setFile(String file) {
+            this.file = file;
+        }
+
+        public String getFile() {
+            return file;
+        }
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java b/broker/src/main/java/org/apache/rocketmq/broker/ShutdownHook.java
similarity index 77%
copy from broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
copy to broker/src/main/java/org/apache/rocketmq/broker/ShutdownHook.java
index 831e293..63567f8 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/ShutdownHook.java
@@ -14,9 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.rocketmq.broker.client;
+package org.apache.rocketmq.broker;
 
-public interface ConsumerIdsChangeListener {
-
-    void handle(ConsumerGroupEvent event, String group, Object... args);
+public interface ShutdownHook {
+    /**
+     * Code to execute before broker shutdown.
+     *
+     * @param controller broker to shutdown
+     */
+    void beforeShutdown(BrokerController controller);
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ClientHousekeepingService.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ClientHousekeepingService.java
index d536db5..e7890a4 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ClientHousekeepingService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ClientHousekeepingService.java
@@ -17,8 +17,8 @@
 package org.apache.rocketmq.broker.client;
 
 import io.netty.channel.Channel;
-import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
@@ -31,11 +31,12 @@ public class ClientHousekeepingService implements ChannelEventListener {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private final BrokerController brokerController;
 
-    private ScheduledExecutorService scheduledExecutorService = Executors
-        .newSingleThreadScheduledExecutor(new ThreadFactoryImpl("ClientHousekeepingScheduledThread"));
+    private ScheduledExecutorService scheduledExecutorService;
 
     public ClientHousekeepingService(final BrokerController brokerController) {
         this.brokerController = brokerController;
+        scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
+            new ThreadFactoryImpl("ClientHousekeepingScheduledThread", brokerController.getBrokerConfig()));
     }
 
     public void start() {
@@ -64,7 +65,7 @@ public class ClientHousekeepingService implements ChannelEventListener {
 
     @Override
     public void onChannelConnect(String remoteAddr, Channel channel) {
-
+        this.brokerController.getBrokerStatsManager().incChannelConnectNum();
     }
 
     @Override
@@ -72,6 +73,7 @@ public class ClientHousekeepingService implements ChannelEventListener {
         this.brokerController.getProducerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getConsumerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getFilterServerManager().doChannelCloseEvent(remoteAddr, channel);
+        this.brokerController.getBrokerStatsManager().incChannelCloseNum();
     }
 
     @Override
@@ -79,6 +81,7 @@ public class ClientHousekeepingService implements ChannelEventListener {
         this.brokerController.getProducerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getConsumerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getFilterServerManager().doChannelCloseEvent(remoteAddr, channel);
+        this.brokerController.getBrokerStatsManager().incChannelExceptionNum();
     }
 
     @Override
@@ -86,5 +89,6 @@ public class ClientHousekeepingService implements ChannelEventListener {
         this.brokerController.getProducerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getConsumerManager().doChannelCloseEvent(remoteAddr, channel);
         this.brokerController.getFilterServerManager().doChannelCloseEvent(remoteAddr, channel);
+        this.brokerController.getBrokerStatsManager().incChannelIdleNum();
     }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupInfo.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupInfo.java
index c90d494..09e1241 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupInfo.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupInfo.java
@@ -68,6 +68,10 @@ public class ConsumerGroupInfo {
         return subscriptionTable;
     }
 
+    public ClientChannelInfo findChannel(final Channel channel) {
+        return this.channelInfoTable.get(channel);
+    }
+
     public ConcurrentMap<Channel, ClientChannelInfo> getChannelInfoTable() {
         return channelInfoTable;
     }
@@ -113,6 +117,15 @@ public class ConsumerGroupInfo {
         return false;
     }
 
+    /**
+     * Update {@link #channelInfoTable} in {@link ConsumerGroupInfo}
+     *
+     * @param infoNew Channel info of new client.
+     * @param consumeType consume type of new client.
+     * @param messageModel message consuming model (CLUSTERING/BROADCASTING) of new client.
+     * @param consumeFromWhere indicate the position when the client consume message firstly.
+     * @return the result that if new connector is connected or not.
+     */
     public boolean updateChannel(final ClientChannelInfo infoNew, ConsumeType consumeType,
         MessageModel messageModel, ConsumeFromWhere consumeFromWhere) {
         boolean updated = false;
@@ -132,9 +145,9 @@ public class ConsumerGroupInfo {
             infoOld = infoNew;
         } else {
             if (!infoOld.getClientId().equals(infoNew.getClientId())) {
-                log.error("[BUG] consumer channel exist in broker, but clientId not equal. GROUP: {} OLD: {} NEW: {} ",
-                    this.groupName,
-                    infoOld.toString(),
+                log.error(
+                    "ConsumerGroupInfo: consumer channel exists in broker, but clientId is not the same one, "
+                        + "group={}, old clientChannelInfo={}, new clientChannelInfo={}", groupName, infoOld.toString(),
                     infoNew.toString());
                 this.channelInfoTable.put(infoNew.getChannel(), infoNew);
             }
@@ -146,6 +159,12 @@ public class ConsumerGroupInfo {
         return updated;
     }
 
+    /**
+     * Update subscription.
+     *
+     * @param subList set of {@link SubscriptionData}
+     * @return the boolean indicates the subscription has changed or not.
+     */
     public boolean updateSubscription(final Set<SubscriptionData> subList) {
         boolean updated = false;
 
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
index 831e293..144092c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerIdsChangeListener.java
@@ -19,4 +19,6 @@ package org.apache.rocketmq.broker.client;
 public interface ConsumerIdsChangeListener {
 
     void handle(ConsumerGroupEvent event, String group, Object... args);
+
+    void shutdown();
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
index cb60655..b3bee7c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerManager.java
@@ -16,32 +16,41 @@
  */
 package org.apache.rocketmq.broker.client;
 
-import io.netty.channel.Channel;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+
+import io.netty.channel.Channel;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
-import org.apache.rocketmq.logging.InternalLogger;
-import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
 import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
 import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.common.RemotingUtil;
+import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
 public class ConsumerManager {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private static final long CHANNEL_EXPIRED_TIMEOUT = 1000 * 120;
-    private final ConcurrentMap<String/* Group */, ConsumerGroupInfo> consumerTable =
+    private final ConcurrentMap<String, ConsumerGroupInfo> consumerTable =
         new ConcurrentHashMap<String, ConsumerGroupInfo>(1024);
     private final ConsumerIdsChangeListener consumerIdsChangeListener;
+    protected final BrokerStatsManager brokerStatsManager;
 
     public ConsumerManager(final ConsumerIdsChangeListener consumerIdsChangeListener) {
         this.consumerIdsChangeListener = consumerIdsChangeListener;
+        this.brokerStatsManager = null;
+    }
+
+    public ConsumerManager(final ConsumerIdsChangeListener consumerIdsChangeListener, final BrokerStatsManager brokerStatsManager) {
+        this.consumerIdsChangeListener = consumerIdsChangeListener;
+        this.brokerStatsManager = brokerStatsManager;
     }
 
     public ClientChannelInfo findChannel(final String group, final String clientId) {
@@ -61,6 +70,10 @@ public class ConsumerManager {
         return null;
     }
 
+    public ConcurrentMap<String, ConsumerGroupInfo> getConsumerTable() {
+        return this.consumerTable;
+    }
+
     public ConsumerGroupInfo getConsumerGroupInfo(final String group) {
         return this.consumerTable.get(group);
     }
@@ -74,17 +87,18 @@ public class ConsumerManager {
         return 0;
     }
 
-    public void doChannelCloseEvent(final String remoteAddr, final Channel channel) {
+    public boolean doChannelCloseEvent(final String remoteAddr, final Channel channel) {
+        boolean removed = false;
         Iterator<Entry<String, ConsumerGroupInfo>> it = this.consumerTable.entrySet().iterator();
         while (it.hasNext()) {
             Entry<String, ConsumerGroupInfo> next = it.next();
             ConsumerGroupInfo info = next.getValue();
-            boolean removed = info.doChannelCloseEvent(remoteAddr, channel);
+            removed = info.doChannelCloseEvent(remoteAddr, channel);
             if (removed) {
                 if (info.getChannelInfoTable().isEmpty()) {
                     ConsumerGroupInfo remove = this.consumerTable.remove(next.getKey());
                     if (remove != null) {
-                        log.info("unregister consumer ok, no any connection, and remove consumer group, {}",
+                        LOGGER.info("unregister consumer ok, no any connection, and remove consumer group, {}",
                             next.getKey());
                         this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, next.getKey());
                     }
@@ -93,12 +107,13 @@ public class ConsumerManager {
                 this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, next.getKey(), info.getAllChannel());
             }
         }
+        return removed;
     }
 
     public boolean registerConsumer(final String group, final ClientChannelInfo clientChannelInfo,
         ConsumeType consumeType, MessageModel messageModel, ConsumeFromWhere consumeFromWhere,
         final Set<SubscriptionData> subList, boolean isNotifyConsumerIdsChangedEnable) {
-
+        long start = System.currentTimeMillis();
         ConsumerGroupInfo consumerGroupInfo = this.consumerTable.get(group);
         if (null == consumerGroupInfo) {
             ConsumerGroupInfo tmp = new ConsumerGroupInfo(group, consumeType, messageModel, consumeFromWhere);
@@ -116,6 +131,9 @@ public class ConsumerManager {
                 this.consumerIdsChangeListener.handle(ConsumerGroupEvent.CHANGE, group, consumerGroupInfo.getAllChannel());
             }
         }
+        if (null != this.brokerStatsManager) {
+            this.brokerStatsManager.incConsumerRegisterTime((int) (System.currentTimeMillis() - start));
+        }
 
         this.consumerIdsChangeListener.handle(ConsumerGroupEvent.REGISTER, group, subList);
 
@@ -130,7 +148,7 @@ public class ConsumerManager {
             if (consumerGroupInfo.getChannelInfoTable().isEmpty()) {
                 ConsumerGroupInfo remove = this.consumerTable.remove(group);
                 if (remove != null) {
-                    log.info("unregister consumer ok, no any connection, and remove consumer group, {}", group);
+                    LOGGER.info("unregister consumer ok, no any connection, and remove consumer group, {}", group);
 
                     this.consumerIdsChangeListener.handle(ConsumerGroupEvent.UNREGISTER, group);
                 }
@@ -156,7 +174,7 @@ public class ConsumerManager {
                 ClientChannelInfo clientChannelInfo = nextChannel.getValue();
                 long diff = System.currentTimeMillis() - clientChannelInfo.getLastUpdateTimestamp();
                 if (diff > CHANNEL_EXPIRED_TIMEOUT) {
-                    log.warn(
+                    LOGGER.warn(
                         "SCAN: remove expired channel from ConsumerManager consumerTable. channel={}, consumerGroup={}",
                         RemotingHelper.parseChannelRemoteAddr(clientChannelInfo.getChannel()), group);
                     RemotingUtil.closeChannel(clientChannelInfo.getChannel());
@@ -165,7 +183,7 @@ public class ConsumerManager {
             }
 
             if (channelInfoTable.isEmpty()) {
-                log.warn(
+                LOGGER.warn(
                     "SCAN: remove expired channel from ConsumerManager consumerTable, all clear, consumerGroup={}",
                     group);
                 it.remove();
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java b/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
index d716a33..8e6e667 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/DefaultConsumerIdsChangeListener.java
@@ -17,18 +17,46 @@
 package org.apache.rocketmq.broker.client;
 
 import io.netty.channel.Channel;
-
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.AbstractBrokerRunnable;
+import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.common.utils.ThreadUtils;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
 
 public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListener {
+    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private final BrokerController brokerController;
+    private final int cacheSize = 8096;
+
+    private final ScheduledExecutorService scheduledExecutorService =  new ScheduledThreadPoolExecutor(1,
+        ThreadUtils.newGenericThreadFactory("DefaultConsumerIdsChangeListener", true));
+
+    private ConcurrentHashMap<String,List<Channel>> consumerChannelMap = new ConcurrentHashMap<>(cacheSize);
 
     public DefaultConsumerIdsChangeListener(BrokerController brokerController) {
         this.brokerController = brokerController;
+
+        scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(brokerController.getBrokerConfig()) {
+            @Override
+            public void run2() {
+                try {
+                    notifyConsumerChange();
+                } catch (Exception e) {
+                    log.error(
+                        "DefaultConsumerIdsChangeListen#notifyConsumerChange: unexpected error occurs", e);
+                }
+            }
+        }, 30, 15, TimeUnit.SECONDS);
     }
 
     @Override
@@ -43,8 +71,12 @@ public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListen
                 }
                 List<Channel> channels = (List<Channel>) args[0];
                 if (channels != null && brokerController.getBrokerConfig().isNotifyConsumerIdsChangedEnable()) {
-                    for (Channel chl : channels) {
-                        this.brokerController.getBroker2Client().notifyConsumerIdsChanged(chl, group);
+                    if (this.brokerController.getBrokerConfig().isRealTimeNotifyConsumerChange()) {
+                        for (Channel chl : channels) {
+                            this.brokerController.getBroker2Client().notifyConsumerIdsChanged(chl, group);
+                        }
+                    } else {
+                        consumerChannelMap.put(group, channels);
                     }
                 }
                 break;
@@ -62,4 +94,34 @@ public class DefaultConsumerIdsChangeListener implements ConsumerIdsChangeListen
                 throw new RuntimeException("Unknown event " + event);
         }
     }
+
+    private void notifyConsumerChange() {
+
+        if (consumerChannelMap.isEmpty()) {
+            return;
+        }
+
+        ConcurrentHashMap<String, List<Channel>> processMap = new ConcurrentHashMap<>(consumerChannelMap);
+        consumerChannelMap = new ConcurrentHashMap<>(cacheSize);
+
+        for (Map.Entry<String, List<Channel>> entry : processMap.entrySet()) {
+            String consumerId = entry.getKey();
+            List<Channel> channelList = entry.getValue();
+            try {
+                if (channelList != null && brokerController.getBrokerConfig().isNotifyConsumerIdsChangedEnable()) {
+                    for (Channel chl : channelList) {
+                        this.brokerController.getBroker2Client().notifyConsumerIdsChanged(chl, consumerId);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("Failed to notify consumer when some consumers changed, consumerId to notify: {}",
+                    consumerId, e);
+            }
+        }
+    }
+
+    @Override
+    public void shutdown() {
+        this.scheduledExecutorService.shutdown();
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/ProducerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/client/ProducerManager.java
index 4bd00ef..11b9088 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/ProducerManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/ProducerManager.java
@@ -29,6 +29,7 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.common.RemotingUtil;
+import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
 public class ProducerManager {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
@@ -37,9 +38,24 @@ public class ProducerManager {
     private final ConcurrentHashMap<String /* group name */, ConcurrentHashMap<Channel, ClientChannelInfo>> groupChannelTable =
         new ConcurrentHashMap<>();
     private final ConcurrentHashMap<String, Channel> clientChannelTable = new ConcurrentHashMap<>();
+    protected final BrokerStatsManager brokerStatsManager;
     private PositiveAtomicCounter positiveAtomicCounter = new PositiveAtomicCounter();
 
     public ProducerManager() {
+        this.brokerStatsManager = null;
+    }
+
+    public ProducerManager(final BrokerStatsManager brokerStatsManager) {
+        this.brokerStatsManager = brokerStatsManager;
+    }
+
+    public int groupSize() {
+        return this.groupChannelTable.size();
+    }
+
+    public boolean groupOnline(String group) {
+        Map<Channel, ClientChannelInfo> channels = this.groupChannelTable.get(group);
+        return channels != null && !channels.isEmpty();
     }
 
     public ConcurrentHashMap<String, ConcurrentHashMap<Channel, ClientChannelInfo>> getGroupChannelTable() {
@@ -63,7 +79,7 @@ public class ProducerManager {
                     it.remove();
                     clientChannelTable.remove(info.getClientId());
                     log.warn(
-                            "SCAN: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
+                            "ProducerManager#scanNotActiveChannel: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
                             RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group);
                     RemotingUtil.closeChannel(info.getChannel());
                 }
@@ -71,7 +87,8 @@ public class ProducerManager {
         }
     }
 
-    public synchronized void doChannelCloseEvent(final String remoteAddr, final Channel channel) {
+    public synchronized boolean doChannelCloseEvent(final String remoteAddr, final Channel channel) {
+        boolean removed = false;
         if (channel != null) {
             for (final Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
                     .entrySet()) {
@@ -82,6 +99,7 @@ public class ProducerManager {
                         clientChannelInfoTable.remove(channel);
                 if (clientChannelInfo != null) {
                     clientChannelTable.remove(clientChannelInfo.getClientId());
+                    removed = true;
                     log.info(
                             "NETTY EVENT: remove channel[{}][{}] from ProducerManager groupChannelTable, producer group: {}",
                             clientChannelInfo.toString(), remoteAddr, group);
@@ -89,6 +107,7 @@ public class ProducerManager {
 
             }
         }
+        return removed;
     }
 
     public synchronized void registerProducer(final String group, final ClientChannelInfo clientChannelInfo) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java b/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
index 4d28cd8..b78129c 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/net/Broker2Client.java
@@ -76,7 +76,7 @@ public class Broker2Client {
     }
 
     public RemotingCommand callClient(final Channel channel,
-                                      final RemotingCommand request
+        final RemotingCommand request
     ) throws RemotingSendRequestException, RemotingTimeoutException, InterruptedException {
         return this.brokerController.getRemotingServer().invokeSync(channel, request, 10000);
     }
@@ -101,12 +101,13 @@ public class Broker2Client {
         }
     }
 
+
     public RemotingCommand resetOffset(String topic, String group, long timeStamp, boolean isForce) {
         return resetOffset(topic, group, timeStamp, isForce, false);
     }
 
     public RemotingCommand resetOffset(String topic, String group, long timeStamp, boolean isForce,
-                                       boolean isC) {
+        boolean isC) {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
 
         TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(topic);
@@ -237,8 +238,7 @@ public class Broker2Client {
             RemotingCommand.createRequestCommand(RequestCode.GET_CONSUMER_STATUS_FROM_CLIENT,
                 requestHeader);
 
-        Map<String, Map<MessageQueue, Long>> consumerStatusTable =
-            new HashMap<String, Map<MessageQueue, Long>>();
+        Map<String, Map<MessageQueue, Long>> consumerStatusTable = new HashMap<String, Map<MessageQueue, Long>>();
         ConcurrentMap<Channel, ClientChannelInfo> channelInfoTable =
             this.brokerController.getConsumerManager().getConsumerGroupInfo(group).getChannelInfoTable();
         if (null == channelInfoTable || channelInfoTable.isEmpty()) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManager.java b/broker/src/main/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManager.java
index 9056998..a0abd70 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/client/rebalance/RebalanceLockManager.java
@@ -36,6 +36,19 @@ public class RebalanceLockManager {
     private final ConcurrentMap<String/* group */, ConcurrentHashMap<MessageQueue, LockEntry>> mqLockTable =
         new ConcurrentHashMap<String, ConcurrentHashMap<MessageQueue, LockEntry>>(1024);
 
+    public boolean isLockAllExpired(final String group) {
+        final ConcurrentHashMap<MessageQueue, LockEntry> lockEntryMap = mqLockTable.get(group);
+        if (null == lockEntryMap) {
+            return true;
+        }
+        for (LockEntry entry : lockEntryMap.values()) {
+            if (!entry.isExpired()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public boolean tryLock(final String group, final MessageQueue mq, final String clientId) {
 
         if (!this.isLocked(group, mq, clientId)) {
@@ -53,10 +66,9 @@ public class RebalanceLockManager {
                         lockEntry = new LockEntry();
                         lockEntry.setClientId(clientId);
                         groupValue.put(mq, lockEntry);
-                        log.info("tryLock, message queue not locked, I got it. Group: {} NewClientId: {} {}",
-                            group,
-                            clientId,
-                            mq);
+                        log.info(
+                            "RebalanceLockManager#tryLock: lock a message queue which has not been locked yet, "
+                                + "group={}, clientId={}, mq={}", group, clientId, mq);
                     }
 
                     if (lockEntry.isLocked(clientId)) {
@@ -70,26 +82,21 @@ public class RebalanceLockManager {
                         lockEntry.setClientId(clientId);
                         lockEntry.setLastUpdateTimestamp(System.currentTimeMillis());
                         log.warn(
-                            "tryLock, message queue lock expired, I got it. Group: {} OldClientId: {} NewClientId: {} {}",
-                            group,
-                            oldClientId,
-                            clientId,
-                            mq);
+                            "RebalanceLockManager#tryLock: try to lock a expired message queue, group={}, mq={}, old "
+                                + "client id={}, new client id={}", group, mq, oldClientId, clientId);
                         return true;
                     }
 
                     log.warn(
-                        "tryLock, message queue locked by other client. Group: {} OtherClientId: {} NewClientId: {} {}",
-                        group,
-                        oldClientId,
-                        clientId,
-                        mq);
+                        "RebalanceLockManager#tryLock: message queue has been locked by other client, group={}, "
+                            + "mq={}, locked client id={}, current client id={}", group, mq, oldClientId, clientId);
                     return false;
                 } finally {
                     this.lock.unlock();
                 }
             } catch (InterruptedException e) {
-                log.error("putMessage exception", e);
+                log.error("RebalanceLockManager#tryLock: unexpected error, group={}, mq={}, clientId={}", group, mq,
+                    clientId, e);
             }
         } else {
 
@@ -145,10 +152,8 @@ public class RebalanceLockManager {
                             lockEntry.setClientId(clientId);
                             groupValue.put(mq, lockEntry);
                             log.info(
-                                "tryLockBatch, message queue not locked, I got it. Group: {} NewClientId: {} {}",
-                                group,
-                                clientId,
-                                mq);
+                                "RebalanceLockManager#tryLockBatch: lock a message which has not been locked yet, "
+                                    + "group={}, clientId={}, mq={}", group, clientId, mq);
                         }
 
                         if (lockEntry.isLocked(clientId)) {
@@ -163,27 +168,23 @@ public class RebalanceLockManager {
                             lockEntry.setClientId(clientId);
                             lockEntry.setLastUpdateTimestamp(System.currentTimeMillis());
                             log.warn(
-                                "tryLockBatch, message queue lock expired, I got it. Group: {} OldClientId: {} NewClientId: {} {}",
-                                group,
-                                oldClientId,
-                                clientId,
-                                mq);
+                                "RebalanceLockManager#tryLockBatch: try to lock a expired message queue, group={}, "
+                                    + "mq={}, old client id={}, new client id={}", group, mq, oldClientId, clientId);
                             lockedMqs.add(mq);
                             continue;
                         }
 
                         log.warn(
-                            "tryLockBatch, message queue locked by other client. Group: {} OtherClientId: {} NewClientId: {} {}",
-                            group,
-                            oldClientId,
-                            clientId,
-                            mq);
+                            "RebalanceLockManager#tryLockBatch: message queue has been locked by other client, "
+                                + "group={}, mq={}, locked client id={}, current client id={}", group, mq, oldClientId,
+                            clientId);
                     }
                 } finally {
                     this.lock.unlock();
                 }
             } catch (InterruptedException e) {
-                log.error("putMessage exception", e);
+                log.error("RebalanceLockManager#tryBatch: unexpected error, group={}, mqs={}, clientId={}", group, mqs,
+                    clientId, e);
             }
         }
 
@@ -201,34 +202,29 @@ public class RebalanceLockManager {
                         if (null != lockEntry) {
                             if (lockEntry.getClientId().equals(clientId)) {
                                 groupValue.remove(mq);
-                                log.info("unlockBatch, Group: {} {} {}",
-                                    group,
-                                    mq,
-                                    clientId);
+                                log.info("RebalanceLockManager#unlockBatch: unlock mq, group={}, clientId={}, mqs={}",
+                                    group, clientId, mq);
                             } else {
-                                log.warn("unlockBatch, but mq locked by other client: {}, Group: {} {} {}",
-                                    lockEntry.getClientId(),
-                                    group,
-                                    mq,
-                                    clientId);
+                                log.warn(
+                                    "RebalanceLockManager#unlockBatch: mq locked by other client, group={}, locked "
+                                        + "clientId={}, current clientId={}, mqs={}", group, lockEntry.getClientId(),
+                                    clientId, mq);
                             }
                         } else {
-                            log.warn("unlockBatch, but mq not locked, Group: {} {} {}",
-                                group,
-                                mq,
-                                clientId);
+                            log.warn("RebalanceLockManager#unlockBatch: mq not locked, group={}, clientId={}, mq={}",
+                                group, clientId, mq);
                         }
                     }
                 } else {
-                    log.warn("unlockBatch, group not exist, Group: {} {}",
-                        group,
-                        clientId);
+                    log.warn("RebalanceLockManager#unlockBatch: group not exist, group={}, clientId={}, mqs={}", group,
+                        clientId, mqs);
                 }
             } finally {
                 this.lock.unlock();
             }
         } catch (InterruptedException e) {
-            log.error("putMessage exception", e);
+            log.error("RebalanceLockManager#unlockBatch: unexpected error, group={}, mqs={}, clientId={}", group, mqs,
+                clientId);
         }
     }
 
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/dledger/DLedgerRoleChangeHandler.java b/broker/src/main/java/org/apache/rocketmq/broker/dledger/DLedgerRoleChangeHandler.java
index 09bf10c..3f5f024 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/dledger/DLedgerRoleChangeHandler.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/dledger/DLedgerRoleChangeHandler.java
@@ -22,6 +22,8 @@ import io.openmessaging.storage.dledger.MemberState;
 import io.openmessaging.storage.dledger.utils.DLedgerUtils;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
 import org.apache.rocketmq.common.constant.LoggerName;
@@ -33,12 +35,14 @@ import org.apache.rocketmq.store.dledger.DLedgerCommitLog;
 
 public class DLedgerRoleChangeHandler implements DLedgerLeaderElector.RoleChangeHandler {
 
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactoryImpl("DLegerRoleChangeHandler_"));
     private BrokerController brokerController;
     private DefaultMessageStore messageStore;
     private DLedgerCommitLog dLedgerCommitLog;
     private DLedgerServer dLegerServer;
+    private Future<?> slaveSyncFuture;
+
     public DLedgerRoleChangeHandler(BrokerController brokerController, DefaultMessageStore messageStore) {
         this.brokerController = brokerController;
         this.messageStore = messageStore;
@@ -52,15 +56,15 @@ public class DLedgerRoleChangeHandler implements DLedgerLeaderElector.RoleChange
                 long start = System.currentTimeMillis();
                 try {
                     boolean succ = true;
-                    log.info("Begin handling broker role change term={} role={} currStoreRole={}", term, role, messageStore.getMessageStoreConfig().getBrokerRole());
+                    LOGGER.info("Begin handling broker role change term={} role={} currStoreRole={}", term, role, messageStore.getMessageStoreConfig().getBrokerRole());
                     switch (role) {
                         case CANDIDATE:
                             if (messageStore.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE) {
-                                brokerController.changeToSlave(dLedgerCommitLog.getId());
+                                changeToSlave(dLedgerCommitLog.getId());
                             }
                             break;
                         case FOLLOWER:
-                            brokerController.changeToSlave(dLedgerCommitLog.getId());
+                            changeToSlave(dLedgerCommitLog.getId());
                             break;
                         case LEADER:
                             while (true) {
@@ -79,21 +83,89 @@ public class DLedgerRoleChangeHandler implements DLedgerLeaderElector.RoleChange
                             }
                             if (succ) {
                                 messageStore.recoverTopicQueueTable();
-                                brokerController.changeToMaster(BrokerRole.SYNC_MASTER);
+                                changeToMaster(BrokerRole.SYNC_MASTER);
                             }
                             break;
                         default:
                             break;
                     }
-                    log.info("Finish handling broker role change succ={} term={} role={} currStoreRole={} cost={}", succ, term, role, messageStore.getMessageStoreConfig().getBrokerRole(), DLedgerUtils.elapsed(start));
+                    LOGGER.info("Finish handling broker role change succ={} term={} role={} currStoreRole={} cost={}", succ, term, role, messageStore.getMessageStoreConfig().getBrokerRole(), DLedgerUtils.elapsed(start));
                 } catch (Throwable t) {
-                    log.info("[MONITOR]Failed handling broker role change term={} role={} currStoreRole={} cost={}", term, role, messageStore.getMessageStoreConfig().getBrokerRole(), DLedgerUtils.elapsed(start), t);
+                    LOGGER.info("[MONITOR]Failed handling broker role change term={} role={} currStoreRole={} cost={}", term, role, messageStore.getMessageStoreConfig().getBrokerRole(), DLedgerUtils.elapsed(start), t);
                 }
             }
         };
         executorService.submit(runnable);
     }
 
+    private void handleSlaveSynchronize(BrokerRole role) {
+        if (role == BrokerRole.SLAVE) {
+            if (null != slaveSyncFuture) {
+                slaveSyncFuture.cancel(false);
+            }
+            this.brokerController.getSlaveSynchronize().setMasterAddr(null);
+            slaveSyncFuture = this.brokerController.getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        brokerController.getSlaveSynchronize().syncAll();
+                    } catch (Throwable e) {
+                        LOGGER.error("ScheduledTask SlaveSynchronize syncAll error.", e);
+                    }
+                }
+            }, 1000 * 3, 1000 * 10, TimeUnit.MILLISECONDS);
+        } else {
+            //handle the slave synchronise
+            if (null != slaveSyncFuture) {
+                slaveSyncFuture.cancel(false);
+            }
+            this.brokerController.getSlaveSynchronize().setMasterAddr(null);
+        }
+    }
+
+    public void changeToSlave(int brokerId) {
+        LOGGER.info("Begin to change to slave brokerName={} brokerId={}", this.brokerController.getBrokerConfig().getBrokerName(), brokerId);
+
+        //change the role
+        this.brokerController.getBrokerConfig().setBrokerId(brokerId == 0 ? 1 : brokerId); //TO DO check
+        this.brokerController.getMessageStoreConfig().setBrokerRole(BrokerRole.SLAVE);
+
+        this.brokerController.changeSpecialServiceStatus(false);
+
+        //handle the slave synchronise
+        handleSlaveSynchronize(BrokerRole.SLAVE);
+
+        try {
+            this.brokerController.registerBrokerAll(true, true, this.brokerController.getBrokerConfig().isForceRegister());
+        } catch (Throwable ignored) {
+
+        }
+        LOGGER.info("Finish to change to slave brokerName={} brokerId={}", this.brokerController.getBrokerConfig().getBrokerName(), brokerId);
+    }
+
+    public void changeToMaster(BrokerRole role) {
+        if (role == BrokerRole.SLAVE) {
+            return;
+        }
+        LOGGER.info("Begin to change to master brokerName={}", this.brokerController.getBrokerConfig().getBrokerName());
+
+        //handle the slave synchronise
+        handleSlaveSynchronize(role);
+
+        this.brokerController.changeSpecialServiceStatus(true);
+
+        //if the operations above are totally successful, we change to master
+        this.brokerController.getBrokerConfig().setBrokerId(0); //TO DO check
+        this.brokerController.getMessageStoreConfig().setBrokerRole(role);
+
+        try {
+            this.brokerController.registerBrokerAll(true, true, this.brokerController.getBrokerConfig().isForceRegister());
+        } catch (Throwable ignored) {
+
+        }
+        LOGGER.info("Finish to change to master brokerName={}", this.brokerController.getBrokerConfig().getBrokerName());
+    }
+
     @Override public void startup() {
 
     }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java b/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java
new file mode 100644
index 0000000..c7f10a2
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/failover/EscapeBridge.java
@@ -0,0 +1,268 @@
+/*
+ * 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.broker.failover;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
+import org.apache.rocketmq.client.consumer.PullResult;
+import org.apache.rocketmq.client.consumer.PullStatus;
+import org.apache.rocketmq.client.exception.MQClientException;
+import org.apache.rocketmq.client.producer.DefaultMQProducer;
+import org.apache.rocketmq.client.producer.MessageQueueSelector;
+import org.apache.rocketmq.client.producer.SendCallback;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
+import org.apache.rocketmq.store.MessageStore;
+import org.apache.rocketmq.store.PutMessageResult;
+import org.apache.rocketmq.store.PutMessageStatus;
+
+public class EscapeBridge {
+    protected static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private final String innerProducerGroupName;
+    private final String innerConsumerGroupName;
+
+    private final BrokerController brokerController;
+
+    private DefaultMQProducer innerProducer;
+    private DefaultMQPullConsumer innerConsumer;
+
+    public EscapeBridge(BrokerController brokerController) {
+        this.brokerController = brokerController;
+        this.innerProducerGroupName = "InnerProducerGroup_" + brokerController.getBrokerConfig().getBrokerName() + "_" + brokerController.getBrokerConfig().getBrokerId();
+        this.innerConsumerGroupName = "InnerConsumerGroup_" + brokerController.getBrokerConfig().getBrokerName() + "_" + brokerController.getBrokerConfig().getBrokerId();
+    }
+
+    public void start() throws Exception {
+        if (brokerController.getBrokerConfig().isEnableSlaveActingMaster() && brokerController.getBrokerConfig().isEnableRemoteEscape()) {
+            String nameserver = this.brokerController.getNameServerList();
+            if (nameserver != null && !nameserver.isEmpty()) {
+                startInnerProducer(nameserver);
+                startInnerConsumer(nameserver);
+                LOG.info("start inner producer and consumer success.");
+            } else {
+                throw new RuntimeException("nameserver address is null or empty");
+            }
+        }
+    }
+
+    public void shutdown() {
+        if (this.innerProducer != null) {
+            this.innerProducer.shutdown();
+        }
+
+        if (this.innerConsumer != null) {
+            this.innerConsumer.shutdown();
+        }
+    }
+
+    private void startInnerProducer(String nameServer) throws MQClientException {
+        try {
+            innerProducer = new DefaultMQProducer(innerProducerGroupName);
+            innerProducer.setNamesrvAddr(nameServer);
+            innerProducer.start();
+        } catch (MQClientException e) {
+            LOG.error("start inner producer failed, nameserver address: {}", nameServer, e);
+            throw e;
+        }
+    }
+
+    private void startInnerConsumer(String nameServer) throws MQClientException {
+        try {
+            innerConsumer = new DefaultMQPullConsumer(innerConsumerGroupName);
+            innerConsumer.setNamesrvAddr(nameServer);
+            innerConsumer.start();
+        } catch (MQClientException e) {
+            LOG.error("start inner consumer failed, nameserver address: {}", nameServer, e);
+            throw e;
+        }
+    }
+
+    public PutMessageResult putMessage(MessageExtBrokerInner messageExt) {
+        BrokerController masterBroker = this.brokerController.peekMasterBroker();
+        if (masterBroker != null) {
+            return masterBroker.getMessageStore().putMessage(messageExt);
+        } else if (this.brokerController.getBrokerConfig().isEnableSlaveActingMaster()
+            && this.brokerController.getBrokerConfig().isEnableRemoteEscape()
+            && innerProducer != null) {
+            // Remote Acting lead to born timestamp, msgId changed, it need to polish.
+            try {
+                messageExt.setWaitStoreMsgOK(false);
+                SendResult sendResult = innerProducer.send(messageExt);
+                return transformSendResult2PutResult(sendResult);
+            } catch (Exception e) {
+                LOG.error("sendMessageInFailover to remote failed", e);
+                return new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true);
+            }
+        } else {
+            LOG.warn("Put message failed, enableSlaveActingMaster={}, enableRemoteEscape={}.",
+                this.brokerController.getBrokerConfig().isEnableSlaveActingMaster(), this.brokerController.getBrokerConfig().isEnableRemoteEscape());
+            return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
+        }
+    }
+
+    public CompletableFuture<PutMessageResult> asyncPutMessage(MessageExtBrokerInner messageExt) {
+        BrokerController masterBroker = this.brokerController.peekMasterBroker();
+        CompletableFuture<PutMessageResult> completableFuture = new CompletableFuture<>();
+        if (masterBroker != null) {
+            return masterBroker.getMessageStore().asyncPutMessage(messageExt);
+        } else if (this.brokerController.getBrokerConfig().isEnableSlaveActingMaster()
+            && this.brokerController.getBrokerConfig().isEnableRemoteEscape()
+            && innerProducer != null) {
+            // Remote Acting lead to born timestamp, msgId changed, it need to polish.
+            try {
+                messageExt.setWaitStoreMsgOK(false);
+                innerProducer.send(messageExt, new SendCallback() {
+                    @Override public void onSuccess(SendResult sendResult) {
+                        completableFuture.complete(transformSendResult2PutResult(sendResult));
+                    }
+
+                    @Override public void onException(Throwable e) {
+                        completableFuture.complete(new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true));
+                    }
+                });
+                return completableFuture;
+            } catch (Exception e) {
+                LOG.error("sendMessageInFailover to remote failed", e);
+                return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true));
+            }
+        } else {
+            LOG.warn("Put message failed, enableSlaveActingMaster={}, enableRemoteEscape={}.",
+                this.brokerController.getBrokerConfig().isEnableSlaveActingMaster(), this.brokerController.getBrokerConfig().isEnableRemoteEscape());
+            return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null));
+        }
+    }
+
+    public PutMessageResult putMessageToSpecificQueue(MessageExtBrokerInner messageExt) {
+        BrokerController masterBroker = this.brokerController.peekMasterBroker();
+        if (masterBroker != null) {
+            return masterBroker.getMessageStore().putMessage(messageExt);
+        } else if (this.brokerController.getBrokerConfig().isEnableSlaveActingMaster()
+            && this.brokerController.getBrokerConfig().isEnableRemoteEscape()
+            && this.innerProducer != null) {
+            try {
+                messageExt.setWaitStoreMsgOK(false);
+                // Remote Acting lead to born timestamp, msgId changed, it need to polish.
+                SendResult sendResult = innerProducer.send(messageExt, new MessageQueueSelector() {
+                    @Override
+                    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
+                        String id = (String) arg;
+                        int index = Math.abs(id.hashCode()) % mqs.size();
+                        return mqs.get(index);
+                    }
+                }, messageExt.getTopic() + messageExt.getStoreHost());
+                return transformSendResult2PutResult(sendResult);
+            } catch (Exception e) {
+                LOG.error("sendMessageInFailover to remote failed", e);
+                return new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true);
+            }
+        } else {
+            LOG.warn("Put message to specific queue failed, enableSlaveActingMaster={}, enableRemoteEscape={}.",
+                this.brokerController.getBrokerConfig().isEnableSlaveActingMaster(), this.brokerController.getBrokerConfig().isEnableRemoteEscape());
+            return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
+        }
+    }
+
+    private PutMessageResult transformSendResult2PutResult(SendResult sendResult) {
+        if (sendResult == null) {
+            return new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true);
+        }
+        switch (sendResult.getSendStatus()) {
+            case SEND_OK:
+                return new PutMessageResult(PutMessageStatus.PUT_OK, null, true);
+            case SLAVE_NOT_AVAILABLE:
+                return new PutMessageResult(PutMessageStatus.SLAVE_NOT_AVAILABLE, null, true);
+            case FLUSH_DISK_TIMEOUT:
+                return new PutMessageResult(PutMessageStatus.FLUSH_DISK_TIMEOUT, null, true);
+            case FLUSH_SLAVE_TIMEOUT:
+                return new PutMessageResult(PutMessageStatus.FLUSH_SLAVE_TIMEOUT, null, true);
+            default:
+                return new PutMessageResult(PutMessageStatus.PUT_TO_REMOTE_BROKER_FAIL, null, true);
+        }
+    }
+
+    public MessageExt getMessage(String topic, long offset, int queueId, String brokerName) {
+        MessageStore messageStore = brokerController.getMessageStoreByBrokerName(brokerName);
+        if (messageStore != null) {
+            final GetMessageResult getMessageTmpResult = messageStore.getMessage(innerConsumerGroupName, topic, queueId, offset, 1, null);
+            List<MessageExt> list = decodeMsgList(getMessageTmpResult);
+            if (list == null || list.isEmpty()) {
+                LOG.warn("Can not get msg , topic {}, offset {}, queueId {}, result is {}", topic, offset, queueId, getMessageTmpResult);
+                return null;
+            } else {
+                return list.get(0);
+            }
+        } else if (innerConsumer != null) {
+            return getMessageFromRemote(topic, offset, queueId, brokerName);
+        } else {
+            return null;
+        }
+    }
+
+    protected List<MessageExt> decodeMsgList(GetMessageResult getMessageResult) {
+        List<MessageExt> foundList = new ArrayList<>();
+        try {
+            List<ByteBuffer> messageBufferList = getMessageResult.getMessageBufferList();
+            if (messageBufferList != null) {
+                for (int i = 0; i < messageBufferList.size(); i++) {
+                    ByteBuffer bb = messageBufferList.get(i);
+                    if (bb == null) {
+                        LOG.error("bb is null {}", getMessageResult);
+                        continue;
+                    }
+                    MessageExt msgExt = MessageDecoder.decode(bb);
+                    if (msgExt == null) {
+                        LOG.error("decode msgExt is null {}", getMessageResult);
+                        continue;
+                    }
+                    // use CQ offset, not offset in Message
+                    msgExt.setQueueOffset(getMessageResult.getMessageQueueOffset().get(i));
+                    foundList.add(msgExt);
+                }
+            }
+        } finally {
+            getMessageResult.release();
+        }
+
+        return foundList;
+    }
+
+    protected MessageExt getMessageFromRemote(String topic, long offset, int queueId, String brokerName) {
+        try {
+            PullResult pullResult = innerConsumer.pull(new MessageQueue(topic, brokerName, queueId), "*", offset, 1);
+            if (pullResult.getPullStatus().equals(PullStatus.FOUND)) {
+                return pullResult.getMsgFoundList().get(0);
+            }
+        } catch (Exception e) {
+            LOG.error("Get message from remote failed.", e);
+        }
+
+        return null;
+    }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/filtersrv/FilterServerManager.java b/broker/src/main/java/org/apache/rocketmq/broker/filtersrv/FilterServerManager.java
index c1a860a..fa01d54 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/filtersrv/FilterServerManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/filtersrv/FilterServerManager.java
@@ -29,6 +29,7 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.broker.BrokerStartup;
+import org.apache.rocketmq.common.AbstractBrokerRunnable;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.logging.InternalLogger;
@@ -52,9 +53,8 @@ public class FilterServerManager {
 
     public void start() {
 
-        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-            @Override
-            public void run() {
+        this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(brokerController.getBrokerConfig()) {
+            @Override public void run2() {
                 try {
                     FilterServerManager.this.createFilterServer();
                 } catch (Exception e) {
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java b/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
index d176b86..b3184e2 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/latency/BrokerFastFailure.java
@@ -17,25 +17,35 @@
 package org.apache.rocketmq.broker.latency;
 
 import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.common.AbstractBrokerRunnable;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
+import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.netty.RequestTask;
 import org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode;
 
+/**
+ * BrokerFastFailure will cover {@link BrokerController#getSendThreadPoolQueue()} and {@link
+ * BrokerController#getPullThreadPoolQueue()}
+ */
 public class BrokerFastFailure {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
-    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
-        "BrokerFastFailureScheduledThread"));
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private final ScheduledExecutorService scheduledExecutorService;
     private final BrokerController brokerController;
 
+    private volatile long jstackTime = System.currentTimeMillis();
+
     public BrokerFastFailure(final BrokerController brokerController) {
         this.brokerController = brokerController;
+        this.scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
+            new ThreadFactoryImpl("BrokerFastFailureScheduledThread", true,
+                brokerController == null ? null : brokerController.getBrokerConfig()));
     }
 
     public static RequestTask castRunnable(final Runnable runnable) {
@@ -45,16 +55,15 @@ public class BrokerFastFailure {
                 return (RequestTask) object.getRunnable();
             }
         } catch (Throwable e) {
-            log.error(String.format("castRunnable exception, %s", runnable.getClass().getName()), e);
+            LOGGER.error(String.format("castRunnable exception, %s", runnable.getClass().getName()), e);
         }
 
         return null;
     }
 
     public void start() {
-        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
-            @Override
-            public void run() {
+        this.scheduledExecutorService.scheduleAtFixedRate(new AbstractBrokerRunnable(this.brokerController.getBrokerConfig()) {
+            @Override public void run2() {
                 if (brokerController.getBrokerConfig().isBrokerFastFailureEnable()) {
                     cleanExpiredRequest();
                 }
@@ -63,6 +72,7 @@ public class BrokerFastFailure {
     }
 
     private void cleanExpiredRequest() {
+
         while (this.brokerController.getMessageStore().isOSPageCacheBusy()) {
             try {
                 if (!this.brokerController.getSendThreadPoolQueue().isEmpty()) {
@@ -72,7 +82,13 @@ public class BrokerFastFailure {
                     }
 
                     final RequestTask rt = castRunnable(runnable);
-                    rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format("[PCBUSY_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d", System.currentTimeMillis() - rt.getCreateTimestamp(), this.brokerController.getSendThreadPoolQueue().size()));
+                    if (rt != null) {
+                        rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format(
+                            "[PCBUSY_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, "
+                                + "size of queue: %d",
+                            System.currentTimeMillis() - rt.getCreateTimestamp(),
+                            this.brokerController.getSendThreadPoolQueue().size()));
+                    }
                 } else {
                     break;
                 }
@@ -86,11 +102,17 @@ public class BrokerFastFailure {
         cleanExpiredRequestInQueue(this.brokerController.getPullThreadPoolQueue(),
             this.brokerController.getBrokerConfig().getWaitTimeMillsInPullQueue());
 
+        cleanExpiredRequestInQueue(this.brokerController.getLitePullThreadPoolQueue(),
+            this.brokerController.getBrokerConfig().getWaitTimeMillsInLitePullQueue());
+
         cleanExpiredRequestInQueue(this.brokerController.getHeartbeatThreadPoolQueue(),
             this.brokerController.getBrokerConfig().getWaitTimeMillsInHeartbeatQueue());
 
         cleanExpiredRequestInQueue(this.brokerController.getEndTransactionThreadPoolQueue(), this
             .brokerController.getBrokerConfig().getWaitTimeMillsInTransactionQueue());
+
+        cleanExpiredRequestInQueue(this.brokerController.getAckThreadPoolQueue(),
+            brokerController.getBrokerConfig().getWaitTimeMillsInAckQueue());
     }
 
     void cleanExpiredRequestInQueue(final BlockingQueue<Runnable> blockingQueue, final long maxWaitTimeMillsInQueue) {
@@ -111,6 +133,10 @@ public class BrokerFastFailure {
                         if (blockingQueue.remove(runnable)) {
                             rt.setStopRun(true);
                             rt.returnResponse(RemotingSysResponseCode.SYSTEM_BUSY, String.format("[TIMEOUT_CLEAN_QUEUE]broker busy, start flow control for a while, period in queue: %sms, size of queue: %d", behind, blockingQueue.size()));
+                            if (System.currentTimeMillis() - jstackTime > 15000) {
+                                jstackTime = System.currentTimeMillis();
+                                LOGGER.warn("broker jstack \n " + UtilAll.jstack());
+                            }
                         }
                     } else {
                         break;
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/loadbalance/AssignmentManager.java b/broker/src/main/java/org/apache/rocketmq/broker/loadbalance/AssignmentManager.java
index 877ddd8..fe5d5d0 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/loadbalance/AssignmentManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/loadbalance/AssignmentManager.java
@@ -24,10 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
 import org.apache.rocketmq.broker.BrokerController;
-import org.apache.rocketmq.broker.out.BrokerOuterAPI;
 import org.apache.rocketmq.client.exception.MQBrokerException;
 import org.apache.rocketmq.client.impl.factory.MQClientInstance;
 import org.apache.rocketmq.common.MixAll;
@@ -40,25 +37,16 @@ import org.apache.rocketmq.common.topic.TopicValidator;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 
-
 public class AssignmentManager {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
 
     private transient BrokerController brokerController;
 
-    private final static long LOCK_TIMEOUT_MILLIS = 3000;
-
-    private final Lock lockNamesrv = new ReentrantLock();
-
-    private final BrokerOuterAPI mQClientAPIImpl;
-
     private final ConcurrentHashMap<String, Set<MessageQueue>> topicSubscribeInfoTable = new ConcurrentHashMap<String, Set<MessageQueue>>();
 
-    private ScheduledExecutorService scheduledExecutorService = Executors
-        .newSingleThreadScheduledExecutor(new ThreadFactoryImpl("LoadBalanceManagerScheduledThread"));
+    private ScheduledExecutorService scheduledExecutorService;
 
     private static final List<String> IGNORE_ROUTE_TOPICS = Lists.newArrayList(
-        TopicValidator.SYSTEM_TOPIC_PREFIX,
         MixAll.CID_RMQ_SYS_PREFIX,
         MixAll.DEFAULT_CONSUMER_GROUP,
         MixAll.TOOLS_CONSUMER_GROUP,
@@ -74,9 +62,10 @@ public class AssignmentManager {
 
     public AssignmentManager(BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.mQClientAPIImpl = brokerController.getBrokerOuterAPI();
         ignoreRouteTopics.add(brokerController.getBrokerConfig().getBrokerClusterName());
         ignoreRouteTopics.add(brokerController.getBrokerConfig().getBrokerName());
+        scheduledExecutorService = Executors
+            .newSingleThreadScheduledExecutor(new ThreadFactoryImpl("LoadBalanceManagerScheduledThread", brokerController.getBrokerConfig()));
     }
 
     public void start() {
@@ -90,9 +79,12 @@ public class AssignmentManager {
                     log.error("ScheduledTask: failed to pull TopicRouteData from NameServer", e);
                 }
             }
-        }, 200, this.brokerController.getBrokerConfig().getLoadBalancePollNameServerInterval(), TimeUnit.MILLISECONDS);
+        }, 1000, this.brokerController.getBrokerConfig().getLoadBalancePollNameServerInterval(), TimeUnit.MILLISECONDS);
     }
 
+    public void shutdown() {
+        this.scheduledExecutorService.shutdown();
+    }
 
     public void updateTopicRouteInfoFromNameServer() {
         Set<String> topicList = new HashSet<>(brokerController.getTopicConfigManager().getTopicConfigTable().keySet());
@@ -100,7 +92,7 @@ public class AssignmentManager {
         LOOP:
         for (String topic : topicList) {
             for (String keyword : ignoreRouteTopics) {
-                if (topic.contains(keyword)) {
+                if (topic.contains(keyword) || TopicValidator.isSystemTopic(topic)) {
                     continue LOOP;
                 }
             }
@@ -111,7 +103,7 @@ public class AssignmentManager {
 
     public boolean updateTopicRouteInfoFromNameServer(final String topic) {
         try {
-            TopicRouteData topicRouteData = this.mQClientAPIImpl.getTopicRouteInfoFromNameServer(topic, 1000 * 3);
+            TopicRouteData topicRouteData = this.brokerController.getBrokerOuterAPI().getTopicRouteInfoFromNameServer(topic, 1000 * 3);
             if (topicRouteData != null) {
                 Set<MessageQueue> newSubscribeInfo = MQClientInstance.topicRouteData2TopicSubscribeInfo(topic, topicRouteData);
                 Set<MessageQueue> oldSubscribeInfo = topicSubscribeInfoTable.get(topic);
@@ -142,7 +134,6 @@ public class AssignmentManager {
         topicSubscribeInfoTable.remove(topic);
     }
 
-
     public Set<MessageQueue> getTopicSubscribeInfo(String topic) {
         return topicSubscribeInfoTable.get(topic);
     }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/ManyPullRequest.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/ManyPullRequest.java
index 170dae2..08703de 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/ManyPullRequest.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/ManyPullRequest.java
@@ -43,4 +43,8 @@ public class ManyPullRequest {
     public ArrayList<PullRequest> getPullRequestList() {
         return pullRequestList;
     }
+
+    public synchronized boolean isEmpty() {
+        return this.pullRequestList.isEmpty();
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotificationRequest.java
similarity index 56%
copy from broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java
copy to broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotificationRequest.java
index e8e4567..fdae881 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotificationRequest.java
@@ -16,22 +16,19 @@
  */
 package org.apache.rocketmq.broker.longpolling;
 
-import io.netty.channel.Channel;
-import java.util.Comparator;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
+
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
-public class PopRequest {
-    private static final AtomicLong COUNTER = new AtomicLong(Long.MIN_VALUE);
+import io.netty.channel.Channel;
 
+public class NotificationRequest {
     private RemotingCommand remotingCommand;
     private Channel channel;
     private long expired;
     private AtomicBoolean complete = new AtomicBoolean(false);
-    private final long op = COUNTER.getAndIncrement();
 
-    public PopRequest(RemotingCommand remotingCommand, Channel channel, long expired) {
+    public NotificationRequest(RemotingCommand remotingCommand, Channel channel, long expired) {
         this.channel = channel;
         this.remotingCommand = remotingCommand;
         this.expired = expired;
@@ -46,42 +43,15 @@ public class PopRequest {
     }
 
     public boolean isTimeout() {
-        return System.currentTimeMillis() > (expired - 50);
+        return System.currentTimeMillis() > (expired - 3000);
     }
 
     public boolean complete() {
         return complete.compareAndSet(false, true);
     }
 
-    public long getExpired() {
-        return expired;
-    }
-
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder("PopRequest{");
-        sb.append("cmd=").append(remotingCommand);
-        sb.append(", channel=").append(channel);
-        sb.append(", expired=").append(expired);
-        sb.append(", complete=").append(complete);
-        sb.append(", op=").append(op);
-        sb.append('}');
-        return sb.toString();
+        return remotingCommand.toString();
     }
-
-    public static final Comparator<PopRequest> COMPARATOR = new Comparator<PopRequest>() {
-        @Override
-        public int compare(PopRequest o1, PopRequest o2) {
-            int ret = (int) (o1.getExpired() - o2.getExpired());
-
-            if (ret != 0) {
-                return ret;
-            }
-            ret = (int) (o1.op - o2.op);
-            if (ret != 0) {
-                return ret;
-            }
-            return -1;
-        }
-    };
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
index 7803462..3c099fe 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/NotifyMessageArrivingListener.java
@@ -17,24 +17,29 @@
 
 package org.apache.rocketmq.broker.longpolling;
 
-import java.util.Map;
+import org.apache.rocketmq.broker.processor.NotificationProcessor;
 import org.apache.rocketmq.broker.processor.PopMessageProcessor;
 import org.apache.rocketmq.store.MessageArrivingListener;
 
+import java.util.Map;
+
 public class NotifyMessageArrivingListener implements MessageArrivingListener {
     private final PullRequestHoldService pullRequestHoldService;
     private final PopMessageProcessor popMessageProcessor;
+    private final NotificationProcessor notificationProcessor;
 
-    public NotifyMessageArrivingListener(final PullRequestHoldService pullRequestHoldService, final PopMessageProcessor popMessageProcessor) {
+    public NotifyMessageArrivingListener(final PullRequestHoldService pullRequestHoldService, final PopMessageProcessor popMessageProcessor, final NotificationProcessor notificationProcessor) {
         this.pullRequestHoldService = pullRequestHoldService;
         this.popMessageProcessor = popMessageProcessor;
+        this.notificationProcessor = notificationProcessor;
     }
 
     @Override
     public void arriving(String topic, int queueId, long logicOffset, long tagsCode,
-        long msgStoreTime, byte[] filterBitMap, Map<String, String> properties) {
+                         long msgStoreTime, byte[] filterBitMap, Map<String, String> properties) {
         this.pullRequestHoldService.notifyMessageArriving(topic, queueId, logicOffset, tagsCode,
             msgStoreTime, filterBitMap, properties);
         this.popMessageProcessor.notifyMessageArriving(topic, queueId);
+        this.notificationProcessor.notifyMessageArriving(topic, queueId);
     }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java
index e8e4567..2eccf77 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PopRequest.java
@@ -16,12 +16,14 @@
  */
 package org.apache.rocketmq.broker.longpolling;
 
-import io.netty.channel.Channel;
 import java.util.Comparator;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
+
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
+import io.netty.channel.Channel;
+
 public class PopRequest {
     private static final AtomicLong COUNTER = new AtomicLong(Long.MIN_VALUE);
 
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
index 85ca9f7..0a05dba 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/longpolling/PullRequestHoldService.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.common.ServiceThread;
 import org.apache.rocketmq.common.SystemClock;
@@ -78,7 +79,7 @@ public class PullRequestHoldService extends ServiceThread {
                 this.checkHoldRequest();
                 long costTime = this.systemClock.now() - beginLockTimestamp;
                 if (costTime > 5 * 1000) {
-                    log.info("[NOTIFYME] check hold request cost {} ms.", costTime);
+                    log.warn("PullRequestHoldService: check hold pull request cost {}ms", costTime);
                 }
             } catch (Throwable e) {
                 log.warn(this.getServiceName() + " service has exception. ", e);
@@ -90,6 +91,9 @@ public class PullRequestHoldService extends ServiceThread {
 
     @Override
     public String getServiceName() {
+        if (brokerController != null && brokerController.getBrokerConfig().isInBrokerContainer()) {
+            return this.brokerController.getBrokerConfig().getLoggerIdentifier() + PullRequestHoldService.class.getSimpleName();
+        }
         return PullRequestHoldService.class.getSimpleName();
     }
 
@@ -103,7 +107,9 @@ public class PullRequestHoldService extends ServiceThread {
                 try {
                     this.notifyMessageArriving(topic, queueId, offset);
                 } catch (Throwable e) {
-                    log.error("check hold request failed. topic={}, queueId={}", topic, queueId, e);
+                    log.error(
+                        "PullRequestHoldService: failed to check hold request failed, topic={}, queueId={}", topic,
+                        queueId, e);
                 }
             }
         }
@@ -141,7 +147,9 @@ public class PullRequestHoldService extends ServiceThread {
                                 this.brokerController.getPullMessageProcessor().executeRequestWhenWakeup(request.getClientChannel(),
                                     request.getRequestCommand());
                             } catch (Throwable e) {
-                                log.error("execute request when wakeup failed.", e);
+                                log.error(
+                                    "PullRequestHoldService#notifyMessageArriving: failed to execute request when "
+                                        + "message matched, topic={}, queueId={}", topic, queueId, e);
                             }
                             continue;
                         }
@@ -152,7 +160,9 @@ public class PullRequestHoldService extends ServiceThread {
                             this.brokerController.getPullMessageProcessor().executeRequestWhenWakeup(request.getClientChannel(),
                                 request.getRequestCommand());
                         } catch (Throwable e) {
-                            log.error("execute request when wakeup failed.", e);
+                            log.error(
+                                "PullRequestHoldService#notifyMessageArriving: failed to execute request when time's "
+                                    + "up, topic={}, queueId={}", topic, queueId, e);
                         }
                         continue;
                     }
@@ -166,4 +176,22 @@ public class PullRequestHoldService extends ServiceThread {
             }
         }
     }
+
+    public void notifyMasterOnline() {
+        for (ManyPullRequest mpr : this.pullRequestTable.values()) {
+            if (mpr == null || mpr.isEmpty()) {
+                continue;
+            }
+            for (PullRequest request : mpr.cloneListAndClear()) {
+                try {
+                    log.info("notify master online, wakeup {} {}", request.getClientChannel(), request.getRequestCommand());
+                    this.brokerController.getPullMessageProcessor().executeRequestWhenWakeup(request.getClientChannel(),
+                        request.getRequestCommand());
+                } catch (Throwable e) {
+                    log.error("execute request when master online failed.", e);
+                }
+            }
+        }
+
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/AbortProcessException.java b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/AbortProcessException.java
new file mode 100644
index 0000000..c81a29a
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/AbortProcessException.java
@@ -0,0 +1,69 @@
+/*
+ * 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.broker.mqtrace;
+
+import org.apache.rocketmq.common.UtilAll;
+import org.apache.rocketmq.common.help.FAQUrl;
+
+/**
+ *
+ * This exception is used for broker hooks only : SendMessageHook, ConsumeMessageHook, pullMessageHook
+ * This exception is not ignored while executing hooks and it means that
+ * certain processor should return an immediate error response to the client. The
+ * error response code is included in AbortProcessException.  it's naming might
+ * be confusing, so feel free to refactor this class. Also when any class implements
+ * the 3 hook interface mentioned above we should be careful if we want to throw
+ * an AbortProcessException, because it will change the control flow of broker
+ * and cause a RemotingCommand return error immediately. So be aware of the side
+ * effect before throw AbortProcessException in your implementation.
+ *
+ */
+public class AbortProcessException extends RuntimeException {
+    private static final long serialVersionUID = -5728810933841185841L;
+    private int responseCode;
+    private String errorMessage;
+
+    public AbortProcessException(String errorMessage, Throwable cause) {
+        super(FAQUrl.attachDefaultURL(errorMessage), cause);
+        this.responseCode = -1;
+        this.errorMessage = errorMessage;
+    }
+
+    public AbortProcessException(int responseCode, String errorMessage) {
+        super(FAQUrl.attachDefaultURL("CODE: " + UtilAll.responseCode2String(responseCode) + "  DESC: "
+            + errorMessage));
+        this.responseCode = responseCode;
+        this.errorMessage = errorMessage;
+    }
+
+    public int getResponseCode() {
+        return responseCode;
+    }
+
+    public AbortProcessException setResponseCode(final int responseCode) {
+        this.responseCode = responseCode;
+        return this;
+    }
+
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(final String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+}
\ No newline at end of file
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/ConsumeMessageContext.java b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/ConsumeMessageContext.java
index ae5d077..ed7bfba 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/ConsumeMessageContext.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/ConsumeMessageContext.java
@@ -17,6 +17,8 @@
 package org.apache.rocketmq.broker.mqtrace;
 
 import java.util.Map;
+
+import org.apache.rocketmq.common.TopicConfig;
 import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
 public class ConsumeMessageContext {
@@ -30,13 +32,22 @@ public class ConsumeMessageContext {
     private boolean success;
     private String status;
     private Object mqTraceContext;
+    private TopicConfig topicConfig;
+
+    private String accountAuthType;
+    private String accountOwnerParent;
+    private String accountOwnerSelf;
+    private int rcvMsgNum;
+    private int rcvMsgSize;
+    private BrokerStatsManager.StatsType rcvStat;
+    private int commercialRcvMsgNum;
 
     private String commercialOwner;
     private BrokerStatsManager.StatsType commercialRcvStats;
     private int commercialRcvTimes;
     private int commercialRcvSize;
-    private String namespace;
 
+    private String namespace;
     public String getConsumerGroup() {
         return consumerGroup;
     }
@@ -109,6 +120,14 @@ public class ConsumeMessageContext {
         this.mqTraceContext = mqTraceContext;
     }
 
+    public TopicConfig getTopicConfig() {
+        return topicConfig;
+    }
+
+    public void setTopicConfig(TopicConfig topicConfig) {
+        this.topicConfig = topicConfig;
+    }
+
     public int getBodyLength() {
         return bodyLength;
     }
@@ -117,6 +136,62 @@ public class ConsumeMessageContext {
         this.bodyLength = bodyLength;
     }
 
+    public String getAccountAuthType() {
+        return accountAuthType;
+    }
+
+    public void setAccountAuthType(String accountAuthType) {
+        this.accountAuthType = accountAuthType;
+    }
+
+    public String getAccountOwnerParent() {
+        return accountOwnerParent;
+    }
+
+    public void setAccountOwnerParent(String accountOwnerParent) {
+        this.accountOwnerParent = accountOwnerParent;
+    }
+
+    public String getAccountOwnerSelf() {
+        return accountOwnerSelf;
+    }
+
+    public void setAccountOwnerSelf(String accountOwnerSelf) {
+        this.accountOwnerSelf = accountOwnerSelf;
+    }
+
+    public int getRcvMsgNum() {
+        return rcvMsgNum;
+    }
+
+    public void setRcvMsgNum(int rcvMsgNum) {
+        this.rcvMsgNum = rcvMsgNum;
+    }
+
+    public int getRcvMsgSize() {
+        return rcvMsgSize;
+    }
+
+    public void setRcvMsgSize(int rcvMsgSize) {
+        this.rcvMsgSize = rcvMsgSize;
+    }
+
+    public BrokerStatsManager.StatsType getRcvStat() {
+        return rcvStat;
+    }
+
+    public void setRcvStat(BrokerStatsManager.StatsType rcvStat) {
+        this.rcvStat = rcvStat;
+    }
+
+    public int getCommercialRcvMsgNum() {
+        return commercialRcvMsgNum;
+    }
+
+    public void setCommercialRcvMsgNum(int commercialRcvMsgNum) {
+        this.commercialRcvMsgNum = commercialRcvMsgNum;
+    }
+
     public String getCommercialOwner() {
         return commercialOwner;
     }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageContext.java b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageContext.java
index ab6452e..aaa84b7 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageContext.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/mqtrace/SendMessageContext.java
@@ -17,11 +17,16 @@
 package org.apache.rocketmq.broker.mqtrace;
 
 import java.util.Properties;
+
 import org.apache.rocketmq.common.message.MessageType;
 import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
 public class SendMessageContext {
+    /** namespace */
+    private String namespace;
+    /** producer group without namespace. */
     private String producerGroup;
+    /** topic without namespace. */
     private String topic;
     private String msgId;
     private String originMsgId;
@@ -38,14 +43,37 @@ public class SendMessageContext {
     private String brokerRegionId;
     private String msgUniqueKey;
     private long bornTimeStamp;
+    private long requestTimeStamp;
     private MessageType msgType = MessageType.Trans_msg_Commit;
+
     private boolean isSuccess = false;
 
+    /**
+     * Account Statistics
+     */
+    private String accountAuthType;
+    private String accountOwnerParent;
+    private String accountOwnerSelf;
+    private int sendMsgNum;
+    private int sendMsgSize;
+    private BrokerStatsManager.StatsType sendStat;
+    private int commercialSendMsgNum;
+
+    /**
+     * For Commercial
+     */
     private String commercialOwner;
     private BrokerStatsManager.StatsType commercialSendStats;
     private int commercialSendSize;
     private int commercialSendTimes;
-    private String namespace;
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
 
     public boolean isSuccess() {
         return isSuccess;
@@ -79,6 +107,14 @@ public class SendMessageContext {
         this.bornTimeStamp = bornTimeStamp;
     }
 
+    public long getRequestTimeStamp() {
+        return requestTimeStamp;
+    }
+
+    public void setRequestTimeStamp(long requestTimeStamp) {
+        this.requestTimeStamp = requestTimeStamp;
+    }
+
     public String getBrokerRegionId() {
         return brokerRegionId;
     }
@@ -207,10 +243,66 @@ public class SendMessageContext {
         this.commercialOwner = commercialOwner;
     }
 
+    public String getAccountAuthType() {
+        return accountAuthType;
+    }
+
+    public void setAccountAuthType(String accountAuthType) {
+        this.accountAuthType = accountAuthType;
+    }
+
+    public String getAccountOwnerParent() {
+        return accountOwnerParent;
+    }
+
+    public void setAccountOwnerParent(String accountOwnerParent) {
+        this.accountOwnerParent = accountOwnerParent;
+    }
+
+    public String getAccountOwnerSelf() {
+        return accountOwnerSelf;
+    }
+
+    public void setAccountOwnerSelf(String accountOwnerSelf) {
+        this.accountOwnerSelf = accountOwnerSelf;
+    }
+
+    public int getSendMsgNum() {
+        return sendMsgNum;
+    }
+
+    public void setSendMsgNum(int sendMsgNum) {
+        this.sendMsgNum = sendMsgNum;
+    }
+
+    public int getSendMsgSize() {
+        return sendMsgSize;
+    }
+
+    public void setSendMsgSize(int sendMsgSize) {
+        this.sendMsgSize = sendMsgSize;
+    }
+
+    public BrokerStatsManager.StatsType getSendStat() {
+        return sendStat;
+    }
+
+    public void setSendStat(BrokerStatsManager.StatsType sendStat) {
+        this.sendStat = sendStat;
+    }
+
     public BrokerStatsManager.StatsType getCommercialSendStats() {
         return commercialSendStats;
     }
 
+    public int getCommercialSendMsgNum() {
+        return commercialSendMsgNum;
+    }
+
+    public void setCommercialSendMsgNum(int commercialSendMsgNum) {
+        this.commercialSendMsgNum = commercialSendMsgNum;
+    }
+
     public void setCommercialSendStats(final BrokerStatsManager.StatsType commercialSendStats) {
         this.commercialSendStats = commercialSendStats;
     }
@@ -230,12 +322,4 @@ public class SendMessageContext {
     public void setCommercialSendTimes(final int commercialSendTimes) {
         this.commercialSendTimes = commercialSendTimes;
     }
-
-    public String getNamespace() {
-        return namespace;
-    }
-
-    public void setNamespace(String namespace) {
-        this.namespace = namespace;
-    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
index f09522a..4eadaa8 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/offset/ConsumerOffsetManager.java
@@ -24,9 +24,11 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.broker.BrokerPathConfigHelper;
 import org.apache.rocketmq.common.ConfigManager;
+import org.apache.rocketmq.common.DataVersion;
 import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.logging.InternalLogger;
@@ -34,14 +36,18 @@ import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 
 public class ConsumerOffsetManager extends ConfigManager {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
-    protected static final String TOPIC_GROUP_SEPARATOR = "@";
+    private static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    public static final String TOPIC_GROUP_SEPARATOR = "@";
+
+    private DataVersion dataVersion = new DataVersion();
 
     protected ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
         new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
 
     protected transient BrokerController brokerController;
 
+    private transient AtomicLong versionChangeCounter = new AtomicLong(0);
+
     public ConsumerOffsetManager() {
     }
 
@@ -49,6 +55,36 @@ public class ConsumerOffsetManager extends ConfigManager {
         this.brokerController = brokerController;
     }
 
+    public void cleanOffset(String group) {
+        Iterator<Entry<String, ConcurrentMap<Integer, Long>>> it = this.offsetTable.entrySet().iterator();
+        while (it.hasNext()) {
+            Entry<String, ConcurrentMap<Integer, Long>> next = it.next();
+            String topicAtGroup = next.getKey();
+            if (topicAtGroup.contains(group)) {
+                String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
+                if (arrays.length == 2 && group.equals(arrays[1])) {
+                    it.remove();
+                    LOG.warn("Clean group's offset, {}, {}", topicAtGroup, next.getValue());
+                }
+            }
+        }
+    }
+
+    public void cleanOffsetByTopic(String topic) {
+        Iterator<Entry<String, ConcurrentMap<Integer, Long>>> it = this.offsetTable.entrySet().iterator();
+        while (it.hasNext()) {
+            Entry<String, ConcurrentMap<Integer, Long>> next = it.next();
+            String topicAtGroup = next.getKey();
+            if (topicAtGroup.contains(topic)) {
+                String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
+                if (arrays.length == 2 && topic.equals(arrays[0])) {
+                    it.remove();
+                    LOG.warn("Clean topic's offset, {}, {}", topicAtGroup, next.getValue());
+                }
+            }
+        }
+    }
+
     public void scanUnsubscribedTopic() {
         Iterator<Entry<String, ConcurrentMap<Integer, Long>>> it = this.offsetTable.entrySet().iterator();
         while (it.hasNext()) {
@@ -62,7 +98,7 @@ public class ConsumerOffsetManager extends ConfigManager {
                 if (null == brokerController.getConsumerManager().findSubscriptionData(group, topic)
                     && this.offsetBehindMuchThanData(topic, next.getValue())) {
                     it.remove();
-                    log.warn("remove topic offset, {}", topicAtGroup);
+                    LOG.warn("remove topic offset, {}", topicAtGroup);
                 }
             }
         }
@@ -118,6 +154,28 @@ public class ConsumerOffsetManager extends ConfigManager {
         return groups;
     }
 
+    public Map<String, Set<String>> getGroupTopicMap() {
+        Map<String, Set<String>> retMap = new HashMap<String, Set<String>>(128);
+
+        for (String key : this.offsetTable.keySet()) {
+            String[] arr = key.split(TOPIC_GROUP_SEPARATOR);
+            if (arr.length == 2) {
+                String topic = arr[0];
+                String group = arr[1];
+
+                Set<String> topics = retMap.get(group);
+                if (topics == null) {
+                    topics = new HashSet<String>(8);
+                    retMap.put(group, topics);
+                }
+
+                topics.add(topic);
+            }
+        }
+
+        return retMap;
+    }
+
     public void commitOffset(final String clientHost, final String group, final String topic, final int queueId,
         final long offset) {
         // topic@group
@@ -134,9 +192,13 @@ public class ConsumerOffsetManager extends ConfigManager {
         } else {
             Long storeOffset = map.put(queueId, offset);
             if (storeOffset != null && offset < storeOffset) {
-                log.warn("[NOTIFYME]update consumer offset less than store. clientHost={}, key={}, queueId={}, requestOffset={}, storeOffset={}", clientHost, key, queueId, offset, storeOffset);
+                LOG.warn("[NOTIFYME]update consumer offset less than store. clientHost={}, key={}, queueId={}, requestOffset={}, storeOffset={}", clientHost, key, queueId, offset, storeOffset);
             }
         }
+        if (versionChangeCounter.incrementAndGet() % brokerController.getBrokerConfig().getConsumerOffsetUpdateVersionStep() == 0) {
+            long stateMachineVersion = brokerController.getMessageStore() != null ? brokerController.getMessageStore().getStateMachineVersion() : 0;
+            dataVersion.nextVersion(stateMachineVersion);
+        }
     }
 
     public long queryOffset(final String group, final String topic, final int queueId) {
@@ -145,8 +207,9 @@ public class ConsumerOffsetManager extends ConfigManager {
         ConcurrentMap<Integer, Long> map = this.offsetTable.get(key);
         if (null != map) {
             Long offset = map.get(queueId);
-            if (offset != null)
+            if (offset != null) {
                 return offset;
+            }
         }
 
         return -1;
@@ -167,10 +230,12 @@ public class ConsumerOffsetManager extends ConfigManager {
             ConsumerOffsetManager obj = RemotingSerializable.fromJson(jsonString, ConsumerOffsetManager.class);
             if (obj != null) {
                 this.offsetTable = obj.offsetTable;
+                this.dataVersion = obj.dataVersion;
             }
         }
     }
 
+    @Override
     public String encode(final boolean prettyFormat) {
         return RemotingSerializable.toJson(this, prettyFormat);
     }
@@ -232,6 +297,14 @@ public class ConsumerOffsetManager extends ConfigManager {
         }
     }
 
+    public DataVersion getDataVersion() {
+        return dataVersion;
+    }
+
+    public void setDataVersion(DataVersion dataVersion) {
+        this.dataVersion = dataVersion;
+    }
+
     public void removeOffset(final String group) {
         Iterator<Entry<String, ConcurrentMap<Integer, Long>>> it = this.offsetTable.entrySet().iterator();
         while (it.hasNext()) {
@@ -241,7 +314,7 @@ public class ConsumerOffsetManager extends ConfigManager {
                 String[] arrays = topicAtGroup.split(TOPIC_GROUP_SEPARATOR);
                 if (arrays.length == 2 && group.equals(arrays[1])) {
                     it.remove();
-                    log.warn("clean group offset {}", topicAtGroup);
+                    LOG.warn("clean group offset {}", topicAtGroup);
                 }
             }
         }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
index 8a2093a..b637951 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/out/BrokerOuterAPI.java
@@ -16,35 +16,67 @@
  */
 package org.apache.rocketmq.broker.out;
 
-import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.broker.latency.BrokerFixedThreadPoolExecutor;
 import org.apache.rocketmq.client.exception.MQBrokerException;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.client.producer.SendStatus;
+import org.apache.rocketmq.common.AbstractBrokerRunnable;
+import org.apache.rocketmq.common.BrokerIdentity;
+import org.apache.rocketmq.common.BrokerSyncInfo;
 import org.apache.rocketmq.common.DataVersion;
+import org.apache.rocketmq.common.LockCallback;
 import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.ThreadFactoryImpl;
+import org.apache.rocketmq.common.UnlockCallback;
 import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.common.message.MessageBatch;
+import org.apache.rocketmq.common.message.MessageClientIDSetter;
+import org.apache.rocketmq.common.message.MessageConst;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.namesrv.DefaultTopAddressing;
 import org.apache.rocketmq.common.namesrv.RegisterBrokerResult;
 import org.apache.rocketmq.common.namesrv.TopAddressing;
 import org.apache.rocketmq.common.protocol.RequestCode;
 import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
 import org.apache.rocketmq.common.protocol.body.ClusterInfo;
 import org.apache.rocketmq.common.protocol.body.ConsumerOffsetSerializeWrapper;
+import org.apache.rocketmq.common.protocol.body.GetBrokerMemberGroupResponseBody;
 import org.apache.rocketmq.common.protocol.body.KVTable;
+import org.apache.rocketmq.common.protocol.body.LockBatchRequestBody;
+import org.apache.rocketmq.common.protocol.body.LockBatchResponseBody;
 import org.apache.rocketmq.common.protocol.body.RegisterBrokerBody;
 import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
 import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
+import org.apache.rocketmq.common.protocol.body.UnlockBatchRequestBody;
+import org.apache.rocketmq.common.protocol.header.ExchangeHAInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.ExchangeHAInfoResponseHeader;
+import org.apache.rocketmq.common.protocol.header.GetBrokerMemberGroupRequestHeader;
+import org.apache.rocketmq.common.protocol.header.GetMaxOffsetRequestHeader;
+import org.apache.rocketmq.common.protocol.header.GetMaxOffsetResponseHeader;
+import org.apache.rocketmq.common.protocol.header.GetMinOffsetRequestHeader;
+import org.apache.rocketmq.common.protocol.header.GetMinOffsetResponseHeader;
+import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeader;
+import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeaderV2;
+import org.apache.rocketmq.common.protocol.header.SendMessageResponseHeader;
+import org.apache.rocketmq.common.protocol.header.namesrv.BrokerHeartbeatRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.GetRouteInfoRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.QueryDataVersionRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.QueryDataVersionResponseHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.RegisterBrokerRequestHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.RegisterBrokerResponseHeader;
 import org.apache.rocketmq.common.protocol.header.namesrv.UnRegisterBrokerRequestHeader;
+import org.apache.rocketmq.common.protocol.route.BrokerData;
 import org.apache.rocketmq.common.protocol.route.TopicRouteData;
 import org.apache.rocketmq.common.rpc.ClientMetadata;
 import org.apache.rocketmq.common.rpc.RpcClient;
 import org.apache.rocketmq.common.rpc.RpcClientImpl;
+import org.apache.rocketmq.common.topic.TopicValidator;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.InvokeCallback;
@@ -61,37 +93,34 @@ import org.apache.rocketmq.remoting.netty.NettyRemotingClient;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
 import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class BrokerOuterAPI {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private final RemotingClient remotingClient;
-    private final BrokerController brokerController;
-    private final TopAddressing topAddressing = new TopAddressing(MixAll.getWSAddr());
+    private final TopAddressing topAddressing = new DefaultTopAddressing(MixAll.getWSAddr());
     private String nameSrvAddr = null;
-    private final String currBrokerName;
     private BrokerFixedThreadPoolExecutor brokerOuterExecutor = new BrokerFixedThreadPoolExecutor(4, 10, 1, TimeUnit.MINUTES,
         new ArrayBlockingQueue<Runnable>(32), new ThreadFactoryImpl("brokerOutApi_thread_", true));
 
     private ClientMetadata clientMetadata;
     private RpcClient rpcClient;
 
-    public BrokerOuterAPI(final NettyClientConfig nettyClientConfig, final BrokerController brokerController) {
-
-        this(nettyClientConfig, null, brokerController, new ClientMetadata());
+    public BrokerOuterAPI(final NettyClientConfig nettyClientConfig) {
+        this(nettyClientConfig, null, new ClientMetadata());
     }
 
-    private BrokerOuterAPI(final NettyClientConfig nettyClientConfig, RPCHook rpcHook, final BrokerController brokerController, ClientMetadata clientMetadata) {
+    private BrokerOuterAPI(final NettyClientConfig nettyClientConfig, RPCHook rpcHook, ClientMetadata clientMetadata) {
         this.remotingClient = new NettyRemotingClient(nettyClientConfig);
         this.clientMetadata = clientMetadata;
         this.remotingClient.registerRPCHook(rpcHook);
-        this.brokerController = brokerController;
-        this.currBrokerName =  brokerController.getBrokerConfig().getBrokerName();
         this.rpcClient = new RpcClientImpl(this.clientMetadata, this.remotingClient);
     }
 
@@ -104,31 +133,224 @@ public class BrokerOuterAPI {
         this.brokerOuterExecutor.shutdown();
     }
 
+    public List<String> getNameServerAddressList() {
+        return this.remotingClient.getNameServerAddressList();
+    }
+
     public String fetchNameServerAddr() {
         try {
             String addrs = this.topAddressing.fetchNSAddr();
             if (addrs != null) {
                 if (!addrs.equals(this.nameSrvAddr)) {
-                    log.info("name server address changed, old: {} new: {}", this.nameSrvAddr, addrs);
+                    LOGGER.info("name server address changed, old: {} new: {}", this.nameSrvAddr, addrs);
                     this.updateNameServerAddressList(addrs);
                     this.nameSrvAddr = addrs;
                     return nameSrvAddr;
                 }
             }
         } catch (Exception e) {
-            log.error("fetchNameServerAddr Exception", e);
+            LOGGER.error("fetchNameServerAddr Exception", e);
         }
         return nameSrvAddr;
     }
 
     public void updateNameServerAddressList(final String addrs) {
-        List<String> lst = new ArrayList<String>();
         String[] addrArray = addrs.split(";");
-        for (String addr : addrArray) {
-            lst.add(addr);
+        List<String> lst = new ArrayList<String>(Arrays.asList(addrArray));
+        this.remotingClient.updateNameServerAddressList(lst);
+    }
+
+    public BrokerMemberGroup syncBrokerMemberGroup(String clusterName, String brokerName)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
+        return syncBrokerMemberGroup(clusterName, brokerName, false);
+    }
+
+    public BrokerMemberGroup syncBrokerMemberGroup(String clusterName, String brokerName,
+        boolean isCompatibleWithOldNameSrv)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
+        if (isCompatibleWithOldNameSrv) {
+            return getBrokerMemberGroupCompatible(clusterName, brokerName);
+        } else {
+            return getBrokerMemberGroup(clusterName, brokerName);
         }
+    }
 
-        this.remotingClient.updateNameServerAddressList(lst);
+    public BrokerMemberGroup getBrokerMemberGroup(String clusterName, String brokerName)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
+        BrokerMemberGroup brokerMemberGroup = new BrokerMemberGroup(clusterName, brokerName);
+
+        GetBrokerMemberGroupRequestHeader requestHeader = new GetBrokerMemberGroupRequestHeader();
+        requestHeader.setClusterName(clusterName);
+        requestHeader.setBrokerName(brokerName);
+
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_BROKER_MEMBER_GROUP, requestHeader);
+
+        RemotingCommand response = null;
+        response = this.remotingClient.invokeSync(null, request, 3000);
+        assert response != null;
+
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                byte[] body = response.getBody();
+                if (body != null) {
+                    GetBrokerMemberGroupResponseBody brokerMemberGroupResponseBody =
+                        GetBrokerMemberGroupResponseBody.decode(body, GetBrokerMemberGroupResponseBody.class);
+
+                    return brokerMemberGroupResponseBody.getBrokerMemberGroup();
+                }
+            }
+            default:
+                break;
+        }
+
+        return brokerMemberGroup;
+    }
+
+    public BrokerMemberGroup getBrokerMemberGroupCompatible(String clusterName, String brokerName)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
+        BrokerMemberGroup brokerMemberGroup = new BrokerMemberGroup(clusterName, brokerName);
+
+        GetRouteInfoRequestHeader requestHeader = new GetRouteInfoRequestHeader();
+        requestHeader.setTopic(TopicValidator.SYNC_BROKER_MEMBER_GROUP_PREFIX + brokerName);
+
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ROUTEINFO_BY_TOPIC, requestHeader);
+
+        RemotingCommand response;
+        response = this.remotingClient.invokeSync(null, request, 3000);
+        assert response != null;
+
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                byte[] body = response.getBody();
+                if (body != null) {
+                    TopicRouteData topicRouteData = TopicRouteData.decode(body, TopicRouteData.class);
+                    for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
+                        if (brokerData != null
+                            && brokerData.getBrokerName().equals(brokerName)
+                            && brokerData.getCluster().equals(clusterName)) {
+                            brokerMemberGroup.getBrokerAddrs().putAll(brokerData.getBrokerAddrs());
+                            break;
+                        }
+                    }
+                    return brokerMemberGroup;
+                }
+            }
+            default:
+                break;
+        }
+
+        return brokerMemberGroup;
+    }
+
+    public void sendHeartbeatViaDataVersion(
+        final String clusterName,
+        final String brokerAddr,
+        final String brokerName,
+        final Long brokerId,
+        final int timeoutMillis,
+        final DataVersion dataVersion,
+        final boolean isInBrokerContainer) {
+        List<String> nameServerAddressList = this.remotingClient.getAvailableNameSrvList();
+        if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
+            final QueryDataVersionRequestHeader requestHeader = new QueryDataVersionRequestHeader();
+            requestHeader.setBrokerAddr(brokerAddr);
+            requestHeader.setBrokerName(brokerName);
+            requestHeader.setBrokerId(brokerId);
+            requestHeader.setClusterName(clusterName);
+
+            for (final String namesrvAddr : nameServerAddressList) {
+                brokerOuterExecutor.execute(new AbstractBrokerRunnable(new BrokerIdentity(clusterName, brokerName, brokerId, isInBrokerContainer)) {
+
+                    @Override
+                    public void run2() {
+                        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.QUERY_DATA_VERSION, requestHeader);
+                        request.setBody(dataVersion.encode());
+
+                        try {
+                            BrokerOuterAPI.this.remotingClient.invokeOneway(namesrvAddr, request, timeoutMillis);
+                        } catch (Exception e) {
+                            LOGGER.error("sendHeartbeat Exception " + namesrvAddr, e);
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    public void sendHeartbeat(final String clusterName,
+        final String brokerAddr,
+        final String brokerName,
+        final Long brokerId,
+        final int timeoutMills,
+        final boolean isInBrokerContainer) {
+        List<String> nameServerAddressList = this.remotingClient.getAvailableNameSrvList();
+
+        final BrokerHeartbeatRequestHeader requestHeader = new BrokerHeartbeatRequestHeader();
+        requestHeader.setClusterName(clusterName);
+        requestHeader.setBrokerAddr(brokerAddr);
+        requestHeader.setBrokerName(brokerName);
+
+        if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
+            for (final String namesrvAddr : nameServerAddressList) {
+                brokerOuterExecutor.execute(new AbstractBrokerRunnable(new BrokerIdentity(clusterName, brokerName, brokerId, isInBrokerContainer)) {
+                    @Override
+                    public void run2() {
+                        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.BROKER_HEARTBEAT, requestHeader);
+
+                        try {
+                            BrokerOuterAPI.this.remotingClient.invokeOneway(namesrvAddr, request, timeoutMills);
+                        } catch (Exception e) {
+                            LOGGER.error("sendHeartbeat Exception " + namesrvAddr, e);
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    public BrokerSyncInfo retrieveBrokerHaInfo(String masterBrokerAddr)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException,
+        MQBrokerException, RemotingCommandException {
+        ExchangeHAInfoRequestHeader requestHeader = new ExchangeHAInfoRequestHeader();
+        requestHeader.setMasterHaAddress(null);
+
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.EXCHANGE_BROKER_HA_INFO, requestHeader);
+
+        RemotingCommand response = this.remotingClient.invokeSync(masterBrokerAddr, request, 3000);
+        assert response != null;
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                ExchangeHAInfoResponseHeader responseHeader = response.decodeCommandCustomHeader(ExchangeHAInfoResponseHeader.class);
+                return new BrokerSyncInfo(responseHeader.getMasterHaAddress(), responseHeader.getMasterFlushOffset(), responseHeader.getMasterAddress());
+            }
+            default:
+                break;
+        }
+
+        throw new MQBrokerException(response.getCode(), response.getRemark());
+    }
+
+    public void sendBrokerHaInfo(String brokerAddr, String masterHaAddr, long brokerInitMaxOffset, String masterAddr)
+        throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
+        ExchangeHAInfoRequestHeader requestHeader = new ExchangeHAInfoRequestHeader();
+        requestHeader.setMasterHaAddress(masterHaAddr);
+        requestHeader.setMasterFlushOffset(brokerInitMaxOffset);
+        requestHeader.setMasterAddress(masterAddr);
+
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.EXCHANGE_BROKER_HA_INFO, requestHeader);
+
+        RemotingCommand response = this.remotingClient.invokeSync(brokerAddr, request, 3000);
+
+        assert response != null;
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                return;
+            }
+            default:
+                break;
+        }
+
+        throw new MQBrokerException(response.getCode(), response.getRemark());
     }
 
     public List<RegisterBrokerResult> registerBrokerAll(
@@ -141,10 +363,56 @@ public class BrokerOuterAPI {
         final List<String> filterServerList,
         final boolean oneway,
         final int timeoutMills,
-        final boolean compressed) {
+        final boolean enableActingMaster,
+        final boolean compressed,
+        final boolean isInBrokerContainer) {
+        return registerBrokerAll(clusterName,
+            brokerAddr,
+            brokerName,
+            brokerId,
+            haServerAddr,
+            topicConfigWrapper,
+            filterServerList,
+            oneway, timeoutMills,
+            enableActingMaster,
+            compressed,
+            null,
+            isInBrokerContainer);
+    }
+
+    /**
+     * Considering compression brings much CPU overhead to name server, stream API will not support compression and
+     * compression feature is deprecated.
+     *
+     * @param clusterName
+     * @param brokerAddr
+     * @param brokerName
+     * @param brokerId
+     * @param haServerAddr
+     * @param topicConfigWrapper
+     * @param filterServerList
+     * @param oneway
+     * @param timeoutMills
+     * @param compressed default false
+     * @return
+     */
+    public List<RegisterBrokerResult> registerBrokerAll(
+        final String clusterName,
+        final String brokerAddr,
+        final String brokerName,
+        final long brokerId,
+        final String haServerAddr,
+        final TopicConfigSerializeWrapper topicConfigWrapper,
+        final List<String> filterServerList,
+        final boolean oneway,
+        final int timeoutMills,
+        final boolean enableActingMaster,
+        final boolean compressed,
+        final Long heartbeatTimeoutMillis,
+        final boolean isInBrokerContainer) {
 
         final List<RegisterBrokerResult> registerBrokerResultList = new CopyOnWriteArrayList<>();
-        List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();
+        List<String> nameServerAddressList = this.remotingClient.getAvailableNameSrvList();
         if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
 
             final RegisterBrokerRequestHeader requestHeader = new RegisterBrokerRequestHeader();
@@ -153,7 +421,11 @@ public class BrokerOuterAPI {
             requestHeader.setBrokerName(brokerName);
             requestHeader.setClusterName(clusterName);
             requestHeader.setHaServerAddr(haServerAddr);
-            requestHeader.setCompressed(compressed);
+            requestHeader.setEnableActingMaster(enableActingMaster);
+            requestHeader.setCompressed(false);
+            if (heartbeatTimeoutMillis != null) {
+                requestHeader.setHeartbeatTimeoutMillis(heartbeatTimeoutMillis);
+            }
 
             RegisterBrokerBody requestBody = new RegisterBrokerBody();
             requestBody.setTopicConfigSerializeWrapper(TopicConfigAndMappingSerializeWrapper.from(topicConfigWrapper));
@@ -163,18 +435,17 @@ public class BrokerOuterAPI {
             requestHeader.setBodyCrc32(bodyCrc32);
             final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());
             for (final String namesrvAddr : nameServerAddressList) {
-                brokerOuterExecutor.execute(new Runnable() {
-                    @Override
-                    public void run() {
+                brokerOuterExecutor.execute(new AbstractBrokerRunnable(new BrokerIdentity(clusterName, brokerName, brokerId, isInBrokerContainer)) {
+                    @Override public void run2() {
                         try {
                             RegisterBrokerResult result = registerBroker(namesrvAddr, oneway, timeoutMills, requestHeader, body);
                             if (result != null) {
                                 registerBrokerResultList.add(result);
                             }
 
-                            log.info("register broker[{}]to name server {} OK", brokerId, namesrvAddr);
+                            LOGGER.info("Registering current broker to name server completed. TargetHost={}", namesrvAddr);
                         } catch (Exception e) {
-                            log.warn("registerBroker Exception, {}", namesrvAddr, e);
+                            LOGGER.error("Failed to register current broker to name server. TargetHost={}", namesrvAddr, e);
                         } finally {
                             countDownLatch.countDown();
                         }
@@ -183,8 +454,10 @@ public class BrokerOuterAPI {
             }
 
             try {
-                countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS);
-            } catch (InterruptedException e) {
+                if (!countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS)) {
+                    LOGGER.warn("Registration to one or more name servers does NOT complete within deadline. Timeout threshold: {}ms", timeoutMills);
+                }
+            } catch (InterruptedException ignore) {
             }
         }
 
@@ -243,9 +516,9 @@ public class BrokerOuterAPI {
             for (String namesrvAddr : nameServerAddressList) {
                 try {
                     this.unregisterBroker(namesrvAddr, clusterName, brokerAddr, brokerName, brokerId);
-                    log.info("unregisterBroker OK, NamesrvAddr: {}", namesrvAddr);
+                    LOGGER.info("unregisterBroker OK, NamesrvAddr: {}", namesrvAddr);
                 } catch (Exception e) {
-                    log.warn("unregisterBroker Exception, {}", namesrvAddr, e);
+                    LOGGER.warn("unregisterBroker Exception, {}", namesrvAddr, e);
                 }
             }
         }
@@ -284,15 +557,15 @@ public class BrokerOuterAPI {
         final String brokerName,
         final long brokerId,
         final TopicConfigSerializeWrapper topicConfigWrapper,
-        final int timeoutMills) {
+        final int timeoutMills,
+        final boolean isInBrokerContainer) {
         final List<Boolean> changedList = new CopyOnWriteArrayList<>();
         List<String> nameServerAddressList = this.remotingClient.getNameServerAddressList();
         if (nameServerAddressList != null && nameServerAddressList.size() > 0) {
             final CountDownLatch countDownLatch = new CountDownLatch(nameServerAddressList.size());
             for (final String namesrvAddr : nameServerAddressList) {
-                brokerOuterExecutor.execute(new Runnable() {
-                    @Override
-                    public void run() {
+                brokerOuterExecutor.execute(new AbstractBrokerRunnable(new BrokerIdentity(clusterName, brokerName, brokerId, isInBrokerContainer)) {
+                    @Override public void run2() {
                         try {
                             QueryDataVersionRequestHeader requestHeader = new QueryDataVersionRequestHeader();
                             requestHeader.setBrokerAddr(brokerAddr);
@@ -323,10 +596,10 @@ public class BrokerOuterAPI {
                                 default:
                                     break;
                             }
-                            log.warn("Query data version from name server {} OK,changed {}, broker {},name server {}", namesrvAddr, changed, topicConfigWrapper.getDataVersion(), nameServerDataVersion == null ? "" : nameServerDataVersion);
+                            LOGGER.warn("Query data version from name server {} OK, changed {}, broker {},name server {}", namesrvAddr, changed, topicConfigWrapper.getDataVersion(), nameServerDataVersion == null ? "" : nameServerDataVersion);
                         } catch (Exception e) {
                             changedList.add(Boolean.TRUE);
-                            log.error("Query data version from name server {}  Exception, {}", namesrvAddr, e);
+                            LOGGER.error("Query data version from name server {}  Exception, {}", namesrvAddr, e);
                         } finally {
                             countDownLatch.countDown();
                         }
@@ -337,7 +610,7 @@ public class BrokerOuterAPI {
             try {
                 countDownLatch.await(timeoutMills, TimeUnit.MILLISECONDS);
             } catch (InterruptedException e) {
-                log.error("query dataversion from nameserver countDownLatch await Exception", e);
+                LOGGER.error("query dataversion from nameserver countDownLatch await Exception", e);
             }
         }
         return changedList;
@@ -416,6 +689,220 @@ public class BrokerOuterAPI {
         remotingClient.registerRPCHook(rpcHook);
     }
 
+    public void clearRPCHook() {
+        remotingClient.clearRPCHook();
+    }
+
+    public long getMaxOffset(final String addr, final String topic, final int queueId, final boolean committed,
+        final boolean isOnlyThisBroker)
+        throws RemotingException, MQBrokerException, InterruptedException {
+        GetMaxOffsetRequestHeader requestHeader = new GetMaxOffsetRequestHeader();
+        requestHeader.setTopic(topic);
+        requestHeader.setQueueId(queueId);
+        requestHeader.setCommitted(committed);
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_MAX_OFFSET, requestHeader);
+
+        RemotingCommand response = this.remotingClient.invokeSync(addr, request, 3000);
+        assert response != null;
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                GetMaxOffsetResponseHeader responseHeader = response.decodeCommandCustomHeader(GetMaxOffsetResponseHeader.class);
+
+                return responseHeader.getOffset();
+            }
+            default:
+                break;
+        }
+
+        throw new MQBrokerException(response.getCode(), response.getRemark());
+    }
+
+    public long getMinOffset(final String addr, final String topic, final int queueId, final boolean isOnlyThisBroker)
+        throws RemotingException, MQBrokerException, InterruptedException {
+        GetMinOffsetRequestHeader requestHeader = new GetMinOffsetRequestHeader();
+        requestHeader.setTopic(topic);
+        requestHeader.setQueueId(queueId);
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_MIN_OFFSET, requestHeader);
+
+        RemotingCommand response = this.remotingClient.invokeSync(addr, request, 3000);
+        assert response != null;
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS: {
+                GetMinOffsetResponseHeader responseHeader = response.decodeCommandCustomHeader(GetMinOffsetResponseHeader.class);
+
+                return responseHeader.getOffset();
+            }
+            default:
+                break;
+        }
+
+        throw new MQBrokerException(response.getCode(), response.getRemark());
+    }
+
+    public void lockBatchMQAsync(
+        final String addr,
+        final LockBatchRequestBody requestBody,
+        final long timeoutMillis,
+        final LockCallback callback) throws RemotingException, InterruptedException {
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.LOCK_BATCH_MQ, null);
+
+        request.setBody(requestBody.encode());
+        this.remotingClient.invokeAsync(addr, request, timeoutMillis, responseFuture -> {
+            if (callback == null) {
+                return;
+            }
+
+            try {
+                RemotingCommand response = responseFuture.getResponseCommand();
+                if (response != null) {
+                    if (response.getCode() == ResponseCode.SUCCESS) {
+                        LockBatchResponseBody responseBody = LockBatchResponseBody.decode(response.getBody(),
+                            LockBatchResponseBody.class);
+                        Set<MessageQueue> messageQueues = responseBody.getLockOKMQSet();
+                        callback.onSuccess(messageQueues);
+                    } else {
+                        callback.onException(new MQBrokerException(response.getCode(), response.getRemark()));
+                    }
+                }
+            } catch (Throwable ignored) {
+
+            }
+        });
+    }
+
+    public void unlockBatchMQAsync(
+        final String addr,
+        final UnlockBatchRequestBody requestBody,
+        final long timeoutMillis,
+        final UnlockCallback callback) throws RemotingException, InterruptedException {
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.UNLOCK_BATCH_MQ, null);
+
+        request.setBody(requestBody.encode());
+
+        this.remotingClient.invokeAsync(addr, request, timeoutMillis, responseFuture -> {
+            if (callback == null) {
+                return;
+            }
+
+            try {
+                RemotingCommand response = responseFuture.getResponseCommand();
+                if (response != null) {
+                    if (response.getCode() == ResponseCode.SUCCESS) {
+                        callback.onSuccess();
+                    } else {
+                        callback.onException(new MQBrokerException(response.getCode(), response.getRemark()));
+                    }
+                }
+            } catch (Throwable ignored) {
+
+            }
+        });
+    }
+
+    public RemotingClient getRemotingClient() {
+        return this.remotingClient;
+    }
+
+    public SendResult sendMessageToSpecificBroker(String brokerAddr, final String brokerName,
+        final MessageExt msg, String group,
+        long timeoutMillis) throws RemotingException, MQBrokerException, InterruptedException {
+
+        SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
+        requestHeader.setProducerGroup(group);
+        requestHeader.setTopic(msg.getTopic());
+        requestHeader.setDefaultTopic(TopicValidator.AUTO_CREATE_TOPIC_KEY_TOPIC);
+        requestHeader.setDefaultTopicQueueNums(8);
+        requestHeader.setQueueId(msg.getQueueId());
+        requestHeader.setSysFlag(msg.getSysFlag());
+        requestHeader.setBornTimestamp(msg.getBornTimestamp());
+        requestHeader.setFlag(msg.getFlag());
+        requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
+        requestHeader.setReconsumeTimes(msg.getReconsumeTimes());
+        requestHeader.setBatch(false);
+
+        SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
+        RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE_V2, requestHeaderV2);
+
+        request.setBody(msg.getBody());
+
+        RemotingCommand response = this.remotingClient.invokeSync(brokerAddr, request, timeoutMillis);
+
+        return this.processSendResponse(brokerName, msg, response);
+    }
+
+    private SendResult processSendResponse(
+        final String brokerName,
+        final Message msg,
+        final RemotingCommand response
+    ) throws MQBrokerException, RemotingCommandException {
+        switch (response.getCode()) {
+            case ResponseCode.FLUSH_DISK_TIMEOUT:
+            case ResponseCode.FLUSH_SLAVE_TIMEOUT:
+            case ResponseCode.SLAVE_NOT_AVAILABLE:
+            case ResponseCode.SUCCESS: {
+                SendStatus sendStatus = SendStatus.SEND_OK;
+                switch (response.getCode()) {
+                    case ResponseCode.FLUSH_DISK_TIMEOUT:
+                        sendStatus = SendStatus.FLUSH_DISK_TIMEOUT;
+                        break;
+                    case ResponseCode.FLUSH_SLAVE_TIMEOUT:
+                        sendStatus = SendStatus.FLUSH_SLAVE_TIMEOUT;
+                        break;
+                    case ResponseCode.SLAVE_NOT_AVAILABLE:
+                        sendStatus = SendStatus.SLAVE_NOT_AVAILABLE;
+                        break;
+                    case ResponseCode.SUCCESS:
+                        sendStatus = SendStatus.SEND_OK;
+                        break;
+                    default:
+                        assert false;
+                        break;
+                }
+
+                SendMessageResponseHeader responseHeader =
+                    (SendMessageResponseHeader) response.decodeCommandCustomHeader(SendMessageResponseHeader.class);
+
+                //If namespace not null , reset Topic without namespace.
+                String topic = msg.getTopic();
+
+                MessageQueue messageQueue = new MessageQueue(topic, brokerName, responseHeader.getQueueId());
+
+                String uniqMsgId = MessageClientIDSetter.getUniqID(msg);
+                if (msg instanceof MessageBatch) {
+                    StringBuilder sb = new StringBuilder();
+                    for (Message message : (MessageBatch) msg) {
+                        sb.append(sb.length() == 0 ? "" : ",").append(MessageClientIDSetter.getUniqID(message));
+                    }
+                    uniqMsgId = sb.toString();
+                }
+                SendResult sendResult = new SendResult(sendStatus,
+                    uniqMsgId,
+                    responseHeader.getMsgId(), messageQueue, responseHeader.getQueueOffset());
+                sendResult.setTransactionId(responseHeader.getTransactionId());
+                String regionId = response.getExtFields().get(MessageConst.PROPERTY_MSG_REGION);
+                String traceOn = response.getExtFields().get(MessageConst.PROPERTY_TRACE_SWITCH);
+                if (regionId == null || regionId.isEmpty()) {
+                    regionId = MixAll.DEFAULT_TRACE_REGION_ID;
+                }
+                if (traceOn != null && traceOn.equals("false")) {
+                    sendResult.setTraceOn(false);
+                } else {
+                    sendResult.setTraceOn(true);
+                }
+                sendResult.setRegionId(regionId);
+                return sendResult;
+            }
+            default:
+                break;
+        }
+
+        throw new MQBrokerException(response.getCode(), response.getRemark());
+    }
+
+    public BrokerFixedThreadPoolExecutor getBrokerOuterExecutor() {
+        return brokerOuterExecutor;
+    }
+
     public TopicRouteData getTopicRouteInfoFromNameServer(final String topic, final long timeoutMillis)
         throws RemotingException, MQBrokerException, InterruptedException {
         return getTopicRouteInfoFromNameServer(topic, timeoutMillis, true);
@@ -433,7 +920,7 @@ public class BrokerOuterAPI {
         switch (response.getCode()) {
             case ResponseCode.TOPIC_NOT_EXIST: {
                 if (allowTopicNotExist) {
-                    log.warn("get Topic [{}] RouteInfoFromNameServer is not exist value", topic);
+                    LOGGER.warn("get Topic [{}] RouteInfoFromNameServer is not exist value", topic);
                 }
 
                 break;
@@ -466,17 +953,16 @@ public class BrokerOuterAPI {
         throw new MQBrokerException(response.getCode(), response.getRemark());
     }
 
-    public void forwardRequest(String brokerAddr, RemotingCommand request, long timeoutMillis, InvokeCallback invokeCallback) throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException, RemotingTooMuchRequestException, RemotingConnectException {
+    public void forwardRequest(String brokerAddr, RemotingCommand request, long timeoutMillis,
+        InvokeCallback invokeCallback) throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException, RemotingTooMuchRequestException, RemotingConnectException {
         this.remotingClient.invokeAsync(brokerAddr, request, timeoutMillis, invokeCallback);
     }
 
-
     public void refreshMetadata() throws Exception {
         ClusterInfo brokerClusterInfo = getBrokerClusterInfo();
         clientMetadata.refreshClusterInfo(brokerClusterInfo);
     }
 
-
     public ClientMetadata getClientMetadata() {
         return clientMetadata;
     }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
index 558f091..952cf4f 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/pagecache/OneMessageTransfer.java
@@ -70,6 +70,7 @@ public class OneMessageTransfer extends AbstractReferenceCounted implements File
         return 0;
     }
 
+
     @Override
     public FileRegion retain() {
         super.retain();
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
index 9aa1d9b..4254221 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/AbstractPluginMessageStore.java
@@ -17,22 +17,43 @@
 
 package org.apache.rocketmq.broker.plugin;
 
+import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.Optional;
 import java.util.Set;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import org.apache.rocketmq.common.SystemClock;
+import org.apache.rocketmq.common.TopicConfig;
 import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageExtBatch;
+import org.apache.rocketmq.common.protocol.body.HARuntimeInfo;
+import org.apache.rocketmq.store.AllocateMappedFileService;
+import org.apache.rocketmq.store.AppendMessageResult;
+import org.apache.rocketmq.store.CommitLog;
 import org.apache.rocketmq.store.CommitLogDispatcher;
+import org.apache.rocketmq.store.DispatchRequest;
 import org.apache.rocketmq.store.GetMessageResult;
-import org.apache.rocketmq.store.MessageExtBatch;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.MessageFilter;
 import org.apache.rocketmq.store.MessageStore;
 import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.QueryMessageResult;
+import org.apache.rocketmq.store.RunningFlags;
 import org.apache.rocketmq.store.SelectMappedBufferResult;
+import org.apache.rocketmq.store.StoreCheckpoint;
+import org.apache.rocketmq.store.StoreStatsService;
+import org.apache.rocketmq.store.TransientStorePool;
+import org.apache.rocketmq.store.config.MessageStoreConfig;
+import org.apache.rocketmq.store.ha.HAService;
+import org.apache.rocketmq.store.hook.PutMessageHook;
+import org.apache.rocketmq.store.hook.SendMessageBackHook;
+import org.apache.rocketmq.store.logfile.MappedFile;
 import org.apache.rocketmq.store.queue.ConsumeQueueInterface;
+import org.apache.rocketmq.store.queue.ConsumeQueueStore;
 import org.apache.rocketmq.store.stats.BrokerStatsManager;
+import org.apache.rocketmq.store.util.PerfCounter;
 
 public abstract class AbstractPluginMessageStore implements MessageStore {
     protected MessageStore next = null;
@@ -201,16 +222,6 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
     }
 
     @Override
-    public void updateHaMasterAddress(String newAddr) {
-        next.updateHaMasterAddress(newAddr);
-    }
-
-    @Override
-    public long slaveFallBehindMuch() {
-        return next.slaveFallBehindMuch();
-    }
-
-    @Override
     public long now() {
         return next.now();
     }
@@ -269,4 +280,255 @@ public abstract class AbstractPluginMessageStore implements MessageStore {
     public BrokerStatsManager getBrokerStatsManager() {
         return next.getBrokerStatsManager();
     }
+
+    @Override
+    public int remainTransientStoreBufferNumbs() {
+        return next.remainTransientStoreBufferNumbs();
+    }
+
+    @Override
+    public long remainHowManyDataToCommit() {
+        return next.remainHowManyDataToCommit();
+    }
+
+    @Override
+    public long remainHowManyDataToFlush() {
+        return next.remainHowManyDataToFlush();
+    }
+
+    @Override
+    public DispatchRequest checkMessageAndReturnSize(final ByteBuffer byteBuffer, final boolean checkCRC,
+        final boolean checkDupInfo, final boolean readBody) {
+        return next.checkMessageAndReturnSize(byteBuffer, checkCRC, checkDupInfo, readBody);
+    }
+
+    @Override
+    public long getStateMachineVersion() {
+        return next.getStateMachineVersion();
+    }
+
+    @Override public PutMessageResult putMessages(MessageExtBatch messageExtBatch) {
+        return next.putMessages(messageExtBatch);
+    }
+
+    @Override public HARuntimeInfo getHARuntimeInfo() {
+        return next.getHARuntimeInfo();
+    }
+
+    @Override public boolean getLastMappedFile(long startOffset) {
+        return next.getLastMappedFile(startOffset);
+    }
+
+    @Override public void updateHaMasterAddress(String newAddr) {
+        next.updateHaMasterAddress(newAddr);
+    }
+
+    @Override public void updateMasterAddress(String newAddr) {
+        next.updateMasterAddress(newAddr);
+    }
+
+    @Override public long slaveFallBehindMuch() {
+        return next.slaveFallBehindMuch();
+    }
+
+    @Override public long getFlushedWhere() {
+        return next.getFlushedWhere();
+    }
+
+    @Override public MessageStore getMasterStoreInProcess() {
+        return next.getMasterStoreInProcess();
+    }
+
+    @Override public void setMasterStoreInProcess(MessageStore masterStoreInProcess) {
+        next.setMasterStoreInProcess(masterStoreInProcess);
+    }
+
+    @Override
+    public boolean getData(long offset, int size, ByteBuffer byteBuffer) {
+        return next.getData(offset, size, byteBuffer);
+    }
+
+    @Override public void setAliveReplicaNumInGroup(int aliveReplicaNums) {
+        next.setAliveReplicaNumInGroup(aliveReplicaNums);
+    }
+
+    @Override public int getAliveReplicaNumInGroup() {
+        return next.getAliveReplicaNumInGroup();
+    }
+
+    @Override public void wakeupHAClient() {
+        next.wakeupHAClient();
+    }
+
+    @Override public long getMasterFlushedOffset() {
+        return next.getMasterFlushedOffset();
+    }
+
+    @Override public long getBrokerInitMaxOffset() {
+        return next.getBrokerInitMaxOffset();
+    }
+
+    @Override public void setMasterFlushedOffset(long masterFlushedOffset) {
+        next.setMasterFlushedOffset(masterFlushedOffset);
+    }
+
+    @Override public void setBrokerInitMaxOffset(long brokerInitMaxOffset) {
+        next.setBrokerInitMaxOffset(brokerInitMaxOffset);
+    }
+
+    @Override public byte[] calcDeltaChecksum(long from, long to) {
+        return next.calcDeltaChecksum(from, to);
+    }
+
+    @Override public HAService getHaService() {
+        return next.getHaService();
+    }
+
+    @Override public boolean truncateFiles(long offsetToTruncate) {
+        return next.truncateFiles(offsetToTruncate);
+    }
+
+    @Override public boolean isOffsetAligned(long offset) {
+        return next.isOffsetAligned(offset);
+    }
+
+    @Override public RunningFlags getRunningFlags() {
+        return next.getRunningFlags();
+    }
+
+    @Override public void setSendMessageBackHook(SendMessageBackHook sendMessageBackHook) {
+        next.setSendMessageBackHook(sendMessageBackHook);
+    }
+
+    @Override public SendMessageBackHook getSendMessageBackHook() {
+        return next.getSendMessageBackHook();
+    }
+
+    @Override
+    public GetMessageResult getMessage(String group, String topic, int queueId, long offset,
+        int maxMsgNums, int maxTotalMsgSize, MessageFilter messageFilter) {
+        return next.getMessage(group, topic, queueId, offset, maxMsgNums, maxTotalMsgSize, messageFilter);
+    }
+
+    @Override public MessageExt lookMessageByOffset(long commitLogOffset, int size) {
+        return next.lookMessageByOffset(commitLogOffset, size);
+    }
+
+    @Override public List<SelectMappedBufferResult> getBulkCommitLogData(long offset, int size) {
+        return next.getBulkCommitLogData(offset, size);
+    }
+
+    @Override
+    public void onCommitLogAppend(MessageExtBrokerInner msg, AppendMessageResult result, MappedFile commitLogFile) {
+        next.onCommitLogAppend(msg, result, commitLogFile);
+    }
+
+    @Override
+    public void onCommitLogDispatch(DispatchRequest dispatchRequest, boolean doDispatch, MappedFile commitLogFile,
+        boolean isRecover, boolean isFileEnd) {
+        next.onCommitLogDispatch(dispatchRequest, doDispatch, commitLogFile, isRecover, isFileEnd);
+    }
+
+    @Override
+    public MessageStoreConfig getMessageStoreConfig() {
+        return next.getMessageStoreConfig();
+    }
+
+    @Override
+    public StoreStatsService getStoreStatsService() {
+        return next.getStoreStatsService();
+    }
+
+    @Override
+    public StoreCheckpoint getStoreCheckpoint() {
+        return next.getStoreCheckpoint();
+    }
+
+    @Override
+    public SystemClock getSystemClock() {
+        return next.getSystemClock();
+    }
+
+    @Override
+    public CommitLog getCommitLog() {
+        return next.getCommitLog();
+    }
+
+    @Override
+    public TransientStorePool getTransientStorePool() {
+        return next.getTransientStorePool();
+    }
+
+    @Override
+    public AllocateMappedFileService getAllocateMappedFileService() {
+        return next.getAllocateMappedFileService();
+    }
+
+    @Override
+    public void truncateDirtyLogicFiles(long phyOffset) {
+        next.truncateDirtyLogicFiles(phyOffset);
+    }
+
+    @Override
+    public void destroyLogics() {
+        next.destroyLogics();
+    }
+
+    @Override
+    public void unlockMappedFile(MappedFile unlockMappedFile) {
+        next.unlockMappedFile(unlockMappedFile);
+    }
+
+    @Override
+    public PerfCounter.Ticks getPerfCounter() {
+        return next.getPerfCounter();
+    }
+
+    @Override
+    public ConsumeQueueStore getQueueStore() {
+        return next.getQueueStore();
+    }
+
+    @Override
+    public boolean isSyncDiskFlush() {
+        return next.isSyncDiskFlush();
+    }
+
+    @Override
+    public boolean isSyncMaster() {
+        return next.isSyncMaster();
+    }
+
+    @Override
+    public void assignOffset(MessageExtBrokerInner msg, short messageNum) {
+        next.assignOffset(msg, messageNum);
+    }
+
+    @Override
+    public Optional<TopicConfig> getTopicConfig(String topic) {
+        return next.getTopicConfig(topic);
+    }
+
+    @Override
+    public List<PutMessageHook> getPutMessageHookList() {
+        return next.getPutMessageHookList();
+    }
+
+    @Override
+    public long getLastFileFromOffset() {
+        return next.getLastFileFromOffset();
+    }
+
+    @Override
+    public void setPhysicalOffset(long phyOffset) {
+        next.setPhysicalOffset(phyOffset);
+    }
+
+    @Override public boolean isMappedFilesEmpty() {
+        return next.isMappedFilesEmpty();
+    }
+
+    @Override public boolean isShutdown() {
+        return next.isShutdown();
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/BrokerAttachedPlugin.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/BrokerAttachedPlugin.java
new file mode 100644
index 0000000..0cd2a27
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/BrokerAttachedPlugin.java
@@ -0,0 +1,74 @@
+/*
+ * 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.broker.plugin;
+
+import java.util.Map;
+
+public interface BrokerAttachedPlugin {
+
+    /**
+     * Get plugin name
+     *
+     * @return plugin name
+     */
+    String pluginName();
+
+    /**
+     * Load broker attached plugin.
+     *
+     * @return load success or failed
+     */
+    boolean load();
+
+    /**
+     * Start broker attached plugin.
+     */
+    void start();
+
+    /**
+     * Shutdown broker attached plugin.
+     */
+    void shutdown();
+
+    /**
+     * Sync metadata from master.
+     */
+    void syncMetadata();
+
+    /**
+     * Sync metadata reverse from slave
+     *
+     * @param brokerAddr
+     */
+    void syncMetadataReverse(String brokerAddr) throws Exception;
+
+    /**
+     * Some plugin need build runningInfo when prepare runtime info.
+     *
+     * @param runtimeInfo
+     */
+    void buildRuntimeInfo(Map<String, String> runtimeInfo);
+
+    /**
+     * Some plugin need do something when status changed. For example, brokerRole change to master or slave.
+     *
+     * @param shouldStart
+     */
+    void statusChanged(boolean shouldStart);
+
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStoreFactory.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStoreFactory.java
index 8db538b..b64ab5a 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStoreFactory.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStoreFactory.java
@@ -22,8 +22,8 @@ import java.lang.reflect.Constructor;
 import org.apache.rocketmq.store.MessageStore;
 
 public final class MessageStoreFactory {
-    public final static MessageStore build(MessageStorePluginContext context, MessageStore messageStore)
-        throws IOException {
+    public final static MessageStore build(MessageStorePluginContext context,
+        MessageStore messageStore) throws IOException {
         String plugin = context.getBrokerConfig().getMessageStorePlugIn();
         if (plugin != null && plugin.trim().length() != 0) {
             String[] pluginClasses = plugin.split(",");
@@ -31,12 +31,13 @@ public final class MessageStoreFactory {
                 String pluginClass = pluginClasses[i];
                 try {
                     @SuppressWarnings("unchecked")
-                    Class<AbstractPluginMessageStore> clazz = (Class<AbstractPluginMessageStore>) Class.forName(pluginClass);
+                    Class<AbstractPluginMessageStore> clazz = (Class<AbstractPluginMessageStore>)Class.forName(pluginClass);
                     Constructor<AbstractPluginMessageStore> construct = clazz.getConstructor(MessageStorePluginContext.class, MessageStore.class);
-                    messageStore = construct.newInstance(context, messageStore);
-                } catch (Throwable e) {
-                    throw new RuntimeException(String.format(
-                        "Initialize plugin's class %s not found!", pluginClass), e);
+                    AbstractPluginMessageStore pluginMessageStore = construct.newInstance(context, messageStore);
+                    messageStore = pluginMessageStore;
+                }
+                catch (Throwable e) {
+                    throw new RuntimeException("Initialize plugin's class: " + pluginClass + " not found!", e);
                 }
             }
         }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStorePluginContext.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStorePluginContext.java
index b822a2f..c132cf9 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStorePluginContext.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/MessageStorePluginContext.java
@@ -17,25 +17,25 @@
 
 package org.apache.rocketmq.broker.plugin;
 
+import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.common.BrokerConfig;
 import org.apache.rocketmq.store.MessageArrivingListener;
 import org.apache.rocketmq.store.config.MessageStoreConfig;
 import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
 public class MessageStorePluginContext {
+    private BrokerController controller;
     private MessageStoreConfig messageStoreConfig;
     private BrokerStatsManager brokerStatsManager;
     private MessageArrivingListener messageArrivingListener;
-    private BrokerConfig brokerConfig;
 
-    public MessageStorePluginContext(MessageStoreConfig messageStoreConfig,
-        BrokerStatsManager brokerStatsManager, MessageArrivingListener messageArrivingListener,
-        BrokerConfig brokerConfig) {
+    public MessageStorePluginContext(BrokerController controller, MessageStoreConfig messageStoreConfig,
+        BrokerStatsManager brokerStatsManager, MessageArrivingListener messageArrivingListener) {
         super();
         this.messageStoreConfig = messageStoreConfig;
         this.brokerStatsManager = brokerStatsManager;
         this.messageArrivingListener = messageArrivingListener;
-        this.brokerConfig = brokerConfig;
+        this.controller = controller;
     }
 
     public MessageStoreConfig getMessageStoreConfig() {
@@ -51,7 +51,11 @@ public class MessageStorePluginContext {
     }
 
     public BrokerConfig getBrokerConfig() {
-        return brokerConfig;
+        return controller.getBrokerConfig();
+    }
+
+    public BrokerController getController() {
+        return controller;
     }
 
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/plugin/PullMessageResultHandler.java b/broker/src/main/java/org/apache/rocketmq/broker/plugin/PullMessageResultHandler.java
new file mode 100644
index 0000000..3383a64
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/plugin/PullMessageResultHandler.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.broker.plugin;
+
+import io.netty.channel.Channel;
+import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.store.MessageFilter;
+
+public interface PullMessageResultHandler {
+
+    /**
+     * Handle result of get message from store.
+     *
+     * @param getMessageResult store result
+     * @param request request
+     * @param requestHeader request header
+     * @param channel channel
+     * @param subscriptionData sub data
+     * @param subscriptionGroupConfig sub config
+     * @param brokerAllowSuspend brokerAllowSuspend
+     * @param messageFilter store message filter
+     * @param response response
+     * @return response or null
+     */
+    RemotingCommand handle(final GetMessageResult getMessageResult,
+                           final RemotingCommand request,
+                           final PullMessageRequestHeader requestHeader,
+                           final Channel channel,
+                           final SubscriptionData subscriptionData,
+                           final SubscriptionGroupConfig subscriptionGroupConfig,
+                           final boolean brokerAllowSuspend,
+                           final MessageFilter messageFilter,
+                           final RemotingCommand response);
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
index 3303d70..1821239 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AbstractSendMessageProcessor.java
@@ -17,16 +17,26 @@
 package org.apache.rocketmq.broker.processor;
 
 import io.netty.channel.ChannelHandlerContext;
-import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.concurrent.ThreadLocalRandom;
 
 import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.mqtrace.AbortProcessException;
+import org.apache.rocketmq.broker.mqtrace.ConsumeMessageContext;
+import org.apache.rocketmq.broker.mqtrace.ConsumeMessageHook;
 import org.apache.rocketmq.broker.mqtrace.SendMessageContext;
 import org.apache.rocketmq.broker.mqtrace.SendMessageHook;
+import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.MQVersion;
+import org.apache.rocketmq.common.UtilAll;
+import org.apache.rocketmq.common.message.MessageExt;
+import org.apache.rocketmq.common.message.MessageType;
+import org.apache.rocketmq.common.protocol.header.ConsumerSendMsgBackRequestHeader;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
 import org.apache.rocketmq.common.topic.TopicValidator;
 import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.TopicConfig;
@@ -46,46 +56,315 @@ import org.apache.rocketmq.common.protocol.header.SendMessageRequestHeaderV2;
 import org.apache.rocketmq.common.protocol.header.SendMessageResponseHeader;
 import org.apache.rocketmq.common.sysflag.MessageSysFlag;
 import org.apache.rocketmq.common.sysflag.TopicSysFlag;
-import org.apache.rocketmq.common.utils.ChannelUtil;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 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.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
+import org.apache.rocketmq.store.PutMessageResult;
+import org.apache.rocketmq.store.stats.BrokerStatsManager;
 
-public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
-    protected static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+public abstract class AbstractSendMessageProcessor implements NettyRequestProcessor {
+    protected static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    protected static final InternalLogger DLQ_LOG = InternalLoggerFactory.getLogger(LoggerName.DLQ_LOGGER_NAME);
+
+    protected List<ConsumeMessageHook> consumeMessageHookList;
 
     protected final static int DLQ_NUMS_PER_GROUP = 1;
-    protected final BrokerController brokerController;
+    protected final Random random = new Random(System.currentTimeMillis());
     protected final SocketAddress storeHost;
     private List<SendMessageHook> sendMessageHookList;
+    protected final BrokerController brokerController;
+
 
     public AbstractSendMessageProcessor(final BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.storeHost =
-            new InetSocketAddress(brokerController.getBrokerConfig().getBrokerIP1(), brokerController
-                .getNettyServerConfig().getListenPort());
+        this.storeHost = brokerController.getStoreHost();
+    }
+
+    public void registerConsumeMessageHook(List<ConsumeMessageHook> consumeMessageHookList) {
+        this.consumeMessageHookList = consumeMessageHookList;
+    }
+
+    protected RemotingCommand consumerSendMsgBack(final ChannelHandlerContext ctx, final RemotingCommand request)
+        throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        final ConsumerSendMsgBackRequestHeader requestHeader =
+            (ConsumerSendMsgBackRequestHeader) request.decodeCommandCustomHeader(ConsumerSendMsgBackRequestHeader.class);
+
+        // The send back requests sent to SlaveBroker will be forwarded to the master broker beside
+        final BrokerController masterBroker = this.brokerController.peekMasterBroker();
+        if (null == masterBroker) {
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("no master available along with " + brokerController.getBrokerConfig().getBrokerIP1());
+            return response;
+        }
+
+        // The broker that received the request.
+        // It may be a master broker or a slave broker
+        final BrokerController currentBroker = this.brokerController;
+
+        SubscriptionGroupConfig subscriptionGroupConfig =
+            masterBroker.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getGroup());
+        if (null == subscriptionGroupConfig) {
+            response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+            response.setRemark("subscription group not exist, " + requestHeader.getGroup() + " "
+                + FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST));
+            return response;
+        }
+
+        BrokerConfig masterBrokerConfig = masterBroker.getBrokerConfig();
+        if (!PermName.isWriteable(masterBrokerConfig.getBrokerPermission())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("the broker[" + masterBrokerConfig.getBrokerIP1() + "] sending message is forbidden");
+            return response;
+        }
+
+        if (subscriptionGroupConfig.getRetryQueueNums() <= 0) {
+            response.setCode(ResponseCode.SUCCESS);
+            response.setRemark(null);
+            return response;
+        }
+
+        String newTopic = MixAll.getRetryTopic(requestHeader.getGroup());
+        int queueIdInt = Math.abs(this.random.nextInt() % 99999999) % subscriptionGroupConfig.getRetryQueueNums();
+
+        int topicSysFlag = 0;
+        if (requestHeader.isUnitMode()) {
+            topicSysFlag = TopicSysFlag.buildSysFlag(false, true);
+        }
+
+        // Create retry topic to master broker
+        TopicConfig topicConfig = masterBroker.getTopicConfigManager().createTopicInSendMessageBackMethod(
+            newTopic,
+            subscriptionGroupConfig.getRetryQueueNums(),
+            PermName.PERM_WRITE | PermName.PERM_READ, topicSysFlag);
+        if (null == topicConfig) {
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("topic[" + newTopic + "] not exist");
+            return response;
+        }
+
+        if (!PermName.isWriteable(topicConfig.getPerm())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark(String.format("the topic[%s] sending message is forbidden", newTopic));
+            return response;
+        }
+
+        // Look message from the origin message store
+        MessageExt msgExt = currentBroker.getMessageStore().lookMessageByOffset(requestHeader.getOffset());
+        if (null == msgExt) {
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("look message by offset failed, " + requestHeader.getOffset());
+            return response;
+        }
+
+        //for logic queue
+        if (requestHeader.getOriginTopic() != null
+            && !msgExt.getTopic().equals(requestHeader.getOriginTopic())) {
+            //here just do some fence in case of some unexpected offset is income
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("look message by offset failed to check the topic name" + requestHeader.getOffset());
+            return response;
+        }
+
+        final String retryTopic = msgExt.getProperty(MessageConst.PROPERTY_RETRY_TOPIC);
+        if (null == retryTopic) {
+            MessageAccessor.putProperty(msgExt, MessageConst.PROPERTY_RETRY_TOPIC, msgExt.getTopic());
+        }
+        msgExt.setWaitStoreMsgOK(false);
+
+        int delayLevel = requestHeader.getDelayLevel();
+
+        int maxReconsumeTimes = subscriptionGroupConfig.getRetryMaxTimes();
+        if (request.getVersion() >= MQVersion.Version.V3_4_9.ordinal()) {
+            Integer times = requestHeader.getMaxReconsumeTimes();
+            if (times != null) {
+                maxReconsumeTimes = times;
+            }
+        }
+
+        boolean isDLQ = false;
+        if (msgExt.getReconsumeTimes() >= maxReconsumeTimes
+            || delayLevel < 0) {
+
+            isDLQ = true;
+            newTopic = MixAll.getDLQTopic(requestHeader.getGroup());
+            queueIdInt = randomQueueId(DLQ_NUMS_PER_GROUP);
+
+            // Create DLQ topic to master broker
+            topicConfig = masterBroker.getTopicConfigManager().createTopicInSendMessageBackMethod(newTopic,
+                DLQ_NUMS_PER_GROUP,
+                PermName.PERM_WRITE | PermName.PERM_READ, 0);
+
+            if (null == topicConfig) {
+                response.setCode(ResponseCode.SYSTEM_ERROR);
+                response.setRemark("topic[" + newTopic + "] not exist");
+                return response;
+            }
+            msgExt.setDelayTimeLevel(0);
+        } else {
+            if (0 == delayLevel) {
+                delayLevel = 3 + msgExt.getReconsumeTimes();
+            }
+
+            msgExt.setDelayTimeLevel(delayLevel);
+        }
+
+        MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
+        msgInner.setTopic(newTopic);
+        msgInner.setBody(msgExt.getBody());
+        msgInner.setFlag(msgExt.getFlag());
+        MessageAccessor.setProperties(msgInner, msgExt.getProperties());
+        msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgExt.getProperties()));
+        msgInner.setTagsCode(MessageExtBrokerInner.tagsString2tagsCode(null, msgExt.getTags()));
+
+        msgInner.setQueueId(queueIdInt);
+        msgInner.setSysFlag(msgExt.getSysFlag());
+        msgInner.setBornTimestamp(msgExt.getBornTimestamp());
+        msgInner.setBornHost(msgExt.getBornHost());
+        msgInner.setStoreHost(this.getStoreHost());
+        msgInner.setReconsumeTimes(msgExt.getReconsumeTimes() + 1);
+
+        String originMsgId = MessageAccessor.getOriginMessageId(msgExt);
+        MessageAccessor.setOriginMessageId(msgInner, UtilAll.isBlank(originMsgId) ? msgExt.getMsgId() : originMsgId);
+        msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgExt.getProperties()));
+
+        boolean succeeded = false;
+
+        // Put retry topic to master message store
+        PutMessageResult putMessageResult = masterBroker.getMessageStore().putMessage(msgInner);
+        if (putMessageResult != null) {
+            String commercialOwner = request.getExtFields().get(BrokerStatsManager.COMMERCIAL_OWNER);
+
+            switch (putMessageResult.getPutMessageStatus()) {
+                case PUT_OK:
+                    String backTopic = msgExt.getTopic();
+                    String correctTopic = msgExt.getProperty(MessageConst.PROPERTY_RETRY_TOPIC);
+                    if (correctTopic != null) {
+                        backTopic = correctTopic;
+                    }
+                    if (TopicValidator.RMQ_SYS_SCHEDULE_TOPIC.equals(msgInner.getTopic())) {
+                        masterBroker.getBrokerStatsManager().incTopicPutNums(msgInner.getTopic());
+                        masterBroker.getBrokerStatsManager().incTopicPutSize(msgInner.getTopic(), putMessageResult.getAppendMessageResult().getWroteBytes());
+                        masterBroker.getBrokerStatsManager().incQueuePutNums(msgInner.getTopic(), msgInner.getQueueId());
+                        masterBroker.getBrokerStatsManager().incQueuePutSize(msgInner.getTopic(), msgInner.getQueueId(), putMessageResult.getAppendMessageResult().getWroteBytes());
+                    }
+                    masterBroker.getBrokerStatsManager().incSendBackNums(requestHeader.getGroup(), backTopic);
+
+                    if (isDLQ) {
+                        masterBroker.getBrokerStatsManager().incDLQStatValue(
+                            BrokerStatsManager.SNDBCK2DLQ_TIMES,
+                            commercialOwner,
+                            requestHeader.getGroup(),
+                            requestHeader.getOriginTopic(),
+                            BrokerStatsManager.StatsType.SEND_BACK_TO_DLQ.name(),
+                            1);
+
+                        String uniqKey = msgInner.getProperties().get(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
+                        DLQ_LOG.info("send msg to DLQ {}, owner={}, originalTopic={}, consumerId={}, msgUniqKey={}, storeTimestamp={}",
+                            newTopic,
+                            commercialOwner,
+                            requestHeader.getOriginTopic(),
+                            requestHeader.getGroup(),
+                            uniqKey,
+                            putMessageResult.getAppendMessageResult().getStoreTimestamp());
+                    }
+
+                    response.setCode(ResponseCode.SUCCESS);
+                    response.setRemark(null);
+
+                    succeeded = true;
+                    break;
+                default:
+                    break;
+            }
+
+            if (!succeeded) {
+                response.setCode(ResponseCode.SYSTEM_ERROR);
+                response.setRemark(putMessageResult.getPutMessageStatus().name());
+            }
+        } else {
+            if (isDLQ) {
+                String owner = request.getExtFields().get(BrokerStatsManager.COMMERCIAL_OWNER);
+                String uniqKey = msgInner.getProperties().get(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
+                DLQ_LOG.info("failed to send msg to DLQ {}, owner={}, originalTopic={}, consumerId={}, msgUniqKey={}, result={}",
+                    newTopic,
+                    owner,
+                    requestHeader.getOriginTopic(),
+                    requestHeader.getGroup(),
+                    uniqKey,
+                    putMessageResult == null ? "null" : putMessageResult.getPutMessageStatus().toString());
+            }
+
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("putMessageResult is null");
+        }
+
+        if (this.hasConsumeMessageHook() && !UtilAll.isBlank(requestHeader.getOriginMsgId())) {
+            String namespace = NamespaceUtil.getNamespaceFromResource(requestHeader.getGroup());
+            ConsumeMessageContext context = new ConsumeMessageContext();
+            context.setNamespace(namespace);
+            context.setTopic(requestHeader.getOriginTopic());
+            context.setConsumerGroup(requestHeader.getGroup());
+            context.setCommercialRcvStats(BrokerStatsManager.StatsType.SEND_BACK);
+            context.setCommercialRcvTimes(1);
+            context.setCommercialOwner(request.getExtFields().get(BrokerStatsManager.COMMERCIAL_OWNER));
+
+            context.setAccountAuthType(request.getExtFields().get(BrokerStatsManager.ACCOUNT_AUTH_TYPE));
+            context.setAccountOwnerParent(request.getExtFields().get(BrokerStatsManager.ACCOUNT_OWNER_PARENT));
+            context.setAccountOwnerSelf(request.getExtFields().get(BrokerStatsManager.ACCOUNT_OWNER_SELF));
+            context.setRcvStat(isDLQ ? BrokerStatsManager.StatsType.SEND_BACK_TO_DLQ : BrokerStatsManager.StatsType.SEND_BACK);
+            context.setSuccess(succeeded);
+            context.setRcvMsgNum(1);
+            //Set msg body size 0 when sent back by consumer.
+            context.setRcvMsgSize(0);
+            context.setCommercialRcvMsgNum(succeeded ? 1 : 0);
+
+            try {
+                this.executeConsumeMessageHookAfter(context);
+            } catch (AbortProcessException e) {
+                response.setCode(e.getResponseCode());
+                response.setRemark(e.getErrorMessage());
+            }
+        }
+
+        return response;
+    }
+
+    public boolean hasConsumeMessageHook() {
+        return consumeMessageHookList != null && !this.consumeMessageHookList.isEmpty();
+    }
+
+    public void executeConsumeMessageHookAfter(final ConsumeMessageContext context) {
+        if (hasConsumeMessageHook()) {
+            for (ConsumeMessageHook hook : this.consumeMessageHookList) {
+                try {
+                    hook.consumeMessageAfter(context);
+                } catch (Throwable e) {
+                    // Ignore
+                }
+            }
+        }
     }
 
     protected SendMessageContext buildMsgContext(ChannelHandlerContext ctx,
         SendMessageRequestHeader requestHeader) {
-        if (!this.hasSendMessageHook()) {
-            return null;
-        }
         String namespace = NamespaceUtil.getNamespaceFromResource(requestHeader.getTopic());
-        SendMessageContext mqtraceContext = new SendMessageContext();
-        mqtraceContext.setProducerGroup(requestHeader.getProducerGroup());
-        mqtraceContext.setNamespace(namespace);
-        mqtraceContext.setTopic(requestHeader.getTopic());
-        mqtraceContext.setMsgProps(requestHeader.getProperties());
-        mqtraceContext.setBornHost(RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
-        mqtraceContext.setBrokerAddr(this.brokerController.getBrokerAddr());
-        mqtraceContext.setBrokerRegionId(this.brokerController.getBrokerConfig().getRegionId());
-        mqtraceContext.setBornTimeStamp(requestHeader.getBornTimestamp());
+
+        SendMessageContext traceContext;
+        traceContext = new SendMessageContext();
+        traceContext.setNamespace(namespace);
+        traceContext.setProducerGroup(requestHeader.getProducerGroup());
+        traceContext.setTopic(requestHeader.getTopic());
+        traceContext.setMsgProps(requestHeader.getProperties());
+        traceContext.setBornHost(RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        traceContext.setBrokerAddr(this.brokerController.getBrokerAddr());
+        traceContext.setBrokerRegionId(this.brokerController.getBrokerConfig().getRegionId());
+        traceContext.setBornTimeStamp(requestHeader.getBornTimestamp());
+        traceContext.setRequestTimeStamp(System.currentTimeMillis());
 
         Map<String, String> properties = MessageDecoder.string2messageProperties(requestHeader.getProperties());
         String uniqueKey = properties.get(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);
@@ -96,8 +375,14 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
         if (uniqueKey == null) {
             uniqueKey = "";
         }
-        mqtraceContext.setMsgUniqueKey(uniqueKey);
-        return mqtraceContext;
+        traceContext.setMsgUniqueKey(uniqueKey);
+
+        if (properties.containsKey(MessageConst.PROPERTY_SHARDING_KEY)) {
+            traceContext.setMsgType(MessageType.Order_Msg);
+        } else {
+            traceContext.setMsgType(MessageType.Normal_Msg);
+        }
+        return traceContext;
     }
 
     public boolean hasSendMessageHook() {
@@ -108,7 +393,7 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
         final SendMessageRequestHeader requestHeader, final byte[] body, TopicConfig topicConfig) {
         int queueIdInt = requestHeader.getQueueId();
         if (queueIdInt < 0) {
-            queueIdInt = ThreadLocalRandom.current().nextInt(99999999) % topicConfig.getWriteQueueNums();
+            queueIdInt = randomQueueId(topicConfig.getWriteQueueNums());
         }
         int sysFlag = requestHeader.getSysFlag();
 
@@ -143,19 +428,24 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
     protected RemotingCommand msgContentCheck(final ChannelHandlerContext ctx,
         final SendMessageRequestHeader requestHeader, RemotingCommand request,
         final RemotingCommand response) {
-        if (requestHeader.getTopic().length() > Byte.MAX_VALUE) {
-            log.warn("putMessage message topic length too long {}", requestHeader.getTopic().length());
+        String topic = requestHeader.getTopic();
+        if (topic.length() > Byte.MAX_VALUE) {
+            LOGGER.warn("msgContentCheck: message topic length is too long, topic={}, topic length={}, threshold={}",
+                topic, topic.length(), Byte.MAX_VALUE);
             response.setCode(ResponseCode.MESSAGE_ILLEGAL);
             return response;
         }
         if (requestHeader.getProperties() != null && requestHeader.getProperties().length() > Short.MAX_VALUE) {
-            log.warn("putMessage message properties length too long {}", requestHeader.getProperties().length());
+            LOGGER.warn(
+                "msgContentCheck: message properties length is too long, topic={}, properties length={}, threshold={}",
+                topic, requestHeader.getProperties().length(), Short.MAX_VALUE);
             response.setCode(ResponseCode.MESSAGE_ILLEGAL);
             return response;
         }
         if (request.getBody().length > DBMsgConstants.MAX_BODY_SIZE) {
-            log.warn(" topic {}  msg body size {}  from {}", requestHeader.getTopic(),
-                request.getBody().length, ChannelUtil.getRemoteIp(ctx.channel()));
+            LOGGER.warn(
+                "msgContentCheck: message body size exceeds the threshold, topic={}, body size={}, threshold={}bytes",
+                topic, request.getBody().length, DBMsgConstants.MAX_BODY_SIZE);
             response.setRemark("msg body must be less 64KB");
             response.setCode(ResponseCode.MESSAGE_ILLEGAL);
             return response;
@@ -164,7 +454,8 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
     }
 
     protected RemotingCommand msgCheck(final ChannelHandlerContext ctx,
-        final SendMessageRequestHeader requestHeader, final RemotingCommand response) {
+        final SendMessageRequestHeader requestHeader, final RemotingCommand request,
+        final RemotingCommand response) {
         if (!PermName.isWriteable(this.brokerController.getBrokerConfig().getBrokerPermission())
             && this.brokerController.getTopicConfigManager().isOrderTopic(requestHeader.getTopic())) {
             response.setCode(ResponseCode.NO_PERMISSION);
@@ -192,7 +483,7 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
                 }
             }
 
-            log.warn("the topic {} not exist, producer: {}", requestHeader.getTopic(), ctx.channel().remoteAddress());
+            LOGGER.warn("the topic {} not exist, producer: {}", requestHeader.getTopic(), ctx.channel().remoteAddress());
             topicConfig = this.brokerController.getTopicConfigManager().createTopicInSendMessageMethod(
                 requestHeader.getTopic(),
                 requestHeader.getDefaultTopic(),
@@ -224,7 +515,7 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
                 topicConfig.toString(),
                 RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
-            log.warn(errorInfo);
+            LOGGER.warn(errorInfo);
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark(errorInfo);
 
@@ -243,9 +534,10 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
             try {
                 ctx.writeAndFlush(response);
             } catch (Throwable e) {
-                log.error("SendMessageProcessor process request over, but response failed", e);
-                log.error(request.toString());
-                log.error(response.toString());
+                LOGGER.error(
+                    "SendMessageProcessor finished processing the request, but failed to send response, client "
+                        + "address={}, request={}, response={}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()),
+                    request.toString(), response.toString(), e);
             }
         }
     }
@@ -257,8 +549,8 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
                 try {
                     final SendMessageRequestHeader requestHeader = parseRequestHeader(request);
 
+                    String namespace = NamespaceUtil.getNamespaceFromResource(requestHeader.getTopic());
                     if (null != requestHeader) {
-                        String namespace = NamespaceUtil.getNamespaceFromResource(requestHeader.getTopic());
                         context.setNamespace(namespace);
                         context.setProducerGroup(requestHeader.getProducerGroup());
                         context.setTopic(requestHeader.getTopic());
@@ -273,8 +565,10 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
                     if (requestHeader != null) {
                         requestHeader.setProperties(context.getMsgProps());
                     }
+                } catch (AbortProcessException e) {
+                    throw e;
                 } catch (Throwable e) {
-                    // Ignore
+                    //ignore
                 }
             }
         }
@@ -304,7 +598,7 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
     }
 
     static SendMessageRequestHeaderV2 decodeSendMessageHeaderV2(RemotingCommand request)
-            throws RemotingCommandException {
+        throws RemotingCommandException {
         SendMessageRequestHeaderV2 r = new SendMessageRequestHeaderV2();
         HashMap<String, String> fields = request.getExtFields();
         if (fields == null) {
@@ -376,6 +670,10 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
         }
     }
 
+    protected int randomQueueId(int writeQueueNums) {
+        return ThreadLocalRandom.current().nextInt(99999999) % writeQueueNums;
+    }
+
     public void executeSendMessageHookAfter(final RemotingCommand response, final SendMessageContext context) {
         if (hasSendMessageHook()) {
             for (SendMessageHook hook : this.sendMessageHookList) {
@@ -391,7 +689,7 @@ public abstract class AbstractSendMessageProcessor extends AsyncNettyRequestProc
                     }
                     hook.sendMessageAfter(context);
                 } catch (Throwable e) {
-                    // Ignore
+                    //ignore
                 }
             }
         }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java
index 29f7507..9c7dbc4 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AckMessageProcessor.java
@@ -38,7 +38,7 @@ import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.PutMessageStatus;
 import org.apache.rocketmq.store.pop.AckMsg;
@@ -51,36 +51,54 @@ public class AckMessageProcessor implements NettyRequestProcessor {
 
     public AckMessageProcessor(final BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.reviveTopic = PopAckConstants.REVIVE_TOPIC + this.brokerController.getBrokerConfig().getBrokerClusterName();
+        this.reviveTopic = PopAckConstants.buildClusterReviveTopic(this.brokerController.getBrokerConfig().getBrokerClusterName());
         this.popReviveServices = new PopReviveService[this.brokerController.getBrokerConfig().getReviveQueueNum()];
         for (int i = 0; i < this.brokerController.getBrokerConfig().getReviveQueueNum(); i++) {
-            this.popReviveServices[i] = new PopReviveService(i, brokerController, reviveTopic);
+            this.popReviveServices[i] = new PopReviveService(brokerController, reviveTopic, i);
+            this.popReviveServices[i].setShouldRunPopRevive(brokerController.getBrokerConfig().getBrokerId() == 0);
         }
     }
 
-    @Override
-    public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
-        return this.processRequest(ctx.channel(), request, true);
+    public void startPopReviveService() {
+        for (PopReviveService popReviveService : popReviveServices) {
+            popReviveService.start();
+        }
     }
 
-    @Override
-    public boolean rejectRequest() {
-        return false;
+    public void shutdownPopReviveService() {
+        for (PopReviveService popReviveService : popReviveServices) {
+            popReviveService.stop();
+        }
     }
 
-    public void startPopReviveService() {
+    public void setPopReviveServiceStatus(boolean shouldStart) {
         for (PopReviveService popReviveService : popReviveServices) {
-            popReviveService.start();
+            popReviveService.setShouldRunPopRevive(shouldStart);
         }
     }
 
-    public void shutdownPopReviveService() {
+    public boolean isPopReviveServiceRunning() {
         for (PopReviveService popReviveService : popReviveServices) {
-            popReviveService.shutdown();
+            if (popReviveService.isShouldRunPopRevive()) {
+                return true;
+            }
         }
+
+        return false;
+    }
+
+    @Override
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        return this.processRequest(ctx.channel(), request, true);
     }
 
-    private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend) throws RemotingCommandException {
+    @Override public boolean rejectRequest() {
+        return false;
+    }
+
+    private RemotingCommand processRequest(final Channel channel, RemotingCommand request,
+        boolean brokerAllowSuspend) throws RemotingCommandException {
         final AckMessageRequestHeader requestHeader = (AckMessageRequestHeader) request.decodeCommandCustomHeader(AckMessageRequestHeader.class);
         MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
         AckMsg ackMsg = new AckMsg();
@@ -105,7 +123,11 @@ public class AckMessageProcessor implements NettyRequestProcessor {
         long minOffset = this.brokerController.getMessageStore().getMinOffsetInQueue(requestHeader.getTopic(), requestHeader.getQueueId());
         long maxOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(requestHeader.getTopic(), requestHeader.getQueueId());
         if (requestHeader.getOffset() < minOffset || requestHeader.getOffset() > maxOffset) {
+            String errorInfo = String.format("offset is illegal, key:%s@%d, commit:%d, store:%d~%d",
+                requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getOffset(), minOffset, maxOffset);
+            POP_LOGGER.warn(errorInfo);
             response.setCode(ResponseCode.NO_MESSAGE);
+            response.setRemark(errorInfo);
             return response;
         }
         String[] extraInfo = ExtraInfoUtil.split(requestHeader.getExtraInfo());
@@ -116,15 +138,19 @@ public class AckMessageProcessor implements NettyRequestProcessor {
         ackMsg.setTopic(requestHeader.getTopic());
         ackMsg.setQueueId(requestHeader.getQueueId());
         ackMsg.setPopTime(ExtraInfoUtil.getPopTime(extraInfo));
+        ackMsg.setBrokerName(ExtraInfoUtil.getBrokerName(extraInfo));
 
         int rqId = ExtraInfoUtil.getReviveQid(extraInfo);
 
+        this.brokerController.getBrokerStatsManager().incBrokerAckNums(1);
+        this.brokerController.getBrokerStatsManager().incGroupAckNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(), 1);
+
         if (rqId == KeyBuilder.POP_ORDER_REVIVE_QUEUE) {
             // order
             String lockKey = requestHeader.getTopic() + PopAckConstants.SPLIT
-                    + requestHeader.getConsumerGroup() + PopAckConstants.SPLIT + requestHeader.getQueueId();
+                + requestHeader.getConsumerGroup() + PopAckConstants.SPLIT + requestHeader.getQueueId();
             long oldOffset = this.brokerController.getConsumerOffsetManager().queryOffset(requestHeader.getConsumerGroup(),
-                    requestHeader.getTopic(), requestHeader.getQueueId());
+                requestHeader.getTopic(), requestHeader.getQueueId());
             if (requestHeader.getOffset() < oldOffset) {
                 return response;
             }
@@ -137,18 +163,18 @@ public class AckMessageProcessor implements NettyRequestProcessor {
                     return response;
                 }
                 long nextOffset = brokerController.getConsumerOrderInfoManager().commitAndNext(
-                        requestHeader.getTopic(), requestHeader.getConsumerGroup(),
-                        requestHeader.getQueueId(), requestHeader.getOffset());
+                    requestHeader.getTopic(), requestHeader.getConsumerGroup(),
+                    requestHeader.getQueueId(), requestHeader.getOffset());
                 if (nextOffset > -1) {
                     this.brokerController.getConsumerOffsetManager().commitOffset(channel.remoteAddress().toString(),
-                            requestHeader.getConsumerGroup(), requestHeader.getTopic(),
-                            requestHeader.getQueueId(),
-                            nextOffset);
+                        requestHeader.getConsumerGroup(), requestHeader.getTopic(),
+                        requestHeader.getQueueId(),
+                        nextOffset);
                     this.brokerController.getPopMessageProcessor().notifyMessageArriving(requestHeader.getTopic(), requestHeader.getConsumerGroup(),
-                            requestHeader.getQueueId());
+                        requestHeader.getQueueId());
                 } else if (nextOffset == -1) {
                     String errorInfo = String.format("offset is illegal, key:%s, old:%d, commit:%d, next:%d, %s",
-                            lockKey, oldOffset, requestHeader.getOffset(), nextOffset, channel.remoteAddress());
+                        lockKey, oldOffset, requestHeader.getOffset(), nextOffset, channel.remoteAddress());
                     POP_LOGGER.warn(errorInfo);
                     response.setCode(ResponseCode.MESSAGE_ILLEGAL);
                     response.setRemark(errorInfo);
@@ -175,7 +201,7 @@ public class AckMessageProcessor implements NettyRequestProcessor {
         MsgUtil.setMessageDeliverTime(this.brokerController, msgInner, ExtraInfoUtil.getPopTime(extraInfo) + ExtraInfoUtil.getInvisibleTime(extraInfo));
         msgInner.getProperties().put(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, PopMessageProcessor.genAckUniqueId(ackMsg));
         msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
-        PutMessageResult putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
+        PutMessageResult putMessageResult = this.brokerController.getEscapeBridge().putMessageToSpecificQueue(msgInner);
         if (putMessageResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
             && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
             && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
index 4606480..a283228 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/AdminBrokerProcessor.java
@@ -20,6 +20,10 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.rocketmq.acl.AccessValidator;
 import org.apache.rocketmq.acl.plain.PlainAccessValidator;
 import org.apache.rocketmq.broker.BrokerController;
@@ -27,20 +31,25 @@ import org.apache.rocketmq.broker.client.ClientChannelInfo;
 import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
 import org.apache.rocketmq.broker.filter.ConsumerFilterData;
 import org.apache.rocketmq.broker.filter.ExpressionMessageFilter;
+import org.apache.rocketmq.broker.plugin.BrokerAttachedPlugin;
+import org.apache.rocketmq.broker.subscription.SubscriptionGroupManager;
 import org.apache.rocketmq.broker.transaction.queue.TransactionalMessageUtil;
 import org.apache.rocketmq.common.AclConfig;
-import org.apache.rocketmq.common.BrokerConfig;
+import org.apache.rocketmq.common.LockCallback;
 import org.apache.rocketmq.common.MQVersion;
 import org.apache.rocketmq.common.MixAll;
 import org.apache.rocketmq.common.PlainAccessConfig;
 import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.UnlockCallback;
 import org.apache.rocketmq.common.UtilAll;
 import org.apache.rocketmq.common.admin.ConsumeStats;
 import org.apache.rocketmq.common.admin.OffsetWrapper;
 import org.apache.rocketmq.common.admin.TopicOffset;
 import org.apache.rocketmq.common.admin.TopicStatsTable;
 import org.apache.rocketmq.common.attribute.AttributeParser;
+import org.apache.rocketmq.common.constant.ConsumeInitMode;
 import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.constant.PermName;
 import org.apache.rocketmq.common.message.MessageAccessor;
 import org.apache.rocketmq.common.message.MessageConst;
 import org.apache.rocketmq.common.message.MessageDecoder;
@@ -49,6 +58,7 @@ import org.apache.rocketmq.common.message.MessageId;
 import org.apache.rocketmq.common.message.MessageQueue;
 import org.apache.rocketmq.common.protocol.RequestCode;
 import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.body.BrokerMemberGroup;
 import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
 import org.apache.rocketmq.common.protocol.body.BrokerStatsItem;
 import org.apache.rocketmq.common.protocol.body.Connection;
@@ -56,6 +66,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumeQueueData;
 import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
 import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
 import org.apache.rocketmq.common.protocol.body.GroupList;
+import org.apache.rocketmq.common.protocol.body.HARuntimeInfo;
 import org.apache.rocketmq.common.protocol.body.KVTable;
 import org.apache.rocketmq.common.protocol.body.LockBatchRequestBody;
 import org.apache.rocketmq.common.protocol.body.LockBatchResponseBody;
@@ -63,6 +74,7 @@ import org.apache.rocketmq.common.protocol.body.ProducerConnection;
 import org.apache.rocketmq.common.protocol.body.QueryConsumeQueueResponseBody;
 import org.apache.rocketmq.common.protocol.body.QueryConsumeTimeSpanBody;
 import org.apache.rocketmq.common.protocol.body.QueryCorrectionOffsetBody;
+import org.apache.rocketmq.common.protocol.body.QuerySubscriptionResponseBody;
 import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
 import org.apache.rocketmq.common.protocol.body.TopicConfigAndMappingSerializeWrapper;
 import org.apache.rocketmq.common.protocol.body.TopicList;
@@ -74,6 +86,8 @@ import org.apache.rocketmq.common.protocol.header.CreateTopicRequestHeader;
 import org.apache.rocketmq.common.protocol.header.DeleteAccessConfigRequestHeader;
 import org.apache.rocketmq.common.protocol.header.DeleteSubscriptionGroupRequestHeader;
 import org.apache.rocketmq.common.protocol.header.DeleteTopicRequestHeader;
+import org.apache.rocketmq.common.protocol.header.ExchangeHAInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.ExchangeHAInfoResponseHeader;
 import org.apache.rocketmq.common.protocol.header.GetAllTopicConfigResponseHeader;
 import org.apache.rocketmq.common.protocol.header.GetBrokerAclConfigResponseHeader;
 import org.apache.rocketmq.common.protocol.header.GetBrokerClusterAclConfigResponseBody;
@@ -91,22 +105,27 @@ import org.apache.rocketmq.common.protocol.header.GetMaxOffsetResponseHeader;
 import org.apache.rocketmq.common.protocol.header.GetMinOffsetRequestHeader;
 import org.apache.rocketmq.common.protocol.header.GetMinOffsetResponseHeader;
 import org.apache.rocketmq.common.protocol.header.GetProducerConnectionListRequestHeader;
+import org.apache.rocketmq.common.protocol.header.GetSubscriptionGroupConfigRequestHeader;
 import org.apache.rocketmq.common.protocol.header.GetTopicConfigRequestHeader;
 import org.apache.rocketmq.common.protocol.header.GetTopicStatsInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.NotifyMinBrokerIdChangeRequestHeader;
 import org.apache.rocketmq.common.protocol.header.QueryConsumeQueueRequestHeader;
 import org.apache.rocketmq.common.protocol.header.QueryConsumeTimeSpanRequestHeader;
 import org.apache.rocketmq.common.protocol.header.QueryCorrectionOffsetHeader;
+import org.apache.rocketmq.common.protocol.header.QuerySubscriptionByConsumerRequestHeader;
 import org.apache.rocketmq.common.protocol.header.QueryTopicConsumeByWhoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.QueryTopicsByConsumerRequestHeader;
+import org.apache.rocketmq.common.protocol.header.ResetMasterFlushOffsetHeader;
 import org.apache.rocketmq.common.protocol.header.ResetOffsetRequestHeader;
 import org.apache.rocketmq.common.protocol.header.ResumeCheckHalfMessageRequestHeader;
 import org.apache.rocketmq.common.protocol.header.SearchOffsetRequestHeader;
 import org.apache.rocketmq.common.protocol.header.SearchOffsetResponseHeader;
 import org.apache.rocketmq.common.protocol.header.UpdateGlobalWhiteAddrsConfigRequestHeader;
+import org.apache.rocketmq.common.protocol.header.UpdateGroupForbiddenRequestHeader;
 import org.apache.rocketmq.common.protocol.header.ViewBrokerStatsDataRequestHeader;
 import org.apache.rocketmq.common.protocol.header.filtersrv.RegisterFilterServerRequestHeader;
 import org.apache.rocketmq.common.protocol.header.filtersrv.RegisterFilterServerResponseHeader;
 import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
-import org.apache.rocketmq.common.rpc.RpcClient;
 import org.apache.rocketmq.common.rpc.RpcClientUtils;
 import org.apache.rocketmq.common.rpc.RpcException;
 import org.apache.rocketmq.common.rpc.RpcRequest;
@@ -118,6 +137,7 @@ import org.apache.rocketmq.common.statictopic.TopicQueueMappingDetail;
 import org.apache.rocketmq.common.statictopic.TopicQueueMappingUtils;
 import org.apache.rocketmq.common.stats.StatsItem;
 import org.apache.rocketmq.common.stats.StatsSnapshot;
+import org.apache.rocketmq.common.subscription.GroupForbidden;
 import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
 import org.apache.rocketmq.common.topic.TopicValidator;
 import org.apache.rocketmq.filter.util.BitsArray;
@@ -126,15 +146,13 @@ import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
-import org.apache.rocketmq.remoting.netty.AsyncNettyRequestProcessor;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.LanguageCode;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
 import org.apache.rocketmq.remoting.protocol.RemotingSysResponseCode;
 import org.apache.rocketmq.store.ConsumeQueueExt;
-import org.apache.rocketmq.store.DefaultMessageStore;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.MessageFilter;
 import org.apache.rocketmq.store.MessageStore;
 import org.apache.rocketmq.store.PutMessageResult;
@@ -159,16 +177,12 @@ import java.util.concurrent.ConcurrentMap;
 
 import static org.apache.rocketmq.remoting.protocol.RemotingCommand.buildErrorResponse;
 
-public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
-    private final BrokerController brokerController;
-    private final RpcClient rpcClient;
-    private final BrokerConfig brokerConfig;
+public class AdminBrokerProcessor implements NettyRequestProcessor {
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    protected final BrokerController brokerController;
 
     public AdminBrokerProcessor(final BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.brokerConfig = brokerController.getBrokerConfig();
-        this.rpcClient = brokerController.getBrokerOuterAPI().getRpcClient();
     }
 
     @Override
@@ -223,6 +237,10 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 return this.getConsumerStatus(ctx, request);
             case RequestCode.QUERY_TOPIC_CONSUME_BY_WHO:
                 return this.queryTopicConsumeByWho(ctx, request);
+            case RequestCode.QUERY_TOPICS_BY_CONSUMER:
+                return this.queryTopicsByConsumer(ctx, request);
+            case RequestCode.QUERY_SUBSCRIPTION_BY_CONSUMER:
+                return this.querySubscriptionByConsumer(ctx, request);
             case RequestCode.REGISTER_FILTER_SERVER:
                 return this.registerFilterServer(ctx, request);
             case RequestCode.QUERY_CONSUME_TIME_SPAN:
@@ -247,6 +265,10 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 return fetchAllConsumeStatsInBroker(ctx, request);
             case RequestCode.QUERY_CONSUME_QUEUE:
                 return queryConsumeQueue(ctx, request);
+            case RequestCode.UPDATE_AND_GET_GROUP_FORBIDDEN:
+                return this.updateAndGetGroupForbidden(ctx, request);
+            case RequestCode.GET_SUBSCRIPTIONGROUP_CONFIG:
+                return this.getSubscriptionGroup(ctx, request);
             case RequestCode.UPDATE_AND_CREATE_ACL_CONFIG:
                 return updateAndCreateAccessConfig(ctx, request);
             case RequestCode.DELETE_ACL_CONFIG:
@@ -263,11 +285,83 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 return getTopicConfig(ctx, request);
             case RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC:
                 return this.updateAndCreateStaticTopic(ctx, request);
+            case RequestCode.NOTIFY_MIN_BROKER_ID_CHANGE:
+                return this.notifyMinBrokerIdChange(ctx, request);
+            case RequestCode.EXCHANGE_BROKER_HA_INFO:
+                return this.updateBrokerHaInfo(ctx, request);
+            case RequestCode.GET_BROKER_HA_STATUS:
+                return this.getBrokerHaStatus(ctx, request);
+            case RequestCode.RESET_MASTER_FLUSH_OFFSET:
+                return this.resetMasterFlushOffset(ctx, request);
             default:
                 return getUnknownCmdResponse(ctx, request);
         }
     }
 
+    /**
+     * @param ctx
+     * @param request
+     * @return
+     * @throws RemotingCommandException
+     */
+    private RemotingCommand getSubscriptionGroup(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        GetSubscriptionGroupConfigRequestHeader requestHeader = (GetSubscriptionGroupConfigRequestHeader) request.decodeCommandCustomHeader(GetSubscriptionGroupConfigRequestHeader.class);
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+        SubscriptionGroupConfig groupConfig = this.brokerController.getSubscriptionGroupManager().getSubscriptionGroupTable().get(requestHeader.getGroup());
+        if (groupConfig == null) {
+            LOGGER.error("No group in this broker, client: {} group: {}", ctx.channel().remoteAddress(), requestHeader.getGroup());
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("No group in this broker");
+            return response;
+        }
+        String content = JSONObject.toJSONString(groupConfig);
+        try {
+            response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
+        } catch (UnsupportedEncodingException e) {
+            LOGGER.error("UnsupportedEncodingException getSubscriptionGroup: group=" + groupConfig.getGroupName(), e);
+
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("UnsupportedEncodingException " + e.getMessage());
+            return response;
+        }
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+
+        return response;
+    }
+
+    /**
+     * @param ctx
+     * @param request
+     * @return
+     */
+    private RemotingCommand updateAndGetGroupForbidden(ChannelHandlerContext ctx, RemotingCommand request)
+        throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        UpdateGroupForbiddenRequestHeader requestHeader = (UpdateGroupForbiddenRequestHeader) //
+            request.decodeCommandCustomHeader(UpdateGroupForbiddenRequestHeader.class);
+        String group = requestHeader.getGroup();
+        String topic = requestHeader.getTopic();
+        LOGGER.info("updateAndGetGroupForbidden called by {} for object {}@{} readable={}",//
+            RemotingHelper.parseChannelRemoteAddr(ctx.channel()), group, //
+            topic, requestHeader.getReadable());
+        SubscriptionGroupManager groupManager = this.brokerController.getSubscriptionGroupManager();
+        if (requestHeader.getReadable() != null) {
+            groupManager.updateForbidden(group, topic, PermName.INDEX_PERM_READ, !requestHeader.getReadable());
+        }
+
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark("");
+        GroupForbidden groupForbidden = new GroupForbidden();
+        groupForbidden.setGroup(group);
+        groupForbidden.setTopic(topic);
+        groupForbidden.setReadable(!groupManager.getForbidden(group, topic, PermName.INDEX_PERM_READ));
+        response.setBody(groupForbidden.toJson().getBytes());
+        return response;
+    }
+
     @Override
     public boolean rejectRequest() {
         return false;
@@ -278,7 +372,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         final CreateTopicRequestHeader requestHeader =
             (CreateTopicRequestHeader) request.decodeCommandCustomHeader(CreateTopicRequestHeader.class);
-        log.info("updateAndCreateTopic called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+
+        LOGGER.info("Broker receive request to update or create topic={}, caller address={}",
+            requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         String topic = requestHeader.getTopic();
 
@@ -302,8 +398,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             this.brokerController.getTopicConfigManager().updateTopicConfig(topicConfig);
             this.brokerController.registerIncrementBrokerData(topicConfig, this.brokerController.getTopicConfigManager().getDataVersion());
             response.setCode(ResponseCode.SUCCESS);
-        }  catch (Exception e) {
-            log.error("Update / create topic failed for [{}]", request, e);
+        } catch (Exception e) {
+            LOGGER.error("Update / create topic failed for [{}]", request, e);
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark(e.getMessage());
         }
@@ -311,11 +407,11 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
     }
 
     private synchronized RemotingCommand updateAndCreateStaticTopic(ChannelHandlerContext ctx,
-                                                              RemotingCommand request) throws RemotingCommandException {
+        RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         final CreateTopicRequestHeader requestHeader =
-                (CreateTopicRequestHeader) request.decodeCommandCustomHeader(CreateTopicRequestHeader.class);
-        log.info("updateAndCreateTopic called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+            (CreateTopicRequestHeader) request.decodeCommandCustomHeader(CreateTopicRequestHeader.class);
+        LOGGER.info("Broker receive request to update or create static topic={}, caller address={}", requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         final TopicQueueMappingDetail topicQueueMappingDetail = RemotingSerializable.decode(request.getBody(), TopicQueueMappingDetail.class);
 
@@ -347,21 +443,21 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             this.brokerController.registerIncrementBrokerData(topicConfig, this.brokerController.getTopicConfigManager().getDataVersion());
             response.setCode(ResponseCode.SUCCESS);
         } catch (Exception e) {
-            log.error("Update static topic failed for [{}]", request, e);
+            LOGGER.error("Update static topic failed for [{}]", request, e);
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark(e.getMessage());
         }
         return response;
     }
 
-
     private synchronized RemotingCommand deleteTopic(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         DeleteTopicRequestHeader requestHeader =
             (DeleteTopicRequestHeader) request.decodeCommandCustomHeader(DeleteTopicRequestHeader.class);
 
-        log.info("deleteTopic called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        LOGGER.info("AdminBrokerProcessor#deleteTopic: broker receive request to delete topic={}, caller={}",
+            requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         String topic = requestHeader.getTopic();
         if (!TopicValidator.validateTopic(topic, response)) {
@@ -371,9 +467,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             return response;
         }
 
-        this.brokerController.getTopicConfigManager().deleteTopicConfig(topic);
-        this.brokerController.getTopicQueueMappingManager().delete(topic);
-
+        this.brokerController.getTopicConfigManager().deleteTopicConfig(requestHeader.getTopic());
+        this.brokerController.getTopicQueueMappingManager().delete(requestHeader.getTopic());
+        this.brokerController.getConsumerOffsetManager().cleanOffsetByTopic(requestHeader.getTopic());
         this.brokerController.getMessageStore()
             .cleanUnusedTopic(this.brokerController.getTopicConfigManager().getTopicConfigTable().keySet());
         if (this.brokerController.getBrokerConfig().isAutoDeleteUnusedStats()) {
@@ -412,13 +508,13 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 ctx.writeAndFlush(response);
             } else {
                 String errorMsg = "The accesskey[" + requestHeader.getAccessKey() + "] corresponding to accessConfig has been updated failed.";
-                log.warn(errorMsg);
+                LOGGER.warn(errorMsg);
                 response.setCode(ResponseCode.UPDATE_AND_CREATE_ACL_CONFIG_FAILED);
                 response.setRemark(errorMsg);
                 return response;
             }
         } catch (Exception e) {
-            log.error("Failed to generate a proper update accessvalidator response", e);
+            LOGGER.error("Failed to generate a proper update accessvalidator response", e);
             response.setCode(ResponseCode.UPDATE_AND_CREATE_ACL_CONFIG_FAILED);
             response.setRemark(e.getMessage());
             return response;
@@ -433,7 +529,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
         final DeleteAccessConfigRequestHeader requestHeader =
             (DeleteAccessConfigRequestHeader) request.decodeCommandCustomHeader(DeleteAccessConfigRequestHeader.class);
-        log.info("DeleteAccessConfig called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        LOGGER.info("DeleteAccessConfig called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         try {
             String accessKey = requestHeader.getAccessKey();
@@ -446,14 +542,14 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 ctx.writeAndFlush(response);
             } else {
                 String errorMsg = "The accesskey[" + requestHeader.getAccessKey() + "] corresponding to accessConfig has been deleted failed.";
-                log.warn(errorMsg);
+                LOGGER.warn(errorMsg);
                 response.setCode(ResponseCode.DELETE_ACL_CONFIG_FAILED);
                 response.setRemark(errorMsg);
                 return response;
             }
 
         } catch (Exception e) {
-            log.error("Failed to generate a proper delete accessvalidator response", e);
+            LOGGER.error("Failed to generate a proper delete accessvalidator response", e);
             response.setCode(ResponseCode.DELETE_ACL_CONFIG_FAILED);
             response.setRemark(e.getMessage());
             return response;
@@ -480,13 +576,13 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 ctx.writeAndFlush(response);
             } else {
                 String errorMsg = "The globalWhiteAddresses[" + requestHeader.getGlobalWhiteAddrs() + "] has been updated failed.";
-                log.warn(errorMsg);
+                LOGGER.warn(errorMsg);
                 response.setCode(ResponseCode.UPDATE_GLOBAL_WHITE_ADDRS_CONFIG_FAILED);
                 response.setRemark(errorMsg);
                 return response;
             }
         } catch (Exception e) {
-            log.error("Failed to generate a proper update globalWhiteAddresses response", e);
+            LOGGER.error("Failed to generate a proper update globalWhiteAddresses response", e);
             response.setCode(ResponseCode.UPDATE_GLOBAL_WHITE_ADDRS_CONFIG_FAILED);
             response.setRemark(e.getMessage());
             return response;
@@ -499,12 +595,11 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
         final RemotingCommand response = RemotingCommand.createResponseCommand(GetBrokerAclConfigResponseHeader.class);
 
-        final GetBrokerAclConfigResponseHeader responseHeader = (GetBrokerAclConfigResponseHeader)response.readCustomHeader();
+        final GetBrokerAclConfigResponseHeader responseHeader = (GetBrokerAclConfigResponseHeader) response.readCustomHeader();
 
         try {
             AccessValidator accessValidator = this.brokerController.getAccessValidatorMap().get(PlainAccessValidator.class);
 
-            responseHeader.setAllAclFileVersion(JSON.toJSONString(accessValidator.getAllAclConfigVersion()));
             responseHeader.setVersion(accessValidator.getAclConfigVersion());
             responseHeader.setBrokerAddr(this.brokerController.getBrokerAddr());
             responseHeader.setBrokerName(this.brokerController.getBrokerConfig().getBrokerName());
@@ -514,7 +609,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             response.setRemark(null);
             return response;
         } catch (Exception e) {
-            log.error("Failed to generate a proper getBrokerAclConfigVersion response", e);
+            LOGGER.error("Failed to generate a proper getBrokerAclConfigVersion response", e);
         }
 
         return null;
@@ -535,7 +630,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             response.setRemark(null);
             return response;
         } catch (Exception e) {
-            log.error("Failed to generate a proper getBrokerClusterAclConfig response", e);
+            LOGGER.error("Failed to generate a proper getBrokerClusterAclConfig response", e);
         }
 
         return null;
@@ -544,7 +639,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
     private RemotingCommand getUnknownCmdResponse(ChannelHandlerContext ctx, RemotingCommand request) {
         String error = " request type " + request.getCode() + " not supported";
         final RemotingCommand response =
-                RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);
+            RemotingCommand.createResponseCommand(RemotingSysResponseCode.REQUEST_CODE_NOT_SUPPORTED, error);
         return response;
     }
 
@@ -561,20 +656,19 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         topicConfigAndMappingSerializeWrapper.setMappingDataVersion(this.brokerController.getTopicQueueMappingManager().getDataVersion());
         topicConfigAndMappingSerializeWrapper.setTopicQueueMappingDetailMap(this.brokerController.getTopicQueueMappingManager().getTopicQueueMappingTable());
 
-
         String content = topicConfigAndMappingSerializeWrapper.toJson();
         if (content != null && content.length() > 0) {
             try {
                 response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
             } catch (UnsupportedEncodingException e) {
-                log.error("", e);
+                LOGGER.error("", e);
 
                 response.setCode(ResponseCode.SYSTEM_ERROR);
-                response.setRemark("UnsupportedEncodingException " + e);
+                response.setRemark("UnsupportedEncodingException " + e.getMessage());
                 return response;
             }
         } else {
-            log.error("No topic in this broker, client: {}", ctx.channel().remoteAddress());
+            LOGGER.error("No topic in this broker, client: {}", ctx.channel().remoteAddress());
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("No topic in this broker");
             return response;
@@ -589,7 +683,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
     private synchronized RemotingCommand updateBrokerConfig(ChannelHandlerContext ctx, RemotingCommand request) {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
 
-        log.info("updateBrokerConfig called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        final String callerAddress = RemotingHelper.parseChannelRemoteAddr(ctx.channel());
+        LOGGER.info("Broker receive request to update config, caller address={}", callerAddress);
 
         byte[] body = request.getBody();
         if (body != null) {
@@ -597,20 +692,21 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 String bodyStr = new String(body, MixAll.DEFAULT_CHARSET);
                 Properties properties = MixAll.string2Properties(bodyStr);
                 if (properties != null) {
-                    log.info("updateBrokerConfig, new config: [{}] client: {} ", properties, ctx.channel().remoteAddress());
+                    LOGGER.info("updateBrokerConfig, new config: [{}] client: {} ", properties, ctx.channel().remoteAddress());
                     this.brokerController.getConfiguration().update(properties);
                     if (properties.containsKey("brokerPermission")) {
                         this.brokerController.getTopicConfigManager().getDataVersion().nextVersion();
                         this.brokerController.registerBrokerAll(false, false, true);
                     }
                 } else {
-                    log.error("string2Properties error");
+                    LOGGER.error("string2Properties error");
                     response.setCode(ResponseCode.SYSTEM_ERROR);
                     response.setRemark("string2Properties error");
                     return response;
                 }
             } catch (UnsupportedEncodingException e) {
-                log.error("", e);
+                LOGGER.error("AdminBrokerProcessor#updateBrokerConfig: unexpected error, caller={}",
+                    callerAddress, e);
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("UnsupportedEncodingException " + e);
                 return response;
@@ -632,7 +728,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             try {
                 response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
             } catch (UnsupportedEncodingException e) {
-                log.error("", e);
+                LOGGER.error("AdminBrokerProcessor#getBrokerConfig: unexpected error, caller={}",
+                    RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
 
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("UnsupportedEncodingException " + e);
@@ -647,7 +744,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
-    private RemotingCommand rewriteRequestForStaticTopic(SearchOffsetRequestHeader requestHeader, TopicQueueMappingContext mappingContext) {
+    private RemotingCommand rewriteRequestForStaticTopic(SearchOffsetRequestHeader requestHeader,
+        TopicQueueMappingContext mappingContext) {
         try {
             if (mappingContext.getMappingDetail() == null) {
                 return null;
@@ -684,7 +782,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                     }
                     SearchOffsetResponseHeader offsetResponseHeader = (SearchOffsetResponseHeader) rpcResponse.getHeader();
                     if (offsetResponseHeader.getOffset() < 0
-                            || (item.checkIfEndOffsetDecided() && offsetResponseHeader.getOffset() >= item.getEndOffset())) {
+                        || (item.checkIfEndOffsetDecided() && offsetResponseHeader.getOffset() >= item.getEndOffset())) {
                         continue;
                     } else {
                         offset = item.computeStaticQueueOffsetStrictly(offsetResponseHeader.getOffset());
@@ -727,7 +825,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
-    private RemotingCommand rewriteRequestForStaticTopic(GetMaxOffsetRequestHeader requestHeader, TopicQueueMappingContext mappingContext) {
+    private RemotingCommand rewriteRequestForStaticTopic(GetMaxOffsetRequestHeader requestHeader,
+        TopicQueueMappingContext mappingContext) {
         if (mappingContext.getMappingDetail() == null) {
             return null;
         }
@@ -793,7 +892,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
-    private CompletableFuture<RpcResponse> handleGetMinOffsetForStaticTopic(RpcRequest request, TopicQueueMappingContext mappingContext)  {
+    private CompletableFuture<RpcResponse> handleGetMinOffsetForStaticTopic(RpcRequest request,
+        TopicQueueMappingContext mappingContext) {
         if (mappingContext.getMappingDetail() == null) {
             return null;
         }
@@ -801,11 +901,11 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         if (!mappingContext.isLeader()) {
             //this may not
             return CompletableFuture.completedFuture(new RpcResponse(new RpcException(ResponseCode.NOT_LEADER_FOR_QUEUE,
-                    String.format("%s-%d is not leader in broker %s, request code %d", mappingContext.getTopic(), mappingContext.getGlobalId(), mappingDetail.getBname(), request.getCode()))));
+                String.format("%s-%d is not leader in broker %s, request code %d", mappingContext.getTopic(), mappingContext.getGlobalId(), mappingDetail.getBname(), request.getCode()))));
         }
         GetMinOffsetRequestHeader requestHeader = (GetMinOffsetRequestHeader) request.getHeader();
         LogicQueueMappingItem mappingItem = TopicQueueMappingUtils.findLogicQueueMappingItem(mappingContext.getMappingItemList(), 0L, true);
-        assert  mappingItem != null;
+        assert mappingItem != null;
         try {
             requestHeader.setBname(mappingItem.getBname());
             requestHeader.setLo(false);
@@ -829,12 +929,12 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             responseHeader.setOffset(offset);
             return CompletableFuture.completedFuture(new RpcResponse(ResponseCode.SUCCESS, responseHeader, null));
         } catch (Throwable t) {
-            log.error("rewriteRequestForStaticTopic failed", t);
+            LOGGER.error("rewriteRequestForStaticTopic failed", t);
             return CompletableFuture.completedFuture(new RpcResponse(new RpcException(ResponseCode.SYSTEM_ERROR, t.getMessage(), t)));
         }
     }
 
-    private CompletableFuture<RpcResponse>  handleGetMinOffset(RpcRequest request) {
+    private CompletableFuture<RpcResponse> handleGetMinOffset(RpcRequest request) {
         assert request.getCode() == RequestCode.GET_MIN_OFFSET;
         GetMinOffsetRequestHeader requestHeader = (GetMinOffsetRequestHeader) request.getHeader();
         TopicQueueMappingContext mappingContext = this.brokerController.getTopicQueueMappingManager().buildTopicQueueMappingContext(requestHeader, false);
@@ -854,14 +954,15 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             (GetMinOffsetRequestHeader) request.decodeCommandCustomHeader(GetMinOffsetRequestHeader.class);
         try {
             CompletableFuture<RpcResponse> responseFuture = handleGetMinOffset(new RpcRequest(RequestCode.GET_MIN_OFFSET, requestHeader, null));
-            RpcResponse  rpcResponse = responseFuture.get();
+            RpcResponse rpcResponse = responseFuture.get();
             return RpcClientUtils.createCommandForRpcResponse(rpcResponse);
         } catch (Throwable t) {
             return buildErrorResponse(ResponseCode.SYSTEM_ERROR, t.getMessage());
         }
     }
 
-    private RemotingCommand rewriteRequestForStaticTopic(GetEarliestMsgStoretimeRequestHeader requestHeader, TopicQueueMappingContext mappingContext)  {
+    private RemotingCommand rewriteRequestForStaticTopic(GetEarliestMsgStoretimeRequestHeader requestHeader,
+        TopicQueueMappingContext mappingContext) {
         if (mappingContext.getMappingDetail() == null) {
             return null;
         }
@@ -935,10 +1036,78 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         LockBatchRequestBody requestBody = LockBatchRequestBody.decode(request.getBody(), LockBatchRequestBody.class);
 
-        Set<MessageQueue> lockOKMQSet = this.brokerController.getRebalanceLockManager().tryLockBatch(
+        Set<MessageQueue> lockOKMQSet = new HashSet<>();
+        Set<MessageQueue> selfLockOKMQSet = this.brokerController.getRebalanceLockManager().tryLockBatch(
             requestBody.getConsumerGroup(),
             requestBody.getMqSet(),
             requestBody.getClientId());
+        if (requestBody.isOnlyThisBroker() || !brokerController.getBrokerConfig().isLockInStrictMode()) {
+            lockOKMQSet = selfLockOKMQSet;
+        } else {
+            requestBody.setOnlyThisBroker(true);
+            int replicaSize = this.brokerController.getMessageStoreConfig().getTotalReplicas();
+
+            int quorum = replicaSize / 2 + 1;
+
+            if (quorum <= 1) {
+                lockOKMQSet = selfLockOKMQSet;
+            } else {
+                final ConcurrentMap<MessageQueue, Integer> mqLockMap = new ConcurrentHashMap<>();
+                for (MessageQueue mq : selfLockOKMQSet) {
+                    if (!mqLockMap.containsKey(mq)) {
+                        mqLockMap.put(mq, 0);
+                    }
+                    mqLockMap.put(mq, mqLockMap.get(mq) + 1);
+                }
+
+                BrokerMemberGroup memberGroup = this.brokerController.getBrokerMemberGroup();
+
+                if (memberGroup != null) {
+                    Map<Long, String> addrMap = new HashMap<>(memberGroup.getBrokerAddrs());
+                    addrMap.remove(this.brokerController.getBrokerConfig().getBrokerId());
+                    final CountDownLatch countDownLatch = new CountDownLatch(addrMap.size());
+                    requestBody.setMqSet(selfLockOKMQSet);
+                    requestBody.setOnlyThisBroker(true);
+                    for (Long brokerId : addrMap.keySet()) {
+                        try {
+                            this.brokerController.getBrokerOuterAPI().lockBatchMQAsync(addrMap.get(brokerId),
+                                requestBody, 1000, new LockCallback() {
+                                    @Override
+                                    public void onSuccess(Set<MessageQueue> lockOKMQSet) {
+                                        for (MessageQueue mq : lockOKMQSet) {
+                                            if (!mqLockMap.containsKey(mq)) {
+                                                mqLockMap.put(mq, 0);
+                                            }
+                                            mqLockMap.put(mq, mqLockMap.get(mq) + 1);
+                                        }
+                                        countDownLatch.countDown();
+                                    }
+
+                                    @Override
+                                    public void onException(Throwable e) {
+                                        LOGGER.warn("lockBatchMQAsync on {} failed, {}", addrMap.get(brokerId), e);
+                                        countDownLatch.countDown();
+                                    }
+                                });
+                        } catch (Exception e) {
+                            LOGGER.warn("lockBatchMQAsync on {} failed, {}", addrMap.get(brokerId), e);
+                            countDownLatch.countDown();
+                        }
+                    }
+                    try {
+                        countDownLatch.await(2000, TimeUnit.MILLISECONDS);
+                    } catch (InterruptedException e) {
+                        LOGGER.warn("lockBatchMQ exception on {}, {}", this.brokerController.getBrokerConfig().getBrokerName(), e);
+                    }
+                }
+
+                for (MessageQueue mq : mqLockMap.keySet()) {
+                    if (mqLockMap.get(mq) >= quorum) {
+                        lockOKMQSet.add(mq);
+                    }
+                }
+            }
+        }
 
         LockBatchResponseBody responseBody = new LockBatchResponseBody();
         responseBody.setLockOKMQSet(lockOKMQSet);
@@ -954,10 +1123,36 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         UnlockBatchRequestBody requestBody = UnlockBatchRequestBody.decode(request.getBody(), UnlockBatchRequestBody.class);
 
-        this.brokerController.getRebalanceLockManager().unlockBatch(
-            requestBody.getConsumerGroup(),
-            requestBody.getMqSet(),
-            requestBody.getClientId());
+        if (requestBody.isOnlyThisBroker() || !this.brokerController.getBrokerConfig().isLockInStrictMode()) {
+            this.brokerController.getRebalanceLockManager().unlockBatch(
+                requestBody.getConsumerGroup(),
+                requestBody.getMqSet(),
+                requestBody.getClientId());
+        } else {
+            requestBody.setOnlyThisBroker(true);
+            BrokerMemberGroup memberGroup = this.brokerController.getBrokerMemberGroup();
+
+            if (memberGroup != null) {
+                Map<Long, String> addrMap = memberGroup.getBrokerAddrs();
+                for (Long brokerId : addrMap.keySet()) {
+                    try {
+                        this.brokerController.getBrokerOuterAPI().unlockBatchMQAsync(addrMap.get(brokerId), requestBody, 1000, new UnlockCallback() {
+                            @Override
+                            public void onSuccess() {
+
+                            }
+
+                            @Override
+                            public void onException(Throwable e) {
+                                LOGGER.warn("unlockBatchMQ exception on {}, {}", addrMap.get(brokerId), e);
+                            }
+                        });
+                    } catch (Exception e) {
+                        LOGGER.warn("unlockBatchMQ exception on {}, {}", addrMap.get(brokerId), e);
+                    }
+                }
+            }
+        }
 
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
@@ -968,7 +1163,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
 
-        log.info("updateAndCreateSubscriptionGroup called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        LOGGER.info("AdminBrokerProcessor#updateAndCreateSubscriptionGroup called by {}",
+            RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         SubscriptionGroupConfig config = RemotingSerializable.decode(request.getBody(), SubscriptionGroupConfig.class);
         if (config != null) {
@@ -980,6 +1176,26 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
+    private void initConsumerOffset(String clientHost, String groupName, int mode, TopicConfig topicConfig) {
+        String topic = topicConfig.getTopicName();
+        for (int queueId = 0; queueId < topicConfig.getReadQueueNums(); queueId++) {
+            if (this.brokerController.getConsumerOffsetManager().queryOffset(groupName, topic, queueId) > -1) {
+                continue;
+            }
+            long offset = 0;
+            if (this.brokerController.getMessageStore().getConsumeQueue(topic, queueId) != null) {
+                if (ConsumeInitMode.MAX == mode) {
+                    offset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId);
+                } else if (ConsumeInitMode.MIN == mode) {
+                    offset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
+                }
+            }
+            this.brokerController.getConsumerOffsetManager().commitOffset(clientHost, groupName, topic, queueId, offset);
+            LOGGER.info("AdminBrokerProcessor#initConsumerOffset: consumerGroup={}, topic={}, queueId={}, offset={}",
+                groupName, topic, queueId, offset);
+        }
+    }
+
     private RemotingCommand getAllSubscriptionGroup(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
@@ -988,14 +1204,14 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             try {
                 response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
             } catch (UnsupportedEncodingException e) {
-                log.error("", e);
+                LOGGER.error("UnsupportedEncodingException getAllSubscriptionGroup", e);
 
                 response.setCode(ResponseCode.SYSTEM_ERROR);
-                response.setRemark("UnsupportedEncodingException " + e);
+                response.setRemark("UnsupportedEncodingException " + e.getMessage());
                 return response;
             }
         } else {
-            log.error("No subscription group in this broker, client:{} ", ctx.channel().remoteAddress());
+            LOGGER.error("No subscription group in this broker, client:{} ", ctx.channel().remoteAddress());
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("No subscription group in this broker");
             return response;
@@ -1013,11 +1229,12 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         DeleteSubscriptionGroupRequestHeader requestHeader =
             (DeleteSubscriptionGroupRequestHeader) request.decodeCommandCustomHeader(DeleteSubscriptionGroupRequestHeader.class);
 
-        log.info("deleteSubscriptionGroup called by {}", RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
+        LOGGER.info("AdminBrokerProcessor#deleteSubscriptionGroup, caller={}",
+            RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
 
         this.brokerController.getSubscriptionGroupManager().deleteSubscriptionGroupConfig(requestHeader.getGroupName());
 
-        if (requestHeader.isRemoveOffset()) {
+        if (requestHeader.isCleanOffset()) {
             this.brokerController.getConsumerOffsetManager().removeOffset(requestHeader.getGroupName());
         }
 
@@ -1029,8 +1246,6 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
-
-
     private RemotingCommand getTopicStatsInfo(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
@@ -1044,8 +1259,8 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             response.setRemark("topic[" + topic + "] not exist");
             return response;
         }
-        TopicStatsTable topicStatsTable = new TopicStatsTable();
 
+        TopicStatsTable topicStatsTable = new TopicStatsTable();
         for (int i = 0; i < topicConfig.getWriteQueueNums(); i++) {
             MessageQueue mq = new MessageQueue();
             mq.setTopic(topic);
@@ -1054,12 +1269,14 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
             TopicOffset topicOffset = new TopicOffset();
             long min = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, i);
-            if (min < 0)
+            if (min < 0) {
                 min = 0;
+            }
 
             long max = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
-            if (max < 0)
+            if (max < 0) {
                 max = 0;
+            }
 
             long timestamp = 0;
             if (max > 0) {
@@ -1172,7 +1389,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         for (String topic : topics) {
             TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(topic);
             if (null == topicConfig) {
-                log.warn("consumeStats, topic config not exist, {}", topic);
+                LOGGER.warn("AdminBrokerProcessor#getConsumeStats: topic config does not exist, topic={}", topic);
                 continue;
             }
 
@@ -1184,7 +1401,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
                 if (null == findSubscriptionData
                     && this.brokerController.getConsumerManager().findSubscriptionDataCount(requestHeader.getConsumerGroup()) > 0) {
-                    log.warn("consumeStats, the consumer group[{}], topic[{}] not exist", requestHeader.getConsumerGroup(), topic);
+                    LOGGER.warn(
+                        "AdminBrokerProcessor#getConsumeStats: topic does not exist in consumer group's subscription, "
+                            + "topic={}, consumer group={}", topic, requestHeader.getConsumerGroup());
                     continue;
                 }
             }
@@ -1198,19 +1417,20 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 OffsetWrapper offsetWrapper = new OffsetWrapper();
 
                 long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
-                if (brokerOffset < 0)
+                if (brokerOffset < 0) {
                     brokerOffset = 0;
+                }
 
                 long consumerOffset = this.brokerController.getConsumerOffsetManager().queryOffset(
-                    requestHeader.getConsumerGroup(),
-                    topic,
-                    i);
+                    requestHeader.getConsumerGroup(), topic, i);
+
                 // the consumerOffset cannot be zero for static topic because of the "double read check" strategy
                 // just remain the logic for dynamic topic
                 // maybe we should remove it in the future
                 if (mappingDetail == null) {
-                    if (consumerOffset < 0)
+                    if (consumerOffset < 0) {
                         consumerOffset = 0;
+                    }
                 }
 
                 offsetWrapper.setBrokerOffset(brokerOffset);
@@ -1223,6 +1443,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                         offsetWrapper.setLastTimestamp(lastTimestamp);
                     }
                 }
+
                 consumeStats.getOffsetTable().put(mq, offsetWrapper);
             }
 
@@ -1247,14 +1468,14 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             try {
                 response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
             } catch (UnsupportedEncodingException e) {
-                log.error("get all consumer offset from master error.", e);
+                LOGGER.error("get all consumer offset from master error.", e);
 
                 response.setCode(ResponseCode.SYSTEM_ERROR);
-                response.setRemark("UnsupportedEncodingException " + e);
+                response.setRemark("UnsupportedEncodingException " + e.getMessage());
                 return response;
             }
         } else {
-            log.error("No consumer offset in this broker, client: {} ", ctx.channel().remoteAddress());
+            LOGGER.error("No consumer offset in this broker, client: {} ", ctx.channel().remoteAddress());
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("No consumer offset in this broker");
             return response;
@@ -1269,26 +1490,21 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
     private RemotingCommand getAllDelayOffset(ChannelHandlerContext ctx, RemotingCommand request) {
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
 
-        if (!(this.brokerController.getMessageStore() instanceof DefaultMessageStore)) {
-            log.error("Delay offset not supported in this messagetore, client: {} ", ctx.channel().remoteAddress());
-            response.setCode(ResponseCode.SYSTEM_ERROR);
-            response.setRemark("Delay offset not supported in this messagetore");
-            return response;
-        }
-
-        String content = ((DefaultMessageStore) this.brokerController.getMessageStore()).getScheduleMessageService().encode();
+        String content = this.brokerController.getScheduleMessageService().encode();
         if (content != null && content.length() > 0) {
             try {
                 response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
             } catch (UnsupportedEncodingException e) {
-                log.error("Get all delay offset from master error.", e);
+                LOGGER.error("AdminBrokerProcessor#getAllDelayOffset: unexpected error, caller={}.",
+                    RemotingHelper.parseChannelRemoteAddr(ctx.channel()), e);
 
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("UnsupportedEncodingException " + e);
                 return response;
             }
         } else {
-            log.error("No delay offset in this broker, client: {} ", ctx.channel().remoteAddress());
+            LOGGER.error("AdminBrokerProcessor#getAllDelayOffset: no delay offset in this broker, caller={}",
+                RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("No delay offset in this broker");
             return response;
@@ -1304,7 +1520,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         RemotingCommand request) throws RemotingCommandException {
         final ResetOffsetRequestHeader requestHeader =
             (ResetOffsetRequestHeader) request.decodeCommandCustomHeader(ResetOffsetRequestHeader.class);
-        log.info("[reset-offset] reset offset started by {}. topic={}, group={}, timestamp={}, isForce={}",
+        LOGGER.info("[reset-offset] reset offset started by {}. topic={}, group={}, timestamp={}, isForce={}",
             RemotingHelper.parseChannelRemoteAddr(ctx.channel()), requestHeader.getTopic(), requestHeader.getGroup(),
             requestHeader.getTimestamp(), requestHeader.isForce());
         boolean isC = false;
@@ -1323,7 +1539,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         final GetConsumerStatusRequestHeader requestHeader =
             (GetConsumerStatusRequestHeader) request.decodeCommandCustomHeader(GetConsumerStatusRequestHeader.class);
 
-        log.info("[get-consumer-status] get consumer status by {}. topic={}, group={}",
+        LOGGER.info("[get-consumer-status] get consumer status by {}. topic={}, group={}",
             RemotingHelper.parseChannelRemoteAddr(ctx.channel()), requestHeader.getTopic(), requestHeader.getGroup());
 
         return this.brokerController.getBroker2Client().getConsumeStatus(requestHeader.getTopic(), requestHeader.getGroup(),
@@ -1353,6 +1569,47 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         return response;
     }
 
+    private RemotingCommand queryTopicsByConsumer(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        QueryTopicsByConsumerRequestHeader requestHeader =
+            (QueryTopicsByConsumerRequestHeader) request.decodeCommandCustomHeader(QueryTopicsByConsumerRequestHeader.class);
+
+        Set<String> topics = this.brokerController.getConsumerOffsetManager().whichTopicByConsumer(requestHeader.getGroup());
+
+        TopicList topicList = new TopicList();
+        topicList.setTopicList(topics);
+        topicList.setBrokerAddr(brokerController.getBrokerAddr());
+        byte[] body = topicList.encode();
+
+        response.setBody(body);
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+        return response;
+    }
+
+    private RemotingCommand querySubscriptionByConsumer(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+        QuerySubscriptionByConsumerRequestHeader requestHeader =
+            (QuerySubscriptionByConsumerRequestHeader) request.decodeCommandCustomHeader(QuerySubscriptionByConsumerRequestHeader.class);
+
+        SubscriptionData subscriptionData =
+            this.brokerController.getConsumerManager().findSubscriptionData(requestHeader.getGroup(), requestHeader.getTopic());
+
+        QuerySubscriptionResponseBody responseBody = new QuerySubscriptionResponseBody();
+        responseBody.setGroup(requestHeader.getGroup());
+        responseBody.setTopic(requestHeader.getTopic());
+        responseBody.setSubscriptionData(subscriptionData);
+        byte[] body = responseBody.encode();
+
+        response.setBody(body);
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+        return response;
+
+    }
+
     private RemotingCommand registerFilterServer(ChannelHandlerContext ctx,
         RemotingCommand request) throws RemotingCommandException {
         final RemotingCommand response = RemotingCommand.createResponseCommand(RegisterFilterServerResponseHeader.class);
@@ -1440,20 +1697,20 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
     }
 
     public RemotingCommand cleanExpiredConsumeQueue() {
-        log.warn("invoke cleanExpiredConsumeQueue start.");
+        LOGGER.info("AdminBrokerProcessor#cleanExpiredConsumeQueue: start.");
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         brokerController.getMessageStore().cleanExpiredConsumerQueue();
-        log.warn("invoke cleanExpiredConsumeQueue end.");
+        LOGGER.info("AdminBrokerProcessor#cleanExpiredConsumeQueue: end.");
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
         return response;
     }
 
     public RemotingCommand cleanUnusedTopic() {
-        log.warn("invoke cleanUnusedTopic start.");
+        LOGGER.warn("invoke cleanUnusedTopic start.");
         final RemotingCommand response = RemotingCommand.createResponseCommand(null);
         brokerController.getMessageStore().cleanUnusedTopic(brokerController.getTopicConfigManager().getTopicConfigTable().keySet());
-        log.warn("invoke cleanUnusedTopic end.");
+        LOGGER.warn("invoke cleanUnusedTopic end.");
         response.setCode(ResponseCode.SUCCESS);
         response.setRemark(null);
         return response;
@@ -1501,7 +1758,22 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         final ConsumeMessageDirectlyResultRequestHeader requestHeader = (ConsumeMessageDirectlyResultRequestHeader) request
             .decodeCommandCustomHeader(ConsumeMessageDirectlyResultRequestHeader.class);
 
+        // brokerName
         request.getExtFields().put("brokerName", this.brokerController.getBrokerConfig().getBrokerName());
+        // topicSysFlag
+        if (StringUtils.isNotEmpty(requestHeader.getTopic())) {
+            TopicConfig topicConfig = this.brokerController.getTopicConfigManager().getTopicConfigTable().get(requestHeader.getTopic());
+            if (topicConfig != null) {
+                request.addExtField("topicSysFlag", String.valueOf(topicConfig.getTopicSysFlag()));
+            }
+        }
+        // groupSysFlag
+        if (StringUtils.isNotEmpty(requestHeader.getConsumerGroup())) {
+            SubscriptionGroupConfig groupConfig = brokerController.getSubscriptionGroupManager().getSubscriptionGroupTable().get(requestHeader.getConsumerGroup());
+            if (groupConfig != null) {
+                request.addExtField("groupSysFlag", String.valueOf(groupConfig.getGroupSysFlag()));
+            }
+        }
         SelectMappedBufferResult selectMappedBufferResult = null;
         try {
             MessageId messageId = MessageDecoder.decodeMessageId(requestHeader.getMsgId());
@@ -1538,7 +1810,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         for (String topic : topics) {
             TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(topic);
             if (null == topicConfig) {
-                log.warn("[cloneGroupOffset], topic config not exist, {}", topic);
+                LOGGER.warn("[cloneGroupOffset], topic config not exist, {}", topic);
                 continue;
             }
 
@@ -1548,7 +1820,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                     this.brokerController.getConsumerManager().findSubscriptionData(requestHeader.getSrcGroup(), topic);
                 if (this.brokerController.getConsumerManager().findSubscriptionDataCount(requestHeader.getSrcGroup()) > 0
                     && findSubscriptionData == null) {
-                    log.warn("[cloneGroupOffset], the consumer group[{}], topic[{}] not exist", requestHeader.getSrcGroup(), topic);
+                    LOGGER.warn(
+                        "AdminBrokerProcessor#cloneGroupOffset: topic does not exist in consumer group's "
+                            + "subscription, topic={}, consumer group={}", topic, requestHeader.getSrcGroup());
                     continue;
                 }
             }
@@ -1633,7 +1907,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 ConsumeStats consumeStats = new ConsumeStats();
                 TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(topic);
                 if (null == topicConfig) {
-                    log.warn("consumeStats, topic config not exist, {}", topic);
+                    LOGGER.warn(
+                        "AdminBrokerProcessor#fetchAllConsumeStatsInBroker: topic config does not exist, topic={}",
+                        topic);
                     continue;
                 }
 
@@ -1646,7 +1922,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
                     if (null == findSubscriptionData
                         && this.brokerController.getConsumerManager().findSubscriptionDataCount(group) > 0) {
-                        log.warn("consumeStats, the consumer group[{}], topic[{}] not exist", group, topic);
+                        LOGGER.warn(
+                            "AdminBrokerProcessor#fetchAllConsumeStatsInBroker: topic does not exist in consumer "
+                                + "group's subscription, topic={}, consumer group={}", topic, group);
                         continue;
                     }
                 }
@@ -1658,8 +1936,9 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                     mq.setQueueId(i);
                     OffsetWrapper offsetWrapper = new OffsetWrapper();
                     long brokerOffset = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, i);
-                    if (brokerOffset < 0)
+                    if (brokerOffset < 0) {
                         brokerOffset = 0;
+                    }
                     long consumerOffset = this.brokerController.getConsumerOffsetManager().queryOffset(
                         group,
                         topic,
@@ -1700,6 +1979,15 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
     private HashMap<String, String> prepareRuntimeInfo() {
         HashMap<String, String> runtimeInfo = this.brokerController.getMessageStore().getRuntimeInfo();
+
+        for (BrokerAttachedPlugin brokerAttachedPlugin : brokerController.getBrokerAttachedPlugins()) {
+            if (brokerAttachedPlugin != null) {
+                brokerAttachedPlugin.buildRuntimeInfo(runtimeInfo);
+            }
+        }
+
+        this.brokerController.getScheduleMessageService().buildRunningStats(runtimeInfo);
+        runtimeInfo.put("brokerActive", String.valueOf(this.brokerController.isSpecialServiceRunning()));
         runtimeInfo.put("brokerVersionDesc", MQVersion.getVersionDesc(MQVersion.CURRENT_VERSION));
         runtimeInfo.put("brokerVersion", String.valueOf(MQVersion.CURRENT_VERSION));
 
@@ -1713,8 +2001,25 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         runtimeInfo.put("msgGetTotalTodayMorning", String.valueOf(this.brokerController.getBrokerStats().getMsgGetTotalTodayMorning()));
         runtimeInfo.put("msgGetTotalTodayNow", String.valueOf(this.brokerController.getBrokerStats().getMsgGetTotalTodayNow()));
 
-        runtimeInfo.put("sendThreadPoolQueueSize", String.valueOf(this.brokerController.getSendThreadPoolQueue().size()));
+        runtimeInfo.put("dispatchBehindBytes", String.valueOf(this.brokerController.getMessageStore().dispatchBehindBytes()));
+        runtimeInfo.put("pageCacheLockTimeMills", String.valueOf(this.brokerController.getMessageStore().lockTimeMills()));
+
+        runtimeInfo.put("earliestMessageTimeStamp", String.valueOf(this.brokerController.getMessageStore().getEarliestMessageTime()));
+        runtimeInfo.put("startAcceptSendRequestTimeStamp", String.valueOf(this.brokerController.getBrokerConfig().getStartAcceptSendRequestTimeStamp()));
 
+        MessageStore messageStore = this.brokerController.getMessageStore();
+        runtimeInfo.put("remainTransientStoreBufferNumbs", String.valueOf(messageStore.remainTransientStoreBufferNumbs()));
+        if (this.brokerController.getMessageStoreConfig().isTransientStorePoolEnable()) {
+            runtimeInfo.put("remainHowManyDataToCommit", MixAll.humanReadableByteCount(messageStore.remainHowManyDataToCommit(), false));
+        }
+        runtimeInfo.put("remainHowManyDataToFlush", MixAll.humanReadableByteCount(messageStore.remainHowManyDataToFlush(), false));
+
+        java.io.File commitLogDir = new java.io.File(this.brokerController.getMessageStoreConfig().getStorePathRootDir());
+        if (commitLogDir.exists()) {
+            runtimeInfo.put("commitLogDirCapacity", String.format("Total : %s, Free : %s.", MixAll.humanReadableByteCount(commitLogDir.getTotalSpace(), false), MixAll.humanReadableByteCount(commitLogDir.getFreeSpace(), false)));
+        }
+
+        runtimeInfo.put("sendThreadPoolQueueSize", String.valueOf(this.brokerController.getSendThreadPoolQueue().size()));
         runtimeInfo.put("sendThreadPoolQueueCapacity",
             String.valueOf(this.brokerController.getBrokerConfig().getSendThreadPoolQueueCapacity()));
 
@@ -1722,36 +2027,22 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         runtimeInfo.put("pullThreadPoolQueueCapacity",
             String.valueOf(this.brokerController.getBrokerConfig().getPullThreadPoolQueueCapacity()));
 
+        runtimeInfo.put("litePullThreadPoolQueueSize", String.valueOf(brokerController.getLitePullThreadPoolQueue().size()));
+        runtimeInfo.put("litePullThreadPoolQueueCapacity",
+            String.valueOf(this.brokerController.getBrokerConfig().getLitePullThreadPoolQueueCapacity()));
+
         runtimeInfo.put("queryThreadPoolQueueSize", String.valueOf(this.brokerController.getQueryThreadPoolQueue().size()));
         runtimeInfo.put("queryThreadPoolQueueCapacity",
             String.valueOf(this.brokerController.getBrokerConfig().getQueryThreadPoolQueueCapacity()));
 
-        runtimeInfo.put("EndTransactionQueueSize", String.valueOf(this.brokerController.getEndTransactionThreadPoolQueue().size()));
-        runtimeInfo.put("EndTransactionThreadPoolQueueCapacity",
-            String.valueOf(this.brokerController.getBrokerConfig().getEndTransactionPoolQueueCapacity()));
-
-        runtimeInfo.put("dispatchBehindBytes", String.valueOf(this.brokerController.getMessageStore().dispatchBehindBytes()));
-        runtimeInfo.put("pageCacheLockTimeMills", String.valueOf(this.brokerController.getMessageStore().lockTimeMills()));
-
         runtimeInfo.put("sendThreadPoolQueueHeadWaitTimeMills", String.valueOf(this.brokerController.headSlowTimeMills4SendThreadPoolQueue()));
-        runtimeInfo.put("pullThreadPoolQueueHeadWaitTimeMills", String.valueOf(this.brokerController.headSlowTimeMills4PullThreadPoolQueue()));
+        runtimeInfo.put("pullThreadPoolQueueHeadWaitTimeMills", String.valueOf(brokerController.headSlowTimeMills4PullThreadPoolQueue()));
         runtimeInfo.put("queryThreadPoolQueueHeadWaitTimeMills", String.valueOf(this.brokerController.headSlowTimeMills4QueryThreadPoolQueue()));
+        runtimeInfo.put("litePullThreadPoolQueueHeadWaitTimeMills", String.valueOf(brokerController.headSlowTimeMills4LitePullThreadPoolQueue()));
 
-        runtimeInfo.put("earliestMessageTimeStamp", String.valueOf(this.brokerController.getMessageStore().getEarliestMessageTime()));
-        runtimeInfo.put("startAcceptSendRequestTimeStamp", String.valueOf(this.brokerController.getBrokerConfig().getStartAcceptSendRequestTimeStamp()));
-        if (this.brokerController.getMessageStore() instanceof DefaultMessageStore) {
-            DefaultMessageStore defaultMessageStore = (DefaultMessageStore) this.brokerController.getMessageStore();
-            runtimeInfo.put("remainTransientStoreBufferNumbs", String.valueOf(defaultMessageStore.remainTransientStoreBufferNumbs()));
-            if (defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
-                runtimeInfo.put("remainHowManyDataToCommit", MixAll.humanReadableByteCount(defaultMessageStore.getCommitLog().remainHowManyDataToCommit(), false));
-            }
-            runtimeInfo.put("remainHowManyDataToFlush", MixAll.humanReadableByteCount(defaultMessageStore.getCommitLog().remainHowManyDataToFlush(), false));
-        }
-
-        java.io.File commitLogDir = new java.io.File(this.brokerController.getMessageStoreConfig().getStorePathRootDir());
-        if (commitLogDir.exists()) {
-            runtimeInfo.put("commitLogDirCapacity", String.format("Total : %s, Free : %s.", MixAll.humanReadableByteCount(commitLogDir.getTotalSpace(), false), MixAll.humanReadableByteCount(commitLogDir.getUsableSpace(), false)));
-        }
+        runtimeInfo.put("EndTransactionQueueSize", String.valueOf(this.brokerController.getEndTransactionThreadPoolQueue().size()));
+        runtimeInfo.put("EndTransactionThreadPoolQueueCapacity",
+            String.valueOf(this.brokerController.getBrokerConfig().getEndTransactionPoolQueueCapacity()));
 
         return runtimeInfo;
     }
@@ -1844,7 +2135,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
             List<ConsumeQueueData> queues = new ArrayList<>();
             while (result.hasNext()) {
                 CqUnit cqUnit = result.next();
-                if (cqUnit.getQueueOffset() - requestHeader.getIndex() >=  requestHeader.getCount()) {
+                if (cqUnit.getQueueOffset() - requestHeader.getIndex() >= requestHeader.getCount()) {
                     break;
                 }
 
@@ -1898,18 +2189,18 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
                 .putMessage(toMessageExtBrokerInner(msg));
             if (putMessageResult != null
                 && putMessageResult.getPutMessageStatus() == PutMessageStatus.PUT_OK) {
-                log.info(
+                LOGGER.info(
                     "Put message back to RMQ_SYS_TRANS_HALF_TOPIC. real topic={}",
                     msg.getUserProperty(MessageConst.PROPERTY_REAL_TOPIC));
                 response.setCode(ResponseCode.SUCCESS);
                 response.setRemark(null);
             } else {
-                log.error("Put message back to RMQ_SYS_TRANS_HALF_TOPIC failed.");
+                LOGGER.error("Put message back to RMQ_SYS_TRANS_HALF_TOPIC failed.");
                 response.setCode(ResponseCode.SYSTEM_ERROR);
                 response.setRemark("Put message back to RMQ_SYS_TRANS_HALF_TOPIC failed.");
             }
         } catch (Exception e) {
-            log.error("Exception was thrown when putting message back to RMQ_SYS_TRANS_HALF_TOPIC.");
+            LOGGER.error("Exception was thrown when putting message back to RMQ_SYS_TRANS_HALF_TOPIC.");
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("Exception was thrown when putting message back to RMQ_SYS_TRANS_HALF_TOPIC.");
         } finally {
@@ -1946,7 +2237,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
         TopicConfig topicConfig = this.brokerController.getTopicConfigManager().getTopicConfigTable().get(requestHeader.getTopic());
         if (topicConfig == null) {
-            log.error("No topic in this broker, client: {} topic: {}", ctx.channel().remoteAddress(), requestHeader.getTopic());
+            LOGGER.error("No topic in this broker, client: {} topic: {}", ctx.channel().remoteAddress(), requestHeader.getTopic());
             //be care of the response code, should set "not-exist" explictly
             response.setCode(ResponseCode.TOPIC_NOT_EXIST);
             response.setRemark("No topic in this broker. topic: " + requestHeader.getTopic());
@@ -1960,7 +2251,7 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
         try {
             response.setBody(content.getBytes(MixAll.DEFAULT_CHARSET));
         } catch (UnsupportedEncodingException e) {
-            log.error("UnsupportedEncodingException getTopicConfig: topic=" + topicConfig.getTopicName(), e);
+            LOGGER.error("UnsupportedEncodingException getTopicConfig: topic=" + topicConfig.getTopicName(), e);
 
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark("UnsupportedEncodingException " + e.getMessage());
@@ -1971,4 +2262,84 @@ public class AdminBrokerProcessor extends AsyncNettyRequestProcessor implements
 
         return response;
     }
+
+    private RemotingCommand notifyMinBrokerIdChange(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        NotifyMinBrokerIdChangeRequestHeader requestHeader = request.decodeCommandCustomHeader(NotifyMinBrokerIdChangeRequestHeader.class);
+
+        RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+        LOGGER.warn("min broker id changed, prev {}, new {}", this.brokerController.getMinBrokerIdInGroup(), requestHeader.getMinBrokerId());
+
+        this.brokerController.updateMinBroker(requestHeader.getMinBrokerId(), requestHeader.getMinBrokerAddr(),
+            requestHeader.getOfflineBrokerAddr(),
+            requestHeader.getHaBrokerAddr());
+
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+
+        return response;
+    }
+
+    private RemotingCommand updateBrokerHaInfo(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        RemotingCommand response = RemotingCommand.createResponseCommand(ExchangeHAInfoResponseHeader.class);
+
+        ExchangeHAInfoRequestHeader requestHeader = request.decodeCommandCustomHeader(ExchangeHAInfoRequestHeader.class);
+        if (requestHeader.getMasterHaAddress() != null) {
+            this.brokerController.getMessageStore().updateHaMasterAddress(requestHeader.getMasterHaAddress());
+            this.brokerController.getMessageStore().updateMasterAddress(requestHeader.getMasterAddress());
+            if (this.brokerController.getMessageStore().getMasterFlushedOffset() == 0
+                && this.brokerController.getMessageStoreConfig().isSyncMasterFlushOffsetWhenStartup()) {
+                LOGGER.info("Set master flush offset in slave to {}", requestHeader.getMasterFlushOffset());
+                this.brokerController.getMessageStore().setMasterFlushedOffset(requestHeader.getMasterFlushOffset());
+            }
+        } else if (this.brokerController.getBrokerConfig().getBrokerId() == MixAll.MASTER_ID) {
+            final ExchangeHAInfoResponseHeader responseHeader = (ExchangeHAInfoResponseHeader) response.readCustomHeader();
+            responseHeader.setMasterHaAddress(this.brokerController.getHAServerAddr());
+            responseHeader.setMasterFlushOffset(this.brokerController.getMessageStore().getBrokerInitMaxOffset());
+            responseHeader.setMasterAddress(this.brokerController.getBrokerAddr());
+        }
+
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+
+        return response;
+    }
+
+    private RemotingCommand getBrokerHaStatus(ChannelHandlerContext ctx, RemotingCommand request) {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+        HARuntimeInfo runtimeInfo = this.brokerController.getMessageStore().getHARuntimeInfo();
+
+        if (runtimeInfo != null) {
+            byte[] body = runtimeInfo.encode();
+            response.setBody(body);
+            response.setCode(ResponseCode.SUCCESS);
+            response.setRemark(null);
+        } else {
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark("Can not get HARuntimeInfo, may be duplicationEnable is true");
+        }
+
+        return response;
+    }
+
+    private RemotingCommand resetMasterFlushOffset(ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        final RemotingCommand response = RemotingCommand.createResponseCommand(null);
+
+        if (this.brokerController.getBrokerConfig().getBrokerId() != MixAll.MASTER_ID) {
+
+            ResetMasterFlushOffsetHeader requestHeader = request.decodeCommandCustomHeader(ResetMasterFlushOffsetHeader.class);
+
+            if (requestHeader.getMasterFlushOffset() != null) {
+                this.brokerController.getMessageStore().setMasterFlushedOffset(requestHeader.getMasterFlushOffset());
+            }
+        }
+
+        response.setCode(ResponseCode.SUCCESS);
+        response.setRemark(null);
+        return response;
+    }
 }
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java
index e40a5e8..991a705 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ChangeInvisibleTimeProcessor.java
@@ -38,7 +38,7 @@ import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.PutMessageStatus;
 import org.apache.rocketmq.store.pop.AckMsg;
@@ -47,16 +47,16 @@ import org.apache.rocketmq.store.pop.PopCheckPoint;
 public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
     private static final InternalLogger POP_LOGGER = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
     private final BrokerController brokerController;
-    private String reviveTopic;
+    private final String reviveTopic;
 
     public ChangeInvisibleTimeProcessor(final BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.reviveTopic = PopAckConstants.REVIVE_TOPIC + this.brokerController.getBrokerConfig().getBrokerClusterName();
-
+        this.reviveTopic = PopAckConstants.buildClusterReviveTopic(this.brokerController.getBrokerConfig().getBrokerClusterName());
     }
 
     @Override
-    public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
         return this.processRequest(ctx.channel(), request, true);
     }
 
@@ -65,7 +65,8 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
         return false;
     }
 
-    private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend) throws RemotingCommandException {
+    private RemotingCommand processRequest(final Channel channel, RemotingCommand request,
+        boolean brokerAllowSuspend) throws RemotingCommandException {
         final ChangeInvisibleTimeRequestHeader requestHeader = (ChangeInvisibleTimeRequestHeader) request.decodeCommandCustomHeader(ChangeInvisibleTimeRequestHeader.class);
         RemotingCommand response = RemotingCommand.createResponseCommand(ChangeInvisibleTimeResponseHeader.class);
         response.setCode(ResponseCode.SUCCESS);
@@ -98,7 +99,7 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
 
         // add new ck
         long now = System.currentTimeMillis();
-        PutMessageResult ckResult = appendCheckPoint(requestHeader, ExtraInfoUtil.getReviveQid(extraInfo), requestHeader.getQueueId(), requestHeader.getOffset(), now);
+        PutMessageResult ckResult = appendCheckPoint(requestHeader, ExtraInfoUtil.getReviveQid(extraInfo), requestHeader.getQueueId(), requestHeader.getOffset(), now, ExtraInfoUtil.getBrokerName(extraInfo));
 
         if (ckResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
             && ckResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
@@ -133,9 +134,13 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
         ackMsg.setTopic(requestHeader.getTopic());
         ackMsg.setQueueId(requestHeader.getQueueId());
         ackMsg.setPopTime(ExtraInfoUtil.getPopTime(extraInfo));
+        ackMsg.setBrokerName(ExtraInfoUtil.getBrokerName(extraInfo));
 
         int rqId = ExtraInfoUtil.getReviveQid(extraInfo);
 
+        this.brokerController.getBrokerStatsManager().incBrokerAckNums(1);
+        this.brokerController.getBrokerStatsManager().incGroupAckNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(), 1);
+
         if (brokerController.getPopMessageProcessor().getPopBufferMergeService().addAk(rqId, ackMsg)) {
             return;
         }
@@ -150,7 +155,7 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
         MsgUtil.setMessageDeliverTime(this.brokerController, msgInner, ExtraInfoUtil.getPopTime(extraInfo) + ExtraInfoUtil.getInvisibleTime(extraInfo));
         msgInner.getProperties().put(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX, PopMessageProcessor.genAckUniqueId(ackMsg));
         msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
-        PutMessageResult putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
+        PutMessageResult putMessageResult = this.brokerController.getEscapeBridge().putMessageToSpecificQueue(msgInner);
         if (putMessageResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
             && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
             && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
@@ -159,7 +164,8 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
         }
     }
 
-    private PutMessageResult appendCheckPoint(final ChangeInvisibleTimeRequestHeader requestHeader, int reviveQid, int queueId, long offset, long popTime) {
+    private PutMessageResult appendCheckPoint(final ChangeInvisibleTimeRequestHeader requestHeader, int reviveQid,
+        int queueId, long offset, long popTime, String brokerName) {
         // add check point msg to revive log
         MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
         msgInner.setTopic(reviveTopic);
@@ -187,7 +193,12 @@ public class ChangeInvisibleTimeProcessor implements NettyRequestProcessor {
 
         if (brokerController.getBrokerConfig().isEnablePopLog()) {
             POP_LOGGER.info("change Invisible , appendCheckPoint, topic {}, queueId {},reviveId {}, cid {}, startOffset {}, rt {}, result {}", requestHeader.getTopic(), queueId, reviveQid, requestHeader.getConsumerGroup(), offset,
-                    ck.getReviveTime(), putMessageResult);
+                ck.getReviveTime(), putMessageResult);
+        }
+
+        if (putMessageResult != null && putMessageResult.isOk()) {
+            this.brokerController.getBrokerStatsManager().incBrokerCkNums(1);
+            this.brokerController.getBrokerStatsManager().incGroupCkNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(), 1);
         }
 
         return putMessageResult;
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
index aa7d0a3..3a453b6 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ClientManageProcessor.java
@@ -28,6 +28,7 @@ import org.apache.rocketmq.common.protocol.ResponseCode;
 import org.apache.rocketmq.common.protocol.body.CheckClientRequestBody;
 import org.apache.rocketmq.common.protocol.header.UnregisterClientRequestHeader;
 import org.apache.rocketmq.common.protocol.header.UnregisterClientResponseHeader;
+import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
 import org.apache.rocketmq.common.protocol.heartbeat.ConsumerData;
 import org.apache.rocketmq.common.protocol.heartbeat.HeartbeatData;
 import org.apache.rocketmq.common.protocol.heartbeat.ProducerData;
@@ -40,11 +41,10 @@ import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.remoting.common.RemotingHelper;
 import org.apache.rocketmq.remoting.exception.RemotingCommandException;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
-import org.apache.rocketmq.remoting.netty.AsyncNettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
-public class ClientManageProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+public class ClientManageProcessor implements NettyRequestProcessor {
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private final BrokerController brokerController;
 
     public ClientManageProcessor(final BrokerController brokerController) {
@@ -82,39 +82,54 @@ public class ClientManageProcessor extends AsyncNettyRequestProcessor implements
             request.getVersion()
         );
 
-        for (ConsumerData data : heartbeatData.getConsumerDataSet()) {
+        for (ConsumerData consumerData : heartbeatData.getConsumerDataSet()) {
+            //Reject the PullConsumer
+            if (brokerController.getBrokerConfig().isRejectPullConsumerEnable()) {
+                if (ConsumeType.CONSUME_ACTIVELY == consumerData.getConsumeType()) {
+                    continue;
+                }
+            }
+
+            boolean hasOrderTopicSub = false;
+
+            for (final SubscriptionData subscriptionData : consumerData.getSubscriptionDataSet()) {
+                if (this.brokerController.getTopicConfigManager().isOrderTopic(subscriptionData.getTopic())) {
+                    hasOrderTopicSub = true;
+                    break;
+                }
+            }
+
             SubscriptionGroupConfig subscriptionGroupConfig =
                 this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(
-                    data.getGroupName());
+                    consumerData.getGroupName());
             boolean isNotifyConsumerIdsChangedEnable = true;
             if (null != subscriptionGroupConfig) {
                 isNotifyConsumerIdsChangedEnable = subscriptionGroupConfig.isNotifyConsumerIdsChangedEnable();
                 int topicSysFlag = 0;
-                if (data.isUnitMode()) {
+                if (consumerData.isUnitMode()) {
                     topicSysFlag = TopicSysFlag.buildSysFlag(false, true);
                 }
-                String newTopic = MixAll.getRetryTopic(data.getGroupName());
+                String newTopic = MixAll.getRetryTopic(consumerData.getGroupName());
                 this.brokerController.getTopicConfigManager().createTopicInSendMessageBackMethod(
                     newTopic,
                     subscriptionGroupConfig.getRetryQueueNums(),
-                    PermName.PERM_WRITE | PermName.PERM_READ, topicSysFlag);
+                    PermName.PERM_WRITE | PermName.PERM_READ, hasOrderTopicSub, topicSysFlag);
             }
 
             boolean changed = this.brokerController.getConsumerManager().registerConsumer(
-                data.getGroupName(),
+                consumerData.getGroupName(),
                 clientChannelInfo,
-                data.getConsumeType(),
-                data.getMessageModel(),
-                data.getConsumeFromWhere(),
-                data.getSubscriptionDataSet(),
+                consumerData.getConsumeType(),
+                consumerData.getMessageModel(),
+                consumerData.getConsumeFromWhere(),
+                consumerData.getSubscriptionDataSet(),
                 isNotifyConsumerIdsChangedEnable
             );
 
             if (changed) {
-                log.info("registerConsumer info changed {} {}",
-                    data.toString(),
-                    RemotingHelper.parseChannelRemoteAddr(ctx.channel())
-                );
+                LOGGER.info(
+                    "ClientManageProcessor: registerConsumer info changed, SDK address={}, consumerData={}",
+                    RemotingHelper.parseChannelRemoteAddr(ctx.channel()), consumerData.toString());
             }
         }
 
@@ -190,7 +205,7 @@ public class ClientManageProcessor extends AsyncNettyRequestProcessor implements
             try {
                 FilterFactory.INSTANCE.get(subscriptionData.getExpressionType()).compile(subscriptionData.getSubString());
             } catch (Exception e) {
-                log.warn("Client {}@{} filter message, but failed to compile expression! sub={}, error={}",
+                LOGGER.warn("Client {}@{} filter message, but failed to compile expression! sub={}, error={}",
                     requestBody.getClientId(), requestBody.getGroup(), requestBody.getSubscriptionData(), e.getMessage());
                 response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
                 response.setRemark(e.getMessage());
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
index 31a7993..afa1aa4 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ConsumerManageProcessor.java
@@ -17,6 +17,7 @@
 package org.apache.rocketmq.broker.processor;
 
 import io.netty.channel.ChannelHandlerContext;
+import java.util.Set;
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.broker.client.ConsumerGroupInfo;
 import org.apache.rocketmq.common.constant.LoggerName;
@@ -40,7 +41,6 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 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;
 
@@ -48,9 +48,8 @@ import java.util.List;
 
 import static org.apache.rocketmq.remoting.protocol.RemotingCommand.buildErrorResponse;
 
-public class ConsumerManageProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
-    private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
-
+public class ConsumerManageProcessor implements NettyRequestProcessor {
+    private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
     private final BrokerController brokerController;
 
     public ConsumerManageProcessor(final BrokerController brokerController) {
@@ -99,11 +98,11 @@ public class ConsumerManageProcessor extends AsyncNettyRequestProcessor implemen
                 response.setRemark(null);
                 return response;
             } else {
-                log.warn("getAllClientId failed, {} {}", requestHeader.getConsumerGroup(),
+                LOGGER.warn("getAllClientId failed, {} {}", requestHeader.getConsumerGroup(),
                     RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
             }
         } else {
-            log.warn("getConsumerGroupInfo failed, {} {}", requestHeader.getConsumerGroup(),
+            LOGGER.warn("getConsumerGroupInfo failed, {} {}", requestHeader.getConsumerGroup(),
                 RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
         }
 
@@ -156,10 +155,17 @@ public class ConsumerManageProcessor extends AsyncNettyRequestProcessor implemen
         if (rewriteResult != null) {
             return rewriteResult;
         }
-        this.brokerController.getConsumerOffsetManager().commitOffset(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), requestHeader.getConsumerGroup(),
-            requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getCommitOffset());
-        response.setCode(ResponseCode.SUCCESS);
-        response.setRemark(null);
+        Set<String> topicSets = this.brokerController.getTopicConfigManager().getTopicConfigTable().keySet();
+        if (topicSets.contains(requestHeader.getTopic())) {
+            this.brokerController.getConsumerOffsetManager().commitOffset(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), requestHeader.getConsumerGroup(),
+                requestHeader.getTopic(), requestHeader.getQueueId(), requestHeader.getCommitOffset());
+            response.setCode(ResponseCode.SUCCESS);
+            response.setRemark(null);
+        } else {
+            response.setCode(ResponseCode.TOPIC_NOT_EXIST);
+            response.setRemark("Topic " + requestHeader.getTopic() + " not exist!");
+        }
+
         return response;
     }
 
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/DefaultPullMessageResultHandler.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/DefaultPullMessageResultHandler.java
new file mode 100644
index 0000000..0e6a112
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/DefaultPullMessageResultHandler.java
@@ -0,0 +1,244 @@
+/*
+ * 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.broker.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.FileRegion;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.longpolling.PullRequest;
+import org.apache.rocketmq.broker.pagecache.ManyMessageTransfer;
+import org.apache.rocketmq.broker.plugin.PullMessageResultHandler;
+import org.apache.rocketmq.common.TopicFilterType;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.message.MessageQueue;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.header.PullMessageRequestHeader;
+import org.apache.rocketmq.common.protocol.header.PullMessageResponseHeader;
+import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData;
+import org.apache.rocketmq.common.protocol.topic.OffsetMovedEvent;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.common.sysflag.MessageSysFlag;
+import org.apache.rocketmq.common.sysflag.PullSysFlag;
+import org.apache.rocketmq.common.topic.TopicValidator;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.apache.rocketmq.remoting.common.RemotingUtil;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
+import org.apache.rocketmq.store.MessageFilter;
+import org.apache.rocketmq.store.PutMessageResult;
+import org.apache.rocketmq.store.config.BrokerRole;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class DefaultPullMessageResultHandler implements PullMessageResultHandler {
+
+    protected static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    protected final BrokerController brokerController;
+
+    public DefaultPullMessageResultHandler(final BrokerController brokerController) {
+        this.brokerController = brokerController;
+    }
+
+    @Override
+    public RemotingCommand handle(final GetMessageResult getMessageResult,
+                                  final RemotingCommand request,
+                                  final PullMessageRequestHeader requestHeader,
+                                  final Channel channel,
+                                  final SubscriptionData subscriptionData,
+                                  final SubscriptionGroupConfig subscriptionGroupConfig,
+                                  final boolean brokerAllowSuspend,
+                                  final MessageFilter messageFilter,
+                                  RemotingCommand response) {
+
+        final PullMessageResponseHeader responseHeader = (PullMessageResponseHeader) response.readCustomHeader();
+
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS:
+                this.brokerController.getBrokerStatsManager().incGroupGetNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
+                        getMessageResult.getMessageCount());
+
+                this.brokerController.getBrokerStatsManager().incGroupGetSize(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
+                        getMessageResult.getBufferTotalSize());
+
+                this.brokerController.getBrokerStatsManager().incBrokerGetNums(getMessageResult.getMessageCount());
+
+                if (!channelIsWritable(channel, requestHeader)) {
+                    getMessageResult.release();
+                    //ignore pull request
+                    return null;
+                }
+
+                if (this.brokerController.getBrokerConfig().isTransferMsgByHeap()) {
+
+                    final long beginTimeMills = this.brokerController.getMessageStore().now();
+                    final byte[] r = this.readGetMessageResult(getMessageResult, requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueId());
+                    this.brokerController.getBrokerStatsManager().incGroupGetLatency(requestHeader.getConsumerGroup(),
+                            requestHeader.getTopic(), requestHeader.getQueueId(),
+                            (int) (this.brokerController.getMessageStore().now() - beginTimeMills));
+                    response.setBody(r);
+                    return response;
+                } else {
+                    try {
+                        FileRegion fileRegion =
+                                new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()), getMessageResult);
+                        channel.writeAndFlush(fileRegion).addListener(new ChannelFutureListener() {
+                            @Override
+                            public void operationComplete(ChannelFuture future) throws Exception {
+                                getMessageResult.release();
+                                if (!future.isSuccess()) {
+                                    log.error("Fail to transfer messages from page cache to {}", channel.remoteAddress(), future.cause());
+                                }
+                            }
+                        });
+                    } catch (Throwable e) {
+                        log.error("Error occurred when transferring messages from page cache", e);
+                        getMessageResult.release();
+                    }
+                    return null;
+                }
+            case ResponseCode.PULL_NOT_FOUND:
+                final boolean hasSuspendFlag = PullSysFlag.hasSuspendFlag(requestHeader.getSysFlag());
+                final long suspendTimeoutMillisLong = hasSuspendFlag ? requestHeader.getSuspendTimeoutMillis() : 0;
+
+                if (brokerAllowSuspend && hasSuspendFlag) {
+                    long pollingTimeMills = suspendTimeoutMillisLong;
+                    if (!this.brokerController.getBrokerConfig().isLongPollingEnable()) {
+                        pollingTimeMills = this.brokerController.getBrokerConfig().getShortPollingTimeMills();
+                    }
+
+                    String topic = requestHeader.getTopic();
+                    long offset = requestHeader.getQueueOffset();
+                    int queueId = requestHeader.getQueueId();
+                    PullRequest pullRequest = new PullRequest(request, channel, pollingTimeMills,
+                            this.brokerController.getMessageStore().now(), offset, subscriptionData, messageFilter);
+                    this.brokerController.getPullRequestHoldService().suspendPullRequest(topic, queueId, pullRequest);
+                    return null;
+                }
+            case ResponseCode.PULL_RETRY_IMMEDIATELY:
+                break;
+            case ResponseCode.PULL_OFFSET_MOVED:
+                if (this.brokerController.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE
+                        || this.brokerController.getMessageStoreConfig().isOffsetCheckInSlave()) {
+                    MessageQueue mq = new MessageQueue();
+                    mq.setTopic(requestHeader.getTopic());
+                    mq.setQueueId(requestHeader.getQueueId());
+                    mq.setBrokerName(this.brokerController.getBrokerConfig().getBrokerName());
+
+                    OffsetMovedEvent event = new OffsetMovedEvent();
+                    event.setConsumerGroup(requestHeader.getConsumerGroup());
+                    event.setMessageQueue(mq);
+                    event.setOffsetRequest(requestHeader.getQueueOffset());
+                    event.setOffsetNew(getMessageResult.getNextBeginOffset());
+                    log.warn(
+                            "PULL_OFFSET_MOVED:correction offset. topic={}, groupId={}, requestOffset={}, newOffset={}, suggestBrokerId={}",
+                            requestHeader.getTopic(), requestHeader.getConsumerGroup(), event.getOffsetRequest(), event.getOffsetNew(),
+                            responseHeader.getSuggestWhichBrokerId());
+                } else {
+                    responseHeader.setSuggestWhichBrokerId(subscriptionGroupConfig.getBrokerId());
+                    response.setCode(ResponseCode.PULL_RETRY_IMMEDIATELY);
+                    log.warn("PULL_OFFSET_MOVED:none correction. topic={}, groupId={}, requestOffset={}, suggestBrokerId={}",
+                            requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getQueueOffset(),
+                            responseHeader.getSuggestWhichBrokerId());
+                }
+
+                break;
+            default:
+                log.warn("[BUG] impossible result code of get message: {}", response.getCode());
+                assert false;
+        }
+
+        return response;
+    }
+
+    private boolean channelIsWritable(Channel channel, PullMessageRequestHeader requestHeader) {
+        if (this.brokerController.getBrokerConfig().isNetWorkFlowController()) {
+            if (!channel.isWritable()) {
+                log.warn("channel {} not writable ,cid {}", channel.remoteAddress(), requestHeader.getConsumerGroup());
+                return false;
+            }
+
+        }
+        return true;
+    }
+
+    protected byte[] readGetMessageResult(final GetMessageResult getMessageResult, final String group, final String topic,
+        final int queueId) {
+        final ByteBuffer byteBuffer = ByteBuffer.allocate(getMessageResult.getBufferTotalSize());
+
+        long storeTimestamp = 0;
+        try {
+            List<ByteBuffer> messageBufferList = getMessageResult.getMessageBufferList();
+            for (ByteBuffer bb : messageBufferList) {
+
+                byteBuffer.put(bb);
+                int sysFlag = bb.getInt(MessageDecoder.SYSFLAG_POSITION);
+//                bornhost has the IPv4 ip if the MessageSysFlag.BORNHOST_V6_FLAG bit of sysFlag is 0
+//                IPv4 host = ip(4 byte) + port(4 byte); IPv6 host = ip(16 byte) + port(4 byte)
+                int bornhostLength = (sysFlag & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 8 : 20;
+                int msgStoreTimePos = 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
+                storeTimestamp = bb.getLong(msgStoreTimePos);
+            }
+        } finally {
+            getMessageResult.release();
+        }
+
+        this.brokerController.getBrokerStatsManager().recordDiskFallBehindTime(group, topic, queueId, this.brokerController.getMessageStore().now() - storeTimestamp);
+        return byteBuffer.array();
+    }
+
+    protected void generateOffsetMovedEvent(final OffsetMovedEvent event) {
+        try {
+            MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
+            msgInner.setTopic(TopicValidator.RMQ_SYS_OFFSET_MOVED_EVENT);
+            msgInner.setTags(event.getConsumerGroup());
+            msgInner.setDelayTimeLevel(0);
+            msgInner.setKeys(event.getConsumerGroup());
+            msgInner.setBody(event.encode());
+            msgInner.setFlag(0);
+            msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
+            msgInner.setTagsCode(MessageExtBrokerInner.tagsString2tagsCode(TopicFilterType.SINGLE_TAG, msgInner.getTags()));
+
+            msgInner.setQueueId(0);
+            msgInner.setSysFlag(0);
+            msgInner.setBornTimestamp(System.currentTimeMillis());
+            msgInner.setBornHost(RemotingUtil.string2SocketAddress(this.brokerController.getBrokerAddr()));
+            msgInner.setStoreHost(msgInner.getBornHost());
+
+            msgInner.setReconsumeTimes(0);
+
+            PutMessageResult putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
+        } catch (Exception e) {
+            log.warn(String.format("generateOffsetMovedEvent Exception, %s", event.toString()), e);
+        }
+    }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
index 41e7df3..8ac52a4 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/EndTransactionProcessor.java
@@ -32,17 +32,16 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 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.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.config.BrokerRole;
 
 /**
  * EndTransaction processor: process commit and rollback message
  */
-public class EndTransactionProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
+public class EndTransactionProcessor implements NettyRequestProcessor {
     private static final InternalLogger LOGGER = InternalLoggerFactory.getLogger(LoggerName.TRANSACTION_LOGGER_NAME);
     private final BrokerController brokerController;
 
@@ -231,7 +230,7 @@ public class EndTransactionProcessor extends AsyncNettyRequestProcessor implemen
                     response.setRemark(null);
                     break;
                 // Failed
-                case CREATE_MAPEDFILE_FAILED:
+                case CREATE_MAPPED_FILE_FAILED:
                     response.setCode(ResponseCode.SYSTEM_ERROR);
                     response.setRemark("Create mapped file failed.");
                     break;
@@ -244,7 +243,7 @@ public class EndTransactionProcessor extends AsyncNettyRequestProcessor implemen
                     response.setCode(ResponseCode.SERVICE_NOT_AVAILABLE);
                     response.setRemark("Service not available now.");
                     break;
-                case OS_PAGECACHE_BUSY:
+                case OS_PAGE_CACHE_BUSY:
                     response.setCode(ResponseCode.SYSTEM_ERROR);
                     response.setRemark("OS page cache busy, please try another machine");
                     break;
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/ForwardRequestProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/ForwardRequestProcessor.java
index cd93598..b0f0a05 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/ForwardRequestProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/ForwardRequestProcessor.java
@@ -21,11 +21,10 @@ import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.common.constant.LoggerName;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
-import org.apache.rocketmq.remoting.netty.AsyncNettyRequestProcessor;
 import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 
-public class ForwardRequestProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
+public class ForwardRequestProcessor implements NettyRequestProcessor {
     private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
 
     private final BrokerController brokerController;
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/NotificationProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/NotificationProcessor.java
new file mode 100644
index 0000000..e007367
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/NotificationProcessor.java
@@ -0,0 +1,318 @@
+/*
+ * 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.broker.processor;
+
+import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.longpolling.NotificationRequest;
+import org.apache.rocketmq.common.AbstractBrokerRunnable;
+import org.apache.rocketmq.common.KeyBuilder;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.header.NotificationRequestHeader;
+import org.apache.rocketmq.common.protocol.header.NotificationResponseHeader;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.apache.rocketmq.remoting.common.RemotingHelper;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
+import org.apache.rocketmq.remoting.netty.RequestTask;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+
+public class NotificationProcessor implements NettyRequestProcessor {
+    private static final InternalLogger POP_LOGGER = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
+    private final BrokerController brokerController;
+    private Random random = new Random(System.currentTimeMillis());
+    private static final String BORN_TIME = "bornTime";
+    private ConcurrentLinkedHashMap<String, ArrayBlockingQueue<NotificationRequest>> pollingMap = new ConcurrentLinkedHashMap.Builder<String, ArrayBlockingQueue<NotificationRequest>>().maximumWeightedCapacity(100000).build();
+    private Thread checkNotificationPollingThread;
+
+    public NotificationProcessor(final BrokerController brokerController) {
+        this.brokerController = brokerController;
+        this.checkNotificationPollingThread = new Thread(new AbstractBrokerRunnable(brokerController.getBrokerConfig()) {
+            @Override public void run2() {
+                while (true) {
+                    if (Thread.currentThread().isInterrupted()) {
+                        break;
+                    }
+                    try {
+                        Thread.sleep(2000L);
+                        Collection<ArrayBlockingQueue<NotificationRequest>> pops = pollingMap.values();
+                        for (ArrayBlockingQueue<NotificationRequest> popQ : pops) {
+                            NotificationRequest tmPopRequest = popQ.peek();
+                            while (tmPopRequest != null) {
+                                if (tmPopRequest.isTimeout()) {
+                                    tmPopRequest = popQ.poll();
+                                    if (tmPopRequest == null) {
+                                        break;
+                                    }
+                                    if (!tmPopRequest.isTimeout()) {
+                                        POP_LOGGER.info("not timeout , but wakeUp Notification in advance: {}", tmPopRequest);
+                                        wakeUp(tmPopRequest, false);
+                                        break;
+                                    } else {
+                                        POP_LOGGER.info("timeout , wakeUp Notification : {}", tmPopRequest);
+                                        wakeUp(tmPopRequest, false);
+                                        tmPopRequest = popQ.peek();
+                                    }
+                                } else {
+                                    break;
+                                }
+                            }
+                        }
+                    } catch (InterruptedException e) {
+                        break;
+                    } catch (Exception e) {
+                        POP_LOGGER.error("checkNotificationPolling error", e);
+                    }
+                }
+            }
+        });
+        this.checkNotificationPollingThread.setDaemon(true);
+        this.checkNotificationPollingThread.setName("checkNotificationPolling");
+        this.checkNotificationPollingThread.start();
+    }
+
+    public void shutdown() {
+        this.checkNotificationPollingThread.interrupt();
+    }
+
+    @Override
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        request.addExtField(BORN_TIME, String.valueOf(System.currentTimeMillis()));
+        return this.processRequest(ctx.channel(), request);
+    }
+
+    @Override
+    public boolean rejectRequest() {
+        return false;
+    }
+
+    public void notifyMessageArriving(final String topic, final int queueId) {
+        ArrayBlockingQueue<NotificationRequest> remotingCommands = pollingMap.get(KeyBuilder.buildPollingNotificationKey(topic, -1));
+        if (remotingCommands != null) {
+            List<NotificationRequest> c = new ArrayList<>();
+            remotingCommands.drainTo(c);
+            for (NotificationRequest notificationRequest : c) {
+                POP_LOGGER.info("new msg arrive , wakeUp : {}", notificationRequest);
+                wakeUp(notificationRequest, true);
+
+            }
+        }
+        remotingCommands = pollingMap.get(KeyBuilder.buildPollingNotificationKey(topic, queueId));
+        if (remotingCommands != null) {
+            List<NotificationRequest> c = new ArrayList<>();
+            remotingCommands.drainTo(c);
+            for (NotificationRequest notificationRequest : c) {
+                POP_LOGGER.info("new msg arrive , wakeUp : {}", notificationRequest);
+                wakeUp(notificationRequest, true);
+            }
+        }
+    }
+
+    private void wakeUp(final NotificationRequest request, final boolean hasMsg) {
+        if (request == null || !request.complete()) {
+            return;
+        }
+        if (!request.getChannel().isActive()) {
+            return;
+        }
+        Runnable run = new Runnable() {
+            @Override
+            public void run() {
+                final RemotingCommand response = NotificationProcessor.this.responseNotification(request.getChannel(), hasMsg);
+
+                if (response != null) {
+                    response.setOpaque(request.getRemotingCommand().getOpaque());
+                    response.markResponseType();
+                    try {
+                        request.getChannel().writeAndFlush(response).addListener(new ChannelFutureListener() {
+                            @Override
+                            public void operationComplete(ChannelFuture future) throws Exception {
+                                if (!future.isSuccess()) {
+                                    POP_LOGGER.error("ProcessRequestWrapper response to {} failed", future.channel().remoteAddress(), future.cause());
+                                    POP_LOGGER.error(request.toString());
+                                    POP_LOGGER.error(response.toString());
+                                }
+                            }
+                        });
+                    } catch (Throwable e) {
+                        POP_LOGGER.error("ProcessRequestWrapper process request over, but response failed", e);
+                        POP_LOGGER.error(request.toString());
+                        POP_LOGGER.error(response.toString());
+                    }
+                }
+            }
+        };
+        this.brokerController.getPullMessageExecutor().submit(new RequestTask(run, request.getChannel(), request.getRemotingCommand()));
+    }
+
+    public RemotingCommand responseNotification(final Channel channel, boolean hasMsg) {
+        RemotingCommand response = RemotingCommand.createResponseCommand(NotificationResponseHeader.class);
+        final NotificationResponseHeader responseHeader = (NotificationResponseHeader) response.readCustomHeader();
+        responseHeader.setHasMsg(hasMsg);
+        response.setCode(ResponseCode.SUCCESS);
+        return response;
+    }
+
+    private RemotingCommand processRequest(final Channel channel, RemotingCommand request)
+        throws RemotingCommandException {
+        RemotingCommand response = RemotingCommand.createResponseCommand(NotificationResponseHeader.class);
+        final NotificationResponseHeader responseHeader = (NotificationResponseHeader) response.readCustomHeader();
+        final NotificationRequestHeader requestHeader =
+            (NotificationRequestHeader) request.decodeCommandCustomHeader(NotificationRequestHeader.class);
+
+        response.setOpaque(request.getOpaque());
+
+        if (!PermName.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark(String.format("the broker[%s] peeking message is forbidden", this.brokerController.getBrokerConfig().getBrokerIP1()));
+            return response;
+        }
+
+        TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
+        if (null == topicConfig) {
+            POP_LOGGER.error("The topic {} not exist, consumer: {} ", requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(channel));
+            response.setCode(ResponseCode.TOPIC_NOT_EXIST);
+            response.setRemark(String.format("topic[%s] not exist, apply first please! %s", requestHeader.getTopic(), FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
+            return response;
+        }
+
+        if (!PermName.isReadable(topicConfig.getPerm())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("the topic[" + requestHeader.getTopic() + "] peeking message is forbidden");
+            return response;
+        }
+
+        if (requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
+            String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
+                requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(), channel.remoteAddress());
+            POP_LOGGER.warn(errorInfo);
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark(errorInfo);
+            return response;
+        }
+        SubscriptionGroupConfig subscriptionGroupConfig = this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
+        if (null == subscriptionGroupConfig) {
+            response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+            response.setRemark(String.format("subscription group [%s] does not exist, %s", requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
+            return response;
+        }
+
+        if (!subscriptionGroupConfig.isConsumeEnable()) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("subscription group no permission, " + requestHeader.getConsumerGroup());
+            return response;
+        }
+        int randomQ = random.nextInt(100);
+        boolean hasMsg = false;
+        boolean needRetry = randomQ % 5 == 0;
+        if (needRetry) {
+            TopicConfig retryTopicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
+            if (retryTopicConfig != null) {
+                for (int i = 0; i < retryTopicConfig.getReadQueueNums(); i++) {
+                    int queueId = (randomQ + i) % retryTopicConfig.getReadQueueNums();
+                    hasMsg = hasMsgFromQueue(true, requestHeader, queueId);
+                }
+            }
+        }
+        if (!hasMsg && requestHeader.getQueueId() < 0) {
+            // read all queue
+            for (int i = 0; i < topicConfig.getReadQueueNums(); i++) {
+                int queueId = (randomQ + i) % topicConfig.getReadQueueNums();
+                hasMsg = hasMsgFromQueue(false, requestHeader, queueId);
+                if (hasMsg) {
+                    break;
+                }
+            }
+        } else {
+            int queueId = requestHeader.getQueueId();
+            hasMsg = hasMsgFromQueue(false, requestHeader, queueId);
+        }
+
+        if (!hasMsg) {
+            if (polling(channel, request, requestHeader)) {
+                return null;
+            }
+        }
+        response.setCode(ResponseCode.SUCCESS);
+        responseHeader.setHasMsg(hasMsg);
+        return response;
+    }
+
+    private boolean hasMsgFromQueue(boolean isRetry, NotificationRequestHeader requestHeader, int queueId) {
+        String topic = isRetry ? KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()) : requestHeader.getTopic();
+        long offset = getPopOffset(topic, requestHeader.getConsumerGroup(), queueId);
+        long restNum = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset;
+        return restNum > 0;
+    }
+
+    private long getPopOffset(String topic, String cid, int queueId) {
+        long offset = this.brokerController.getConsumerOffsetManager().queryOffset(cid, topic, queueId);
+        if (offset < 0) {
+            offset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
+        }
+        long bufferOffset = this.brokerController.getPopMessageProcessor().getPopBufferMergeService()
+            .getLatestOffset(topic, cid, queueId);
+        if (bufferOffset < 0) {
+            return offset;
+        } else {
+            return bufferOffset > offset ? bufferOffset : offset;
+        }
+    }
+
+    private boolean polling(final Channel channel, RemotingCommand remotingCommand,
+        final NotificationRequestHeader requestHeader) {
+        if (requestHeader.getPollTime() <= 0) {
+            return false;
+        }
+
+        long expired = requestHeader.getBornTime() + requestHeader.getPollTime();
+        final NotificationRequest request = new NotificationRequest(remotingCommand, channel, expired);
+        boolean result = false;
+        if (!request.isTimeout()) {
+            String key = KeyBuilder.buildPollingNotificationKey(requestHeader.getTopic(), requestHeader.getQueueId());
+            ArrayBlockingQueue<NotificationRequest> queue = pollingMap.get(key);
+            if (queue == null) {
+                queue = new ArrayBlockingQueue<>(this.brokerController.getBrokerConfig().getPopPollingSize());
+                pollingMap.put(key, queue);
+                result = queue.offer(request);
+            } else {
+                result = queue.offer(request);
+            }
+        }
+        POP_LOGGER.info("polling {}, result {}", remotingCommand, result);
+        return result;
+
+    }
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PeekMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PeekMessageProcessor.java
new file mode 100644
index 0000000..7cc15f2
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PeekMessageProcessor.java
@@ -0,0 +1,271 @@
+/*
+ * 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.broker.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.FileRegion;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.pagecache.ManyMessageTransfer;
+import org.apache.rocketmq.common.KeyBuilder;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.message.MessageDecoder;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.header.PeekMessageRequestHeader;
+import org.apache.rocketmq.common.protocol.header.PopMessageResponseHeader;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.apache.rocketmq.remoting.common.RemotingHelper;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+import org.apache.rocketmq.store.GetMessageResult;
+import org.apache.rocketmq.store.GetMessageStatus;
+import org.apache.rocketmq.store.SelectMappedBufferResult;
+
+public class PeekMessageProcessor implements NettyRequestProcessor {
+    private static final InternalLogger LOG = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
+    private final BrokerController brokerController;
+    private Random random = new Random(System.currentTimeMillis());
+
+    public PeekMessageProcessor(final BrokerController brokerController) {
+        this.brokerController = brokerController;
+    }
+
+    @Override
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        return this.processRequest(ctx.channel(), request, true);
+    }
+
+    @Override
+    public boolean rejectRequest() {
+        return false;
+    }
+
+    private RemotingCommand processRequest(final Channel channel, RemotingCommand request, boolean brokerAllowSuspend)
+        throws RemotingCommandException {
+        RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
+        final PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
+        final PeekMessageRequestHeader requestHeader =
+            (PeekMessageRequestHeader) request.decodeCommandCustomHeader(PeekMessageRequestHeader.class);
+
+        response.setOpaque(request.getOpaque());
+
+        if (!PermName.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark(String.format("the broker[%s] peeking message is forbidden", this.brokerController.getBrokerConfig().getBrokerIP1()));
+            return response;
+        }
+
+        TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
+        if (null == topicConfig) {
+            LOG.error("The topic {} not exist, consumer: {} ", requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(channel));
+            response.setCode(ResponseCode.TOPIC_NOT_EXIST);
+            response.setRemark(String.format("topic[%s] not exist, apply first please! %s", requestHeader.getTopic(), FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
+            return response;
+        }
+
+        if (!PermName.isReadable(topicConfig.getPerm())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("the topic[" + requestHeader.getTopic() + "] peeking message is forbidden");
+            return response;
+        }
+
+        if (requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
+            String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
+                requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(), channel.remoteAddress());
+            LOG.warn(errorInfo);
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark(errorInfo);
+            return response;
+        }
+        SubscriptionGroupConfig subscriptionGroupConfig = this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
+        if (null == subscriptionGroupConfig) {
+            response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+            response.setRemark(String.format("subscription group [%s] does not exist, %s", requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
+            return response;
+        }
+
+        if (!subscriptionGroupConfig.isConsumeEnable()) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("subscription group no permission, " + requestHeader.getConsumerGroup());
+            return response;
+        }
+        int randomQ = random.nextInt(100);
+        int reviveQid = randomQ % this.brokerController.getBrokerConfig().getReviveQueueNum();
+        int commercialSizePerMsg = this.brokerController.getBrokerConfig().getCommercialSizePerMsg();
+        GetMessageResult getMessageResult = new GetMessageResult(commercialSizePerMsg);
+        boolean needRetry = randomQ % 5 == 0;
+        long popTime = System.currentTimeMillis();
+        long restNum = 0;
+        if (needRetry) {
+            TopicConfig retryTopicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
+            if (retryTopicConfig != null) {
+                for (int i = 0; i < retryTopicConfig.getReadQueueNums(); i++) {
+                    int queueId = (randomQ + i) % retryTopicConfig.getReadQueueNums();
+                    restNum = peekMsgFromQueue(true, getMessageResult, requestHeader, queueId, restNum, reviveQid, channel, popTime);
+                }
+            }
+        }
+        if (requestHeader.getQueueId() < 0) {
+            // read all queue
+            for (int i = 0; i < topicConfig.getReadQueueNums(); i++) {
+                int queueId = (randomQ + i) % topicConfig.getReadQueueNums();
+                restNum = peekMsgFromQueue(false, getMessageResult, requestHeader, queueId, restNum, reviveQid, channel, popTime);
+            }
+        } else {
+            int queueId = requestHeader.getQueueId();
+            restNum = peekMsgFromQueue(false, getMessageResult, requestHeader, queueId, restNum, reviveQid, channel, popTime);
+        }
+        // if not full , fetch retry again
+        if (!needRetry && getMessageResult.getMessageMapedList().size() < requestHeader.getMaxMsgNums()) {
+            TopicConfig retryTopicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
+            if (retryTopicConfig != null) {
+                for (int i = 0; i < retryTopicConfig.getReadQueueNums(); i++) {
+                    int queueId = (randomQ + i) % retryTopicConfig.getReadQueueNums();
+                    restNum = peekMsgFromQueue(true, getMessageResult, requestHeader, queueId, restNum, reviveQid, channel, popTime);
+                }
+            }
+        }
+        if (!getMessageResult.getMessageBufferList().isEmpty()) {
+            response.setCode(ResponseCode.SUCCESS);
+            getMessageResult.setStatus(GetMessageStatus.FOUND);
+        } else {
+            response.setCode(ResponseCode.PULL_NOT_FOUND);
+            getMessageResult.setStatus(GetMessageStatus.NO_MESSAGE_IN_QUEUE);
+
+        }
+        responseHeader.setRestNum(restNum);
+        response.setRemark(getMessageResult.getStatus().name());
+        switch (response.getCode()) {
+            case ResponseCode.SUCCESS:
+
+                this.brokerController.getBrokerStatsManager().incGroupGetNums(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
+                    getMessageResult.getMessageCount());
+
+                this.brokerController.getBrokerStatsManager().incGroupGetSize(requestHeader.getConsumerGroup(), requestHeader.getTopic(),
+                    getMessageResult.getBufferTotalSize());
+
+                this.brokerController.getBrokerStatsManager().incBrokerGetNums(getMessageResult.getMessageCount());
+                if (this.brokerController.getBrokerConfig().isTransferMsgByHeap()) {
+                    final long beginTimeMills = this.brokerController.getMessageStore().now();
+                    final byte[] r = this.readGetMessageResult(getMessageResult, requestHeader.getConsumerGroup(), requestHeader.getTopic(), requestHeader.getQueueId());
+                    this.brokerController.getBrokerStatsManager().incGroupGetLatency(requestHeader.getConsumerGroup(),
+                        requestHeader.getTopic(), requestHeader.getQueueId(),
+                        (int) (this.brokerController.getMessageStore().now() - beginTimeMills));
+                    response.setBody(r);
+                } else {
+                    final GetMessageResult tmpGetMessageResult = getMessageResult;
+                    try {
+                        FileRegion fileRegion =
+                            new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()), getMessageResult);
+                        channel.writeAndFlush(fileRegion).addListener(new ChannelFutureListener() {
+                            @Override
+                            public void operationComplete(ChannelFuture future) throws Exception {
+                                tmpGetMessageResult.release();
+                                if (!future.isSuccess()) {
+                                    LOG.error("Fail to transfer messages from page cache to {}", channel.remoteAddress(), future.cause());
+                                }
+                            }
+                        });
+                    } catch (Throwable e) {
+                        LOG.error("Error occurred when transferring messages from page cache", e);
+                        getMessageResult.release();
+                    }
+
+                    response = null;
+                }
+                break;
+            default:
+                assert false;
+        }
+        return response;
+    }
+
+    private long peekMsgFromQueue(boolean isRetry, GetMessageResult getMessageResult,
+        PeekMessageRequestHeader requestHeader, int queueId, long restNum, int reviveQid, Channel channel,
+        long popTime) {
+        String topic = isRetry ? KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()) : requestHeader.getTopic();
+        GetMessageResult getMessageTmpResult;
+        long offset = getPopOffset(topic, requestHeader.getConsumerGroup(), queueId);
+        restNum = this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum;
+        if (getMessageResult.getMessageMapedList().size() >= requestHeader.getMaxMsgNums()) {
+            return restNum;
+        }
+        getMessageTmpResult = this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), topic, queueId, offset,
+            requestHeader.getMaxMsgNums() - getMessageResult.getMessageMapedList().size(), null);
+        // maybe store offset is not correct.
+        if (GetMessageStatus.OFFSET_TOO_SMALL.equals(getMessageTmpResult.getStatus()) || GetMessageStatus.OFFSET_OVERFLOW_BADLY.equals(getMessageTmpResult.getStatus())) {
+            offset = getMessageTmpResult.getNextBeginOffset();
+            getMessageTmpResult = this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), topic, queueId, offset,
+                requestHeader.getMaxMsgNums() - getMessageResult.getMessageMapedList().size(), null);
+        }
+        if (getMessageTmpResult != null) {
+            for (SelectMappedBufferResult mapedBuffer : getMessageTmpResult.getMessageMapedList()) {
+                getMessageResult.addMessage(mapedBuffer);
+            }
+        }
+        return restNum;
+    }
+
+    private long getPopOffset(String topic, String cid, int queueId) {
+        long offset = this.brokerController.getConsumerOffsetManager().queryOffset(cid, topic, queueId);
+        if (offset < 0) {
+            offset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
+        }
+        long bufferOffset = this.brokerController.getPopMessageProcessor().getPopBufferMergeService()
+            .getLatestOffset(topic, cid, queueId);
+        if (bufferOffset < 0) {
+            return offset;
+        } else {
+            return bufferOffset > offset ? bufferOffset : offset;
+        }
+    }
+
+    private byte[] readGetMessageResult(final GetMessageResult getMessageResult, final String group, final String topic,
+        final int queueId) {
+        final ByteBuffer byteBuffer = ByteBuffer.allocate(getMessageResult.getBufferTotalSize());
+
+        long storeTimestamp = 0;
+        try {
+            List<ByteBuffer> messageBufferList = getMessageResult.getMessageBufferList();
+            for (ByteBuffer bb : messageBufferList) {
+
+                byteBuffer.put(bb);
+                storeTimestamp = bb.getLong(MessageDecoder.MESSAGE_STORE_TIMESTAMP_POSITION);
+            }
+        } finally {
+            getMessageResult.release();
+        }
+
+        this.brokerController.getBrokerStatsManager().recordDiskFallBehindTime(group, topic, queueId, this.brokerController.getMessageStore().now() - storeTimestamp);
+        return byteBuffer.array();
+    }
+
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PollingInfoProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PollingInfoProcessor.java
new file mode 100644
index 0000000..827a8d6
--- /dev/null
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PollingInfoProcessor.java
@@ -0,0 +1,119 @@
+/*
+ * 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.broker.processor;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import java.util.concurrent.ConcurrentSkipListSet;
+import org.apache.rocketmq.broker.BrokerController;
+import org.apache.rocketmq.broker.longpolling.PopRequest;
+import org.apache.rocketmq.common.KeyBuilder;
+import org.apache.rocketmq.common.TopicConfig;
+import org.apache.rocketmq.common.constant.LoggerName;
+import org.apache.rocketmq.common.constant.PermName;
+import org.apache.rocketmq.common.help.FAQUrl;
+import org.apache.rocketmq.common.protocol.ResponseCode;
+import org.apache.rocketmq.common.protocol.header.PollingInfoRequestHeader;
+import org.apache.rocketmq.common.protocol.header.PollingInfoResponseHeader;
+import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
+import org.apache.rocketmq.logging.InternalLogger;
+import org.apache.rocketmq.logging.InternalLoggerFactory;
+import org.apache.rocketmq.remoting.common.RemotingHelper;
+import org.apache.rocketmq.remoting.exception.RemotingCommandException;
+import org.apache.rocketmq.remoting.netty.NettyRequestProcessor;
+import org.apache.rocketmq.remoting.protocol.RemotingCommand;
+
+public class PollingInfoProcessor implements NettyRequestProcessor {
+    private static final InternalLogger POP_LOGGER = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
+    private final BrokerController brokerController;
+
+    public PollingInfoProcessor(final BrokerController brokerController) {
+        this.brokerController = brokerController;
+    }
+
+    @Override
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
+        return this.processRequest(ctx.channel(), request);
+    }
+
+    @Override
+    public boolean rejectRequest() {
+        return false;
+    }
+
+    private RemotingCommand processRequest(final Channel channel, RemotingCommand request)
+        throws RemotingCommandException {
+        RemotingCommand response = RemotingCommand.createResponseCommand(PollingInfoResponseHeader.class);
+        final PollingInfoResponseHeader responseHeader = (PollingInfoResponseHeader) response.readCustomHeader();
+        final PollingInfoRequestHeader requestHeader =
+            (PollingInfoRequestHeader) request.decodeCommandCustomHeader(PollingInfoRequestHeader.class);
+
+        response.setOpaque(request.getOpaque());
+
+        if (!PermName.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark(String.format("the broker[%s] peeking message is forbidden", this.brokerController.getBrokerConfig().getBrokerIP1()));
+            return response;
+        }
+
+        TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
+        if (null == topicConfig) {
+            POP_LOGGER.error("The topic {} not exist, consumer: {} ", requestHeader.getTopic(), RemotingHelper.parseChannelRemoteAddr(channel));
+            response.setCode(ResponseCode.TOPIC_NOT_EXIST);
+            response.setRemark(String.format("topic[%s] not exist, apply first please! %s", requestHeader.getTopic(), FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
+            return response;
+        }
+
+        if (!PermName.isReadable(topicConfig.getPerm())) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("the topic[" + requestHeader.getTopic() + "] peeking message is forbidden");
+            return response;
+        }
+
+        if (requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
+            String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] consumer:[%s]",
+                requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(), channel.remoteAddress());
+            POP_LOGGER.warn(errorInfo);
+            response.setCode(ResponseCode.SYSTEM_ERROR);
+            response.setRemark(errorInfo);
+            return response;
+        }
+        SubscriptionGroupConfig subscriptionGroupConfig = this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
+        if (null == subscriptionGroupConfig) {
+            response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
+            response.setRemark(String.format("subscription group [%s] does not exist, %s", requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
+            return response;
+        }
+
+        if (!subscriptionGroupConfig.isConsumeEnable()) {
+            response.setCode(ResponseCode.NO_PERMISSION);
+            response.setRemark("subscription group no permission, " + requestHeader.getConsumerGroup());
+            return response;
+        }
+        String key = KeyBuilder.buildPollingKey(requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getQueueId());
+        ConcurrentSkipListSet<PopRequest> queue = this.brokerController.getPopMessageProcessor().getPollingMap().get(key);
+        if (queue != null) {
+            responseHeader.setPollingNum(queue.size());
+        } else {
+            responseHeader.setPollingNum(0);
+        }
+        response.setCode(ResponseCode.SUCCESS);
+        return response;
+    }
+
+}
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java
index 615a70e..cee84e6 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopBufferMergeService.java
@@ -33,7 +33,7 @@ import org.apache.rocketmq.common.message.MessageDecoder;
 import org.apache.rocketmq.common.utils.DataConverter;
 import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.PutMessageResult;
 import org.apache.rocketmq.store.PutMessageStatus;
 import org.apache.rocketmq.store.config.BrokerRole;
@@ -43,9 +43,9 @@ import org.apache.rocketmq.store.pop.PopCheckPoint;
 public class PopBufferMergeService extends ServiceThread {
     private static final InternalLogger POP_LOGGER = InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
     ConcurrentHashMap<String/*mergeKey*/, PopCheckPointWrapper>
-            buffer = new ConcurrentHashMap<>(1024 * 16);
+        buffer = new ConcurrentHashMap<>(1024 * 16);
     ConcurrentHashMap<String/*topic@cid@queueId*/, QueueWithTime<PopCheckPointWrapper>> commitOffsets =
-            new ConcurrentHashMap<>();
+        new ConcurrentHashMap<>();
     private volatile boolean serving = true;
     private AtomicInteger counter = new AtomicInteger(0);
     private int scanTimes = 0;
@@ -61,20 +61,25 @@ public class PopBufferMergeService extends ServiceThread {
     private volatile boolean master = false;
 
     public PopBufferMergeService(BrokerController brokerController, PopMessageProcessor popMessageProcessor) {
-        super();
         this.brokerController = brokerController;
         this.popMessageProcessor = popMessageProcessor;
         this.queueLockManager = popMessageProcessor.getQueueLockManager();
     }
 
-    private boolean checkAndSetMaster() {
+    private boolean isShouldRunning() {
+        if (this.brokerController.getBrokerConfig().isEnableSlaveActingMaster()) {
+            return true;
+        }
         this.master = brokerController.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE;
         return this.master;
     }
 
     @Override
     public String getServiceName() {
-        return "PopBufferMergeService";
+        if (this.brokerController != null && this.brokerController.getBrokerConfig().isInBrokerContainer()) {
+            return brokerController.getBrokerConfig().getLoggerIdentifier() + PopBufferMergeService.class.getSimpleName();
+        }
+        return PopBufferMergeService.class.getSimpleName();
     }
 
     @Override
@@ -82,11 +87,11 @@ public class PopBufferMergeService extends ServiceThread {
         // scan
         while (!this.isStopped()) {
             try {
-                if (!checkAndSetMaster()) {
+                if (!isShouldRunning()) {
                     // slave
                     this.waitForRunning(interval * 200 * 5);
                     POP_LOGGER.info("Broker is {}, {}, clear all data",
-                            brokerController.getMessageStoreConfig().getBrokerRole(), this.master);
+                        brokerController.getMessageStoreConfig().getBrokerRole(), this.master);
                     this.buffer.clear();
                     this.commitOffsets.clear();
                     continue;
@@ -113,7 +118,7 @@ public class PopBufferMergeService extends ServiceThread {
             Thread.sleep(2000);
         } catch (InterruptedException e) {
         }
-        if (!checkAndSetMaster()) {
+        if (!isShouldRunning()) {
             return;
         }
         while (this.buffer.size() > 0 || totalSize() > 0) {
@@ -133,7 +138,7 @@ public class PopBufferMergeService extends ServiceThread {
                 // 2. ck is buffer(acked)
                 // 3. ck is buffer(not all acked), all ak are stored and ck is stored
                 if ((pointWrapper.isJustOffset() && pointWrapper.isCkStored()) || isCkDone(pointWrapper)
-                        || (isCkDoneForFinish(pointWrapper) && pointWrapper.isCkStored())) {
+                    || (isCkDoneForFinish(pointWrapper) && pointWrapper.isCkStored())) {
                     if (commitOffset(pointWrapper)) {
                         queue.poll();
                     } else {
@@ -141,7 +146,7 @@ public class PopBufferMergeService extends ServiceThread {
                     }
                 } else {
                     if (System.currentTimeMillis() - pointWrapper.getCk().getPopTime()
-                            > brokerController.getBrokerConfig().getPopCkStayBufferTime() * 2) {
+                        > brokerController.getBrokerConfig().getPopCkStayBufferTime() * 2) {
                         POP_LOGGER.warn("[PopBuffer] ck offset long time not commit, {}", pointWrapper);
                     }
                     break;
@@ -151,7 +156,7 @@ public class PopBufferMergeService extends ServiceThread {
             count += qs;
             if (qs > 5000 && scanTimes % countOfSecond1 == 0) {
                 POP_LOGGER.info("[PopBuffer] offset queue size too long, {}, {}",
-                        entry.getKey(), qs);
+                    entry.getKey(), qs);
             }
         }
         return count;
@@ -214,7 +219,7 @@ public class PopBufferMergeService extends ServiceThread {
 
             // just process offset(already stored at pull thread), or buffer ck(not stored and ack finish)
             if ((pointWrapper.isJustOffset() && pointWrapper.isCkStored()) || isCkDone(pointWrapper)
-                    || (isCkDoneForFinish(pointWrapper) && pointWrapper.isCkStored())) {
+                || (isCkDoneForFinish(pointWrapper) && pointWrapper.isCkStored())) {
                 if (brokerController.getBrokerConfig().isEnablePopLog()) {
                     POP_LOGGER.info("[PopBuffer]ck done, {}", pointWrapper);
                 }
@@ -226,7 +231,7 @@ public class PopBufferMergeService extends ServiceThread {
             PopCheckPoint point = pointWrapper.getCk();
             long now = System.currentTimeMillis();
 
-            boolean removeCk = !this.serving;
+            boolean removeCk = this.serving ? false : true;
             // ck will be timeout
             if (point.getReviveTime() - now < brokerController.getBrokerConfig().getPopCkStayBufferTimeOut()) {
                 removeCk = true;
@@ -265,7 +270,7 @@ public class PopBufferMergeService extends ServiceThread {
                 for (byte i = 0; i < point.getNum(); i++) {
                     // reput buffer ak to store
                     if (DataConverter.getBit(pointWrapper.getBits().get(), i)
-                            && !DataConverter.getBit(pointWrapper.getToStoreBits().get(), i)) {
+                        && !DataConverter.getBit(pointWrapper.getToStoreBits().get(), i)) {
                         if (putAckToStore(pointWrapper, i)) {
                             count++;
                             markBitCAS(pointWrapper.getToStoreBits(), i);
@@ -289,14 +294,14 @@ public class PopBufferMergeService extends ServiceThread {
         long eclipse = System.currentTimeMillis() - startTime;
         if (eclipse > brokerController.getBrokerConfig().getPopCkStayBufferTimeOut() - 1000) {
             POP_LOGGER.warn("[PopBuffer]scan stop, because eclipse too long, PopBufferEclipse={}, " +
-                            "PopBufferToStoreAck={}, PopBufferToStoreCk={}, PopBufferSize={}, PopBufferOffsetSize={}",
-                    eclipse, count, countCk, counter.get(), offsetBufferSize);
+                    "PopBufferToStoreAck={}, PopBufferToStoreCk={}, PopBufferSize={}, PopBufferOffsetSize={}",
+                eclipse, count, countCk, counter.get(), offsetBufferSize);
             this.serving = false;
         } else {
             if (scanTimes % countOfSecond1 == 0) {
                 POP_LOGGER.info("[PopBuffer]scan, PopBufferEclipse={}, " +
-                                "PopBufferToStoreAck={}, PopBufferToStoreCk={}, PopBufferSize={}, PopBufferOffsetSize={}",
-                        eclipse, count, countCk, counter.get(), offsetBufferSize);
+                        "PopBufferToStoreAck={}, PopBufferToStoreCk={}, PopBufferSize={}, PopBufferOffsetSize={}",
+                    eclipse, count, countCk, counter.get(), offsetBufferSize);
             }
         }
         scanTimes++;
@@ -354,7 +359,7 @@ public class PopBufferMergeService extends ServiceThread {
                 POP_LOGGER.warn("Commit offset, consumer offset less than store, {}, {}", wrapper, offset);
             }
             brokerController.getConsumerOffsetManager().commitOffset(getServiceName(),
-                    popCheckPoint.getCId(), popCheckPoint.getTopic(), popCheckPoint.getQueueId(), wrapper.getNextBeginOffset());
+                popCheckPoint.getCId(), popCheckPoint.getTopic(), popCheckPoint.getQueueId(), wrapper.getNextBeginOffset());
         } finally {
             queueLockManager.unLock(lockKey);
         }
@@ -384,6 +389,7 @@ public class PopBufferMergeService extends ServiceThread {
 
     /**
      * put to store && add to buffer.
+     *
      * @param point
      * @param reviveQueueId
      * @param reviveQueueOffset
@@ -404,7 +410,7 @@ public class PopBufferMergeService extends ServiceThread {
     }
 
     public void addCkMock(String group, String topic, int queueId, long startOffset, long invisibleTime,
-                          long popTime, int reviveQueueId, long nextBeginOffset) {
+        long popTime, int reviveQueueId, long nextBeginOffset, String brokerName) {
         final PopCheckPoint ck = new PopCheckPoint();
         ck.setBitMap(0);
         ck.setNum((byte) 0);
@@ -414,6 +420,7 @@ public class PopBufferMergeService extends ServiceThread {
         ck.setCId(group);
         ck.setTopic(topic);
         ck.setQueueId((byte) queueId);
+        ck.setBrokerName(brokerName);
 
         PopCheckPointWrapper pointWrapper = new PopCheckPointWrapper(reviveQueueId, Long.MAX_VALUE, ck, nextBeginOffset, true);
         pointWrapper.setCkStored(true);
@@ -469,7 +476,7 @@ public class PopBufferMergeService extends ServiceThread {
             return false;
         }
         try {
-            PopCheckPointWrapper pointWrapper = this.buffer.get(ackMsg.getTopic() + ackMsg.getConsumerGroup() + ackMsg.getQueueId() + ackMsg.getStartOffset() + ackMsg.getPopTime());
+            PopCheckPointWrapper pointWrapper = this.buffer.get(ackMsg.getTopic() + ackMsg.getConsumerGroup() + ackMsg.getQueueId() + ackMsg.getStartOffset() + ackMsg.getPopTime() + ackMsg.getBrokerName());
             if (pointWrapper == null) {
                 if (brokerController.getBrokerConfig().isEnablePopLog()) {
                     POP_LOGGER.warn("[PopBuffer]add ack fail, rqId={}, no ck, {}", reviveQid, ackMsg);
@@ -523,6 +530,10 @@ public class PopBufferMergeService extends ServiceThread {
         return false;
     }
 
+    public void clearOffsetQueue(String lockKey) {
+        this.commitOffsets.remove(lockKey);
+    }
+
     private void putCkToStore(final PopCheckPointWrapper pointWrapper, final boolean runInCurrent) {
         if (pointWrapper.getReviveQueueOffset() >= 0) {
             return;
@@ -530,9 +541,9 @@ public class PopBufferMergeService extends ServiceThread {
         MessageExtBrokerInner msgInner = popMessageProcessor.buildCkMsg(pointWrapper.getCk(), pointWrapper.getReviveQueueId());
         PutMessageResult putMessageResult = brokerController.getMessageStore().putMessage(msgInner);
         if (putMessageResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.SLAVE_NOT_AVAILABLE) {
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.SLAVE_NOT_AVAILABLE) {
             POP_LOGGER.error("[PopBuffer]put ck to store fail: {}, {}", pointWrapper, putMessageResult);
             return;
         }
@@ -568,9 +579,9 @@ public class PopBufferMergeService extends ServiceThread {
         msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
         PutMessageResult putMessageResult = brokerController.getMessageStore().putMessage(msgInner);
         if (putMessageResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
-                && putMessageResult.getPutMessageStatus() != PutMessageStatus.SLAVE_NOT_AVAILABLE) {
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.SLAVE_NOT_AVAILABLE) {
             POP_LOGGER.error("[PopBuffer]put ack to store fail: {}, {}, {}", pointWrapper, ackMsg, putMessageResult);
             return false;
         }
@@ -581,6 +592,38 @@ public class PopBufferMergeService extends ServiceThread {
         return true;
     }
 
+    private boolean cancelCkTimer(final PopCheckPointWrapper pointWrapper) {
+        // not stored, no need cancel
+        if (pointWrapper.getReviveQueueOffset() < 0) {
+            return true;
+        }
+        PopCheckPoint point = pointWrapper.getCk();
+        MessageExtBrokerInner msgInner = new MessageExtBrokerInner();
+        msgInner.setTopic(popMessageProcessor.reviveTopic);
+        msgInner.setBody((pointWrapper.getReviveQueueId() + "-" + pointWrapper.getReviveQueueOffset()).getBytes());
+        msgInner.setQueueId(pointWrapper.getReviveQueueId());
+        msgInner.setTags(PopAckConstants.CK_TAG);
+        msgInner.setBornTimestamp(System.currentTimeMillis());
+        msgInner.setBornHost(brokerController.getStoreHost());
+        msgInner.setStoreHost(brokerController.getStoreHost());
+
+        MsgUtil.setMessageDeliverTime(brokerController, msgInner, point.getReviveTime() - PopAckConstants.ackTimeInterval);
+
+        msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProperties()));
+        PutMessageResult putMessageResult = brokerController.getEscapeBridge().putMessageToSpecificQueue(msgInner);
+        if (putMessageResult.getPutMessageStatus() != PutMessageStatus.PUT_OK
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_DISK_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.FLUSH_SLAVE_TIMEOUT
+            && putMessageResult.getPutMessageStatus() != PutMessageStatus.SLAVE_NOT_AVAILABLE) {
+            POP_LOGGER.error("[PopBuffer]PutMessageCallback cancelCheckPoint fail, {}, {}", pointWrapper, putMessageResult);
+            return false;
+        }
+        if (brokerController.getBrokerConfig().isEnablePopLog()) {
+            POP_LOGGER.info("[PopBuffer]cancelCheckPoint, {}", pointWrapper);
+        }
+        return true;
+    }
+
     private boolean isCkDone(PopCheckPointWrapper pointWrapper) {
         byte num = pointWrapper.getCk().getNum();
         for (byte i = 0; i < num; i++) {
@@ -639,7 +682,8 @@ public class PopBufferMergeService extends ServiceThread {
         private final boolean justOffset;
         private volatile boolean ckStored = false;
 
-        public PopCheckPointWrapper(int reviveQueueId, long reviveQueueOffset, PopCheckPoint point, long nextBeginOffset) {
+        public PopCheckPointWrapper(int reviveQueueId, long reviveQueueOffset, PopCheckPoint point,
+            long nextBeginOffset) {
             this.reviveQueueId = reviveQueueId;
             this.reviveQueueOffset = reviveQueueOffset;
             this.ck = point;
@@ -647,12 +691,13 @@ public class PopBufferMergeService extends ServiceThread {
             this.toStoreBits = new AtomicInteger(0);
             this.nextBeginOffset = nextBeginOffset;
             this.lockKey = ck.getTopic() + PopAckConstants.SPLIT + ck.getCId() + PopAckConstants.SPLIT + ck.getQueueId();
-            this.mergeKey = point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime();
+            this.mergeKey = point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime() + point.getBrokerName();
             this.justOffset = false;
         }
 
-        public PopCheckPointWrapper(int reviveQueueId, long reviveQueueOffset, PopCheckPoint point, long nextBeginOffset,
-                                    boolean justOffset) {
+        public PopCheckPointWrapper(int reviveQueueId, long reviveQueueOffset, PopCheckPoint point,
+            long nextBeginOffset,
+            boolean justOffset) {
             this.reviveQueueId = reviveQueueId;
             this.reviveQueueOffset = reviveQueueOffset;
             this.ck = point;
@@ -660,7 +705,7 @@ public class PopBufferMergeService extends ServiceThread {
             this.toStoreBits = new AtomicInteger(0);
             this.nextBeginOffset = nextBeginOffset;
             this.lockKey = ck.getTopic() + PopAckConstants.SPLIT + ck.getCId() + PopAckConstants.SPLIT + ck.getQueueId();
-            this.mergeKey = point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime();
+            this.mergeKey = point.getTopic() + point.getCId() + point.getQueueId() + point.getStartOffset() + point.getPopTime() + point.getBrokerName();
             this.justOffset = justOffset;
         }
 
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
index fcc972d..c9f4485 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopMessageProcessor.java
@@ -68,14 +68,14 @@ import org.apache.rocketmq.remoting.netty.RequestTask;
 import org.apache.rocketmq.remoting.protocol.RemotingCommand;
 import org.apache.rocketmq.store.GetMessageResult;
 import org.apache.rocketmq.store.GetMessageStatus;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.SelectMappedBufferResult;
 import org.apache.rocketmq.store.pop.AckMsg;
 import org.apache.rocketmq.store.pop.PopCheckPoint;
 
 public class PopMessageProcessor implements NettyRequestProcessor {
     private static final InternalLogger POP_LOGGER =
-            InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
+        InternalLoggerFactory.getLogger(LoggerName.ROCKETMQ_POP_LOGGER_NAME);
     private final BrokerController brokerController;
     private Random random = new Random(System.currentTimeMillis());
     String reviveTopic;
@@ -96,12 +96,11 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
     public PopMessageProcessor(final BrokerController brokerController) {
         this.brokerController = brokerController;
-        this.reviveTopic =
-                PopAckConstants.REVIVE_TOPIC + this.brokerController.getBrokerConfig().getBrokerClusterName();
-        // 100000 topic default,  100000 lru topic + cid + qid 
+        this.reviveTopic = PopAckConstants.buildClusterReviveTopic(this.brokerController.getBrokerConfig().getBrokerClusterName());
+        // 100000 topic default,  100000 lru topic + cid + qid
         this.topicCidMap = new ConcurrentHashMap<>(this.brokerController.getBrokerConfig().getPopPollingMapSize());
         this.pollingMap = new ConcurrentLinkedHashMap.Builder<String, ConcurrentSkipListSet<PopRequest>>()
-                .maximumWeightedCapacity(this.brokerController.getBrokerConfig().getPopPollingMapSize()).build();
+            .maximumWeightedCapacity(this.brokerController.getBrokerConfig().getPopPollingMapSize()).build();
         this.popLongPollingService = new PopLongPollingService();
         this.queueLockManager = new QueueLockManager();
         this.popBufferMergeService = new PopBufferMergeService(this.brokerController, this);
@@ -122,24 +121,27 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
     public static String genAckUniqueId(AckMsg ackMsg) {
         return ackMsg.getTopic()
-                + PopAckConstants.SPLIT + ackMsg.getQueueId()
-                + PopAckConstants.SPLIT + ackMsg.getAckOffset()
-                + PopAckConstants.SPLIT + ackMsg.getConsumerGroup()
-                + PopAckConstants.SPLIT + ackMsg.getPopTime()
-                + PopAckConstants.SPLIT + PopAckConstants.ACK_TAG;
+            + PopAckConstants.SPLIT + ackMsg.getQueueId()
+            + PopAckConstants.SPLIT + ackMsg.getAckOffset()
+            + PopAckConstants.SPLIT + ackMsg.getConsumerGroup()
+            + PopAckConstants.SPLIT + ackMsg.getPopTime()
+            + PopAckConstants.SPLIT + ackMsg.getBrokerName()
+            + PopAckConstants.SPLIT + PopAckConstants.ACK_TAG;
     }
 
     public static String genCkUniqueId(PopCheckPoint ck) {
         return ck.getTopic()
-                + PopAckConstants.SPLIT + ck.getQueueId()
-                + PopAckConstants.SPLIT + ck.getStartOffset()
-                + PopAckConstants.SPLIT + ck.getCId()
-                + PopAckConstants.SPLIT + ck.getPopTime()
-                + PopAckConstants.SPLIT + PopAckConstants.CK_TAG;
+            + PopAckConstants.SPLIT + ck.getQueueId()
+            + PopAckConstants.SPLIT + ck.getStartOffset()
+            + PopAckConstants.SPLIT + ck.getCId()
+            + PopAckConstants.SPLIT + ck.getPopTime()
+            + PopAckConstants.SPLIT + ck.getBrokerName()
+            + PopAckConstants.SPLIT + PopAckConstants.CK_TAG;
     }
 
     @Override
-    public RemotingCommand processRequest(final ChannelHandlerContext ctx, RemotingCommand request) throws RemotingCommandException {
+    public RemotingCommand processRequest(final ChannelHandlerContext ctx,
+        RemotingCommand request) throws RemotingCommandException {
         request.addExtField(BORN_TIME, String.valueOf(System.currentTimeMillis()));
         return this.processRequest(ctx.channel(), request);
     }
@@ -167,8 +169,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
     }
 
     public void notifyMessageArriving(final String topic, final String cid, final int queueId) {
-        ConcurrentSkipListSet<PopRequest> remotingCommands = pollingMap.get(KeyBuilder.buildPollingKey(topic, cid,
-                queueId));
+        ConcurrentSkipListSet<PopRequest> remotingCommands = pollingMap.get(KeyBuilder.buildPollingKey(topic, cid, queueId));
         if (remotingCommands == null || remotingCommands.isEmpty()) {
             return;
         }
@@ -192,12 +193,14 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         if (request == null || !request.complete()) {
             return;
         }
+        if (!request.getChannel().isActive()) {
+            return;
+        }
         Runnable run = new Runnable() {
             @Override
             public void run() {
                 try {
-                    final RemotingCommand response = PopMessageProcessor.this.processRequest(request.getChannel(),
-                            request.getRemotingCommand());
+                    final RemotingCommand response = PopMessageProcessor.this.processRequest(request.getChannel(), request.getRemotingCommand());
 
                     if (response != null) {
                         response.setOpaque(request.getRemotingCommand().getOpaque());
@@ -207,8 +210,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                                 @Override
                                 public void operationComplete(ChannelFuture future) throws Exception {
                                     if (!future.isSuccess()) {
-                                        POP_LOGGER.error("ProcessRequestWrapper response to {} failed",
-                                                future.channel().remoteAddress(), future.cause());
+                                        POP_LOGGER.error("ProcessRequestWrapper response to {} failed", future.channel().remoteAddress(), future.cause());
                                         POP_LOGGER.error(request.toString());
                                         POP_LOGGER.error(response.toString());
                                     }
@@ -225,16 +227,15 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                 }
             }
         };
-        this.brokerController.getPullMessageExecutor().submit(new RequestTask(run, request.getChannel(),
-                request.getRemotingCommand()));
+        this.brokerController.getPullMessageExecutor().submit(new RequestTask(run, request.getChannel(), request.getRemotingCommand()));
     }
 
     private RemotingCommand processRequest(final Channel channel, RemotingCommand request)
-            throws RemotingCommandException {
+        throws RemotingCommandException {
         RemotingCommand response = RemotingCommand.createResponseCommand(PopMessageResponseHeader.class);
         final PopMessageResponseHeader responseHeader = (PopMessageResponseHeader) response.readCustomHeader();
         final PopMessageRequestHeader requestHeader =
-                (PopMessageRequestHeader) request.decodeCommandCustomHeader(PopMessageRequestHeader.class);
+            (PopMessageRequestHeader) request.decodeCommandCustomHeader(PopMessageRequestHeader.class);
         StringBuilder startOffsetInfo = new StringBuilder(64);
         StringBuilder msgOffsetInfo = new StringBuilder(64);
         StringBuilder orderCountInfo = null;
@@ -251,30 +252,30 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         if (requestHeader.isTimeoutTooMuch()) {
             response.setCode(POLLING_TIMEOUT);
             response.setRemark(String.format("the broker[%s] poping message is timeout too much",
-                    this.brokerController.getBrokerConfig().getBrokerIP1()));
+                this.brokerController.getBrokerConfig().getBrokerIP1()));
             return response;
         }
         if (!PermName.isReadable(this.brokerController.getBrokerConfig().getBrokerPermission())) {
             response.setCode(ResponseCode.NO_PERMISSION);
             response.setRemark(String.format("the broker[%s] poping message is forbidden",
-                    this.brokerController.getBrokerConfig().getBrokerIP1()));
+                this.brokerController.getBrokerConfig().getBrokerIP1()));
             return response;
         }
         if (requestHeader.getMaxMsgNums() > 32) {
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark(String.format("the broker[%s] poping message's num is greater than 32",
-                    this.brokerController.getBrokerConfig().getBrokerIP1()));
+                this.brokerController.getBrokerConfig().getBrokerIP1()));
             return response;
         }
 
         TopicConfig topicConfig =
-                this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
+            this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());
         if (null == topicConfig) {
             POP_LOGGER.error("The topic {} not exist, consumer: {} ", requestHeader.getTopic(),
-                    RemotingHelper.parseChannelRemoteAddr(channel));
+                RemotingHelper.parseChannelRemoteAddr(channel));
             response.setCode(ResponseCode.TOPIC_NOT_EXIST);
             response.setRemark(String.format("topic[%s] not exist, apply first please! %s", requestHeader.getTopic(),
-                    FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
+                FAQUrl.suggestTodo(FAQUrl.APPLY_TOPIC_URL)));
             return response;
         }
 
@@ -286,20 +287,20 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
         if (requestHeader.getQueueId() >= topicConfig.getReadQueueNums()) {
             String errorInfo = String.format("queueId[%d] is illegal, topic:[%s] topicConfig.readQueueNums:[%d] " +
-                            "consumer:[%s]",
-                    requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(),
-                    channel.remoteAddress());
+                    "consumer:[%s]",
+                requestHeader.getQueueId(), requestHeader.getTopic(), topicConfig.getReadQueueNums(),
+                channel.remoteAddress());
             POP_LOGGER.warn(errorInfo);
             response.setCode(ResponseCode.SYSTEM_ERROR);
             response.setRemark(errorInfo);
             return response;
         }
         SubscriptionGroupConfig subscriptionGroupConfig =
-                this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
+            this.brokerController.getSubscriptionGroupManager().findSubscriptionGroupConfig(requestHeader.getConsumerGroup());
         if (null == subscriptionGroupConfig) {
             response.setCode(ResponseCode.SUBSCRIPTION_GROUP_NOT_EXIST);
             response.setRemark(String.format("subscription group [%s] does not exist, %s",
-                    requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
+                requestHeader.getConsumerGroup(), FAQUrl.suggestTodo(FAQUrl.SUBSCRIPTION_GROUP_NOT_EXIST)));
             return response;
         }
         ConsumerGroupInfo consumerGroupInfo =
@@ -311,7 +312,6 @@ public class PopMessageProcessor implements NettyRequestProcessor {
             return response;
         }
 
-
         if (!subscriptionGroupConfig.isConsumeEnable()) {
             response.setCode(ResponseCode.NO_PERMISSION);
             response.setRemark("subscription group no permission, " + requestHeader.getConsumerGroup());
@@ -325,22 +325,22 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                 ConsumerFilterData consumerFilterData = null;
                 if (!ExpressionType.isTagType(subscriptionData.getExpressionType())) {
                     consumerFilterData = ConsumerFilterManager.build(
-                            requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getExp(),
-                            requestHeader.getExpType(), System.currentTimeMillis()
+                        requestHeader.getTopic(), requestHeader.getConsumerGroup(), requestHeader.getExp(),
+                        requestHeader.getExpType(), System.currentTimeMillis()
                     );
                     if (consumerFilterData == null) {
                         POP_LOGGER.warn("Parse the consumer's subscription[{}] failed, group: {}",
-                                requestHeader.getExp(), requestHeader.getConsumerGroup());
+                            requestHeader.getExp(), requestHeader.getConsumerGroup());
                         response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
                         response.setRemark("parse the consumer's subscription failed");
                         return response;
                     }
                 }
                 messageFilter = new ExpressionMessageFilter(subscriptionData, consumerFilterData,
-                        brokerController.getConsumerFilterManager());
+                    brokerController.getConsumerFilterManager());
             } catch (Exception e) {
                 POP_LOGGER.warn("Parse the consumer's subscription[{}] error, group: {}", requestHeader.getExp(),
-                        requestHeader.getConsumerGroup());
+                    requestHeader.getConsumerGroup());
                 response.setCode(ResponseCode.SUBSCRIPTION_PARSE_FAILED);
                 response.setRemark("parse the consumer's subscription failed");
                 return response;
@@ -355,20 +355,21 @@ public class PopMessageProcessor implements NettyRequestProcessor {
             reviveQid = (int) Math.abs(ckMessageNumber.getAndIncrement() % this.brokerController.getBrokerConfig().getReviveQueueNum());
         }
 
-        GetMessageResult getMessageResult = new GetMessageResult();
+        int commercialSizePerMsg = this.brokerController.getBrokerConfig().getCommercialSizePerMsg();
+        GetMessageResult getMessageResult = new GetMessageResult(commercialSizePerMsg);
 
         long restNum = 0;
         boolean needRetry = randomQ % 5 == 0;
         long popTime = System.currentTimeMillis();
         if (needRetry && !requestHeader.isOrder()) {
             TopicConfig retryTopicConfig =
-                    this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
+                this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
             if (retryTopicConfig != null) {
                 for (int i = 0; i < retryTopicConfig.getReadQueueNums(); i++) {
                     int queueId = (randomQ + i) % retryTopicConfig.getReadQueueNums();
                     restNum = popMsgFromQueue(true, getMessageResult, requestHeader, queueId, restNum, reviveQid,
-                            channel, popTime, messageFilter,
-                            startOffsetInfo, msgOffsetInfo, orderCountInfo);
+                        channel, popTime, messageFilter,
+                        startOffsetInfo, msgOffsetInfo, orderCountInfo);
                 }
             }
         }
@@ -382,19 +383,19 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         } else {
             int queueId = requestHeader.getQueueId();
             restNum = popMsgFromQueue(false, getMessageResult, requestHeader, queueId, restNum, reviveQid, channel,
-                    popTime, messageFilter,
+                popTime, messageFilter,
                 startOffsetInfo, msgOffsetInfo, orderCountInfo);
         }
         // if not full , fetch retry again
         if (!needRetry && getMessageResult.getMessageMapedList().size() < requestHeader.getMaxMsgNums() && !requestHeader.isOrder()) {
             TopicConfig retryTopicConfig =
-                    this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
+                this.brokerController.getTopicConfigManager().selectTopicConfig(KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(), requestHeader.getConsumerGroup()));
             if (retryTopicConfig != null) {
                 for (int i = 0; i < retryTopicConfig.getReadQueueNums(); i++) {
                     int queueId = (randomQ + i) % retryTopicConfig.getReadQueueNums();
                     restNum = popMsgFromQueue(true, getMessageResult, requestHeader, queueId, restNum, reviveQid,
-                            channel, popTime, messageFilter,
-                            startOffsetInfo, msgOffsetInfo, orderCountInfo);
+                        channel, popTime, messageFilter,
+                        startOffsetInfo, msgOffsetInfo, orderCountInfo);
                 }
             }
         }
@@ -404,7 +405,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
             if (restNum > 0) {
                 // all queue pop can not notify specified queue pop, and vice versa
                 notifyMessageArriving(requestHeader.getTopic(), requestHeader.getConsumerGroup(),
-                        requestHeader.getQueueId());
+                    requestHeader.getQueueId());
             }
         } else {
             int pollingResult = polling(channel, request, requestHeader);
@@ -432,24 +433,24 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                 if (this.brokerController.getBrokerConfig().isTransferMsgByHeap()) {
                     final long beginTimeMills = this.brokerController.getMessageStore().now();
                     final byte[] r = this.readGetMessageResult(getMessageResult, requestHeader.getConsumerGroup(),
-                            requestHeader.getTopic(), requestHeader.getQueueId());
+                        requestHeader.getTopic(), requestHeader.getQueueId());
                     this.brokerController.getBrokerStatsManager().incGroupGetLatency(requestHeader.getConsumerGroup(),
-                            requestHeader.getTopic(), requestHeader.getQueueId(),
-                            (int) (this.brokerController.getMessageStore().now() - beginTimeMills));
+                        requestHeader.getTopic(), requestHeader.getQueueId(),
+                        (int) (this.brokerController.getMessageStore().now() - beginTimeMills));
                     response.setBody(r);
                 } else {
                     final GetMessageResult tmpGetMessageResult = getMessageResult;
                     try {
                         FileRegion fileRegion =
-                                new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()),
-                                        getMessageResult);
+                            new ManyMessageTransfer(response.encodeHeader(getMessageResult.getBufferTotalSize()),
+                                getMessageResult);
                         channel.writeAndFlush(fileRegion).addListener(new ChannelFutureListener() {
                             @Override
                             public void operationComplete(ChannelFuture future) throws Exception {
                                 tmpGetMessageResult.release();
                                 if (!future.isSuccess()) {
                                     POP_LOGGER.error("Fail to transfer messages from page cache to {}",
-                                            channel.remoteAddress(), future.cause());
+                                        channel.remoteAddress(), future.cause());
                                 }
                             }
                         });
@@ -468,14 +469,14 @@ public class PopMessageProcessor implements NettyRequestProcessor {
     }
 
     private long popMsgFromQueue(boolean isRetry, GetMessageResult getMessageResult,
-                                 PopMessageRequestHeader requestHeader, int queueId, long restNum, int reviveQid,
-                                 Channel channel, long popTime,
-                                 ExpressionMessageFilter messageFilter, StringBuilder startOffsetInfo,
-                                 StringBuilder msgOffsetInfo, StringBuilder orderCountInfo) {
+        PopMessageRequestHeader requestHeader, int queueId, long restNum, int reviveQid,
+        Channel channel, long popTime,
+        ExpressionMessageFilter messageFilter, StringBuilder startOffsetInfo,
+        StringBuilder msgOffsetInfo, StringBuilder orderCountInfo) {
         String topic = isRetry ? KeyBuilder.buildPopRetryTopic(requestHeader.getTopic(),
-                requestHeader.getConsumerGroup()) : requestHeader.getTopic();
+            requestHeader.getConsumerGroup()) : requestHeader.getTopic();
         String lockKey =
-                topic + PopAckConstants.SPLIT + requestHeader.getConsumerGroup() + PopAckConstants.SPLIT + queueId;
+            topic + PopAckConstants.SPLIT + requestHeader.getConsumerGroup() + PopAckConstants.SPLIT + queueId;
         boolean isOrder = requestHeader.isOrder();
         long offset = getPopOffset(topic, requestHeader, queueId, false, lockKey);
         if (!queueLockManager.tryLock(lockKey)) {
@@ -486,34 +487,34 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         GetMessageResult getMessageTmpResult;
         try {
             if (isOrder && brokerController.getConsumerOrderInfoManager().checkBlock(topic,
-                    requestHeader.getConsumerGroup(), queueId, requestHeader.getInvisibleTime())) {
+                requestHeader.getConsumerGroup(), queueId, requestHeader.getInvisibleTime())) {
                 return this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum;
             }
 
             if (getMessageResult.getMessageMapedList().size() >= requestHeader.getMaxMsgNums()) {
                 restNum =
-                        this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum;
+                    this.brokerController.getMessageStore().getMaxOffsetInQueue(topic, queueId) - offset + restNum;
                 return restNum;
             }
             getMessageTmpResult = this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup()
-                    , topic, queueId, offset,
-                    requestHeader.getMaxMsgNums() - getMessageResult.getMessageMapedList().size(), messageFilter);
+                , topic, queueId, offset,
+                requestHeader.getMaxMsgNums() - getMessageResult.getMessageMapedList().size(), messageFilter);
             // maybe store offset is not correct.
             if (getMessageTmpResult == null
                 || GetMessageStatus.OFFSET_TOO_SMALL.equals(getMessageTmpResult.getStatus())
-                    || GetMessageStatus.OFFSET_OVERFLOW_BADLY.equals(getMessageTmpResult.getStatus())
-                    || GetMessageStatus.OFFSET_FOUND_NULL.equals(getMessageTmpResult.getStatus())) {
+                || GetMessageStatus.OFFSET_OVERFLOW_BADLY.equals(getMessageTmpResult.getStatus())
+                || GetMessageStatus.OFFSET_FOUND_NULL.equals(getMessageTmpResult.getStatus())) {
                 // commit offset, because the offset is not correct
                 // If offset in store is greater than cq offset, it will cause duplicate messages,
                 // because offset in PopBuffer is not committed.
                 POP_LOGGER.warn("Pop initial offset, because store is no correct, {}, {}->{}",
-                        lockKey, offset, getMessageTmpResult != null ? getMessageTmpResult.getNextBeginOffset() : "null");
+                    lockKey, offset, getMessageTmpResult != null ? getMessageTmpResult.getNextBeginOffset() : "null");
                 offset = getMessageTmpResult != null ? getMessageTmpResult.getNextBeginOffset() : 0;
                 this.brokerController.getConsumerOffsetManager().commitOffset(channel.remoteAddress().toString(), requestHeader.getConsumerGroup(), topic,
-                        queueId, offset);
+                    queueId, offset);
                 getMessageTmpResult =
-                        this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), topic,
-                                queueId, offset,
+                    this.brokerController.getMessageStore().getMessage(requestHeader.getConsumerGroup(), topic,
+                        queueId, offset,
                         requestHeader.getMaxMsgNums() - getMessageResult.getMessageMapedList().size(), messageFilter);
             }
 
@@ -521,30 +522,32 @@ public class PopMessageProcessor implements NettyRequestProcessor {
             if (!getMessageTmpResult.getMessageMapedList().isEmpty()) {
                 this.brokerController.getBrokerStatsManager().incBrokerGetNums(getMessageTmpResult.getMessageCount());
                 this.brokerController.getBrokerStatsManager().incGroupGetNums(requestHeader.getConsumerGroup(), topic,
-                        getMessageTmpResult.getMessageCount());
+                    getMessageTmpResult.getMessageCount());
                 this.brokerController.getBrokerStatsManager().incGroupGetSize(requestHeader.getConsumerGroup(), topic,
-                        getMessageTmpResult.getBufferTotalSize());
+                    getMessageTmpResult.getBufferTotalSize());
 
                 if (isOrder) {
                     int count = brokerController.getConsumerOrderInfoManager().update(topic,
-                            requestHeader.getConsumerGroup(),
-                            queueId, getMessageTmpResult.getMessageQueueOffset());
+                        requestHeader.getConsumerGroup(),
+                        queueId, getMessageTmpResult.getMessageQueueOffset());
                     this.brokerController.getConsumerOffsetManager().commitOffset(channel.remoteAddress().toString(),
-                            requestHeader.getConsumerGroup(), topic, queueId, offset);
+                        requestHeader.getConsumerGroup(), topic, queueId, offset);
                     ExtraInfoUtil.buildOrderCountInfo(orderCountInfo, isRetry, queueId, count);
                 } else {
-                    appendCheckPoint(requestHeader, topic, reviveQid, queueId, offset, getMessageTmpResult, popTime);
+                    appendCheckPoint(requestHeader, topic, reviveQid, queueId, offset, getMessageTmpResult, popTime, this.brokerController.getBrokerConfig().getBrokerName());
                 }
                 ExtraInfoUtil.buildStartOffsetInfo(startOffsetInfo, isRetry, queueId, offset);
                 ExtraInfoUtil.buildMsgOffsetInfo(msgOffsetInfo, isRetry, queueId,
-                        getMessageTmpResult.getMessageQueueOffset());
+                    getMessageTmpResult.getMessageQueueOffset());
             } else if ((GetMessageStatus.NO_MATCHED_MESSAGE.equals(getMessageTmpResult.getStatus())
-                    || GetMessageStatus.OFFSET_FOUND_NULL.equals(getMessageTmpResult.getStatus())
-                    || GetMessageStatus.MESSAGE_WAS_REMOVING.equals(getMessageTmpResult.getStatus())
-                    || GetMessageStatus.NO_MATCHED_LOGIC_QUEUE.equals(getMessageTmpResult.getStatus()))
-                    && getMessageTmpResult.getNextBeginOffset() > -1) {
+                || GetMessageStatus.OFFSET_FOUND_NULL.equals(getMessageTmpResult.getStatus())
+                || GetMessageStatus.MESSAGE_WAS_REMOVING.equals(getMessageTmpResult.getStatus())
+                || GetMessageStatus.NO_MATCHED_LOGIC_QUEUE.equals(getMessageTmpResult.getStatus()))
+                && getMessageTmpResult.getNextBeginOffset() > -1) {
                 popBufferMergeService.addCkMock(requestHeader.getConsumerGroup(), topic, queueId, offset,
-                        requestHeader.getInvisibleTime(), popTime, reviveQid, getMessageTmpResult.getNextBeginOffset());
+                    requestHeader.getInvisibleTime(), popTime, reviveQid, getMessageTmpResult.getNextBeginOffset(), brokerController.getBrokerConfig().getBrokerName());
+//                this.brokerController.getConsumerOffsetManager().commitOffset(channel.remoteAddress().toString(), requestHeader.getConsumerGroup(), topic,
+//                        queueId, getMessageTmpResult.getNextBeginOffset());
             }
         } finally {
             queueLockManager.unLock(lockKey);
@@ -558,9 +561,9 @@ public class PopMessageProcessor implements NettyRequestProcessor {
     }
 
     private long getPopOffset(String topic, PopMessageRequestHeader requestHeader, int queueId, boolean init,
-                              String lockKey) {
+        String lockKey) {
         long offset = this.brokerController.getConsumerOffsetManager().queryOffset(requestHeader.getConsumerGroup(),
-                topic, queueId);
+            topic, queueId);
         if (offset < 0) {
             if (ConsumeInitMode.MIN == requestHeader.getInitMode()) {
                 offset = this.brokerController.getMessageStore().getMinOffsetInQueue(topic, queueId);
@@ -573,8 +576,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                 }
                 if (init) {
                     this.brokerController.getConsumerOffsetManager().commitOffset("getPopOffset",
-                            requestHeader.getConsumerGroup(), topic,
-                            queueId, offset);
+                        requestHeader.getConsumerGroup(), topic,
+                        queueId, offset);
                 }
             }
         }
@@ -593,7 +596,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
      * @return
      */
     private int polling(final Channel channel, RemotingCommand remotingCommand,
-                        final PopMessageRequestHeader requestHeader) {
+        final PopMessageRequestHeader requestHeader) {
         if (requestHeader.getPollTime() <= 0 || this.popLongPollingService.isStopped()) {
             return NOT_POLLING;
         }
@@ -621,7 +624,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
             return POLLING_TIMEOUT;
         }
         String key = KeyBuilder.buildPollingKey(requestHeader.getTopic(), requestHeader.getConsumerGroup(),
-                requestHeader.getQueueId());
+            requestHeader.getQueueId());
         ConcurrentSkipListSet<PopRequest> queue = pollingMap.get(key);
         if (queue == null) {
             queue = new ConcurrentSkipListSet<>(PopRequest.COMPARATOR);
@@ -667,8 +670,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
     }
 
     private void appendCheckPoint(final PopMessageRequestHeader requestHeader,
-                                  final String topic, final int reviveQid, final int queueId, final long offset,
-                                  final GetMessageResult getMessageTmpResult, final long popTime) {
+        final String topic, final int reviveQid, final int queueId, final long offset,
+        final GetMessageResult getMessageTmpResult, final long popTime, final String brokerName) {
         // add check point msg to revive log
         final PopCheckPoint ck = new PopCheckPoint();
         ck.setBitMap(0);
@@ -679,12 +682,13 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         ck.setCId(requestHeader.getConsumerGroup());
         ck.setTopic(topic);
         ck.setQueueId((byte) queueId);
+        ck.setBrokerName(brokerName);
         for (Long msgQueueOffset : getMessageTmpResult.getMessageQueueOffset()) {
             ck.addDiff((int) (msgQueueOffset - offset));
         }
 
         final boolean addBufferSuc = this.popBufferMergeService.addCk(
-                ck, reviveQid, -1, getMessageTmpResult.getNextBeginOffset()
+            ck, reviveQid, -1, getMessageTmpResult.getNextBeginOffset()
         );
 
         if (addBufferSuc) {
@@ -692,12 +696,12 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         }
 
         this.popBufferMergeService.addCkJustOffset(
-                ck, reviveQid, -1, getMessageTmpResult.getNextBeginOffset()
+            ck, reviveQid, -1, getMessageTmpResult.getNextBeginOffset()
         );
     }
 
-    private byte[] readGetMessageResult(final GetMessageResult getMessageResult, final String group,
-                                        final String topic, final int queueId) {
+    private byte[] readGetMessageResult(final GetMessageResult getMessageResult, final String group, final String topic,
+        final int queueId) {
         final ByteBuffer byteBuffer = ByteBuffer.allocate(getMessageResult.getBufferTotalSize());
 
         long storeTimestamp = 0;
@@ -713,7 +717,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         }
 
         this.brokerController.getBrokerStatsManager().recordDiskFallBehindTime(group, topic, queueId,
-                this.brokerController.getMessageStore().now() - storeTimestamp);
+            this.brokerController.getMessageStore().now() - storeTimestamp);
         return byteBuffer.array();
     }
 
@@ -723,7 +727,10 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
         @Override
         public String getServiceName() {
-            return "PopLongPollingService";
+            if (PopMessageProcessor.this.brokerController.getBrokerConfig().isInBrokerContainer()) {
+                return PopMessageProcessor.this.brokerController.getBrokerConfig().getLoggerIdentifier() + PopLongPollingService.class.getName();
+            }
+            return PopLongPollingService.class.getName();
         }
 
         private void cleanUnusedResource() {
@@ -819,7 +826,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                             }
                             totalPollingNum.decrementAndGet();
                             wakeUp(first);
-                        } while (true);
+                        }
+                        while (true);
                         if (i >= 100) {
                             long tmpPollingNum = popQ.size();
                             tmpTotalPollingNum = tmpTotalPollingNum + tmpPollingNum;
@@ -831,8 +839,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
                     if (i >= 100) {
                         POP_LOGGER.info("pollingMapSize={},tmpTotalSize={},atomicTotalSize={},diffSize={}",
-                                pollingMap.size(), tmpTotalPollingNum, totalPollingNum.get(),
-                                Math.abs(totalPollingNum.get() - tmpTotalPollingNum));
+                            pollingMap.size(), tmpTotalPollingNum, totalPollingNum.get(),
+                            Math.abs(totalPollingNum.get() - tmpTotalPollingNum));
                         totalPollingNum.set(tmpTotalPollingNum);
                         i = 0;
                     }
@@ -893,7 +901,7 @@ public class PopMessageProcessor implements NettyRequestProcessor {
         }
     }
 
-    public static class QueueLockManager extends ServiceThread {
+    public class QueueLockManager extends ServiceThread {
         private ConcurrentHashMap<String, TimedLock> expiredLocalCache = new ConcurrentHashMap<>(100000);
 
         public boolean tryLock(String key) {
@@ -931,8 +939,8 @@ public class PopMessageProcessor implements NettyRequestProcessor {
                 if (System.currentTimeMillis() - entry.getValue().getLockTime() > usedExpireMillis) {
                     iterator.remove();
                     POP_LOGGER.info("Remove unused queue lock: {}, {}, {}", entry.getKey(),
-                            entry.getValue().getLockTime(),
-                            entry.getValue().isLock());
+                        entry.getValue().getLockTime(),
+                        entry.getValue().isLock());
                 }
 
                 total++;
@@ -950,7 +958,10 @@ public class PopMessageProcessor implements NettyRequestProcessor {
 
         @Override
         public String getServiceName() {
-            return "QueueLockManager";
+            if (PopMessageProcessor.this.brokerController.getBrokerConfig().isInBrokerContainer()) {
+                return PopMessageProcessor.this.brokerController.getBrokerConfig().getLoggerIdentifier() + QueueLockManager.class.getName();
+            }
+            return QueueLockManager.class.getName();
         }
 
         @Override
diff --git a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
index 6d94a75..858ad61 100644
--- a/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
+++ b/broker/src/main/java/org/apache/rocketmq/broker/processor/PopReviveService.java
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
+
 import org.apache.rocketmq.broker.BrokerController;
 import org.apache.rocketmq.broker.util.MsgUtil;
 import org.apache.rocketmq.client.consumer.PullResult;
@@ -43,9 +44,8 @@ import org.apache.rocketmq.logging.InternalLogger;
 import org.apache.rocketmq.logging.InternalLoggerFactory;
 import org.apache.rocketmq.store.AppendMessageStatus;
 import org.apache.rocketmq.store.GetMessageResult;
-import org.apache.rocketmq.store.MessageExtBrokerInner;
+import org.apache.rocketmq.common.message.MessageExtBrokerInner;
 import org.apache.rocketmq.store.PutMessageResult;
-import org.apache.rocketmq.store.config.BrokerRole;
 import org.apache.rocketmq.store.pop.AckMsg;
 import org.apache.rocketmq.store.pop.PopCheckPoint;
 
@@ -55,10 +55,9 @@ public class PopReviveService extends ServiceThread {
     private int queueId;
     private BrokerController brokerController;
     private String reviveTopic;
-    private static volatile boolean isMaster = false;
+    private volatile boolean shouldRunPopRevive = false;
 
-    public PopReviveService(int queueId, BrokerController brokerController, String reviveTopic) {
-        super();
+    public PopReviveService(BrokerController brokerController, String reviveTopic, int queueId) {
         this.queueId = queueId;
         this.brokerController = brokerController;
         this.reviveTopic = reviveTopic;
@@ -66,20 +65,22 @@ public class PopReviveService extends ServiceThread {
 
     @Override
     public String getServiceName() {
+        if (brokerController != null && brokerController.getBrokerConfig().isInBrokerContainer()) {
+            return brokerController.getBrokerConfig().getLoggerIdentifier() + "PopReviveService_" + this.queueId;
+        }
         return "PopReviveService_" + this.queueId;
     }
 
-    private boolean checkMaster() {
-        return brokerController.getMessageStoreConfig().getBrokerRole() != BrokerRole.SLAVE;
+    public void setShouldRunPopRevive(final boolean shouldRunPopRevive) {
+        this.shouldRunPopRevive = shouldRunPopRevive;
     }
 
-    private boolean checkAndSetMaster() {
-        isMaster = checkMaster();
-        return isMaster;
+    public boolean isShouldRunPopRevive() {
+        return shouldRunPopRevive;
     }
 
     private void reviveRetry(PopCheckPoint popCheckPoint, MessageExt messageExt) throws Exception {
-        if (!checkAndSetMaster()) {
+        if (!shouldRunPopRevive) {
             POP_LOGGER.info("slave skip retry , revive topic={}, reviveQueueId={}", reviveTopic, queueId);
             return;
         }
@@ -109,8 +110,8 @@ public class PopReviveService extends ServiceThread {
         PutMessageResult putMessageResult = brokerController.getMessageStore().putMessage(msgInner);
         if (brokerController.getBrokerConfig().isEnablePopLog()) {
             POP_LOGGER.info("reviveQueueId={},retry msg , ck={}, msg queueId {}, offset {}, reviveDelay={}, result is {} ",
-                    queueId, popCheckPoint, messageExt.getQueueId(), messageExt.getQueueOffset(),
-                    (System.currentTimeMillis() - popCheckPoint.getReviveTime()) / 1000, putMessageResult);
+                queueId, popCheckPoint, messageExt.getQueueId(), messageExt.getQueueOffset(),
+                (System.currentTimeMillis() - popCheckPoint.getReviveTime()) / 1000, putMessageResult);
         }
         if (putMessageResult.getAppendMessageResult() == null || putMessageResult.getAppendMessageResult().getStatus() != AppendMessageStatus.PUT_OK) {
             throw new Exception("reviveQueueId=" + queueId + ",revive error ,msg is :" + msgInner);
@@ -120,9 +121,9 @@ public class PopReviveService extends ServiceThread {
         this.brokerController.getBrokerStatsManager().incTopicPutSize(msgInner.getTopic(), putMessageResult.getAppendMessageResult().getWroteBytes());
         if (brokerController.getPopMessageProcessor() != null) {
             brokerController.getPopMessageProcessor().notifyMessageArriving(
-                    KeyBuilder.parseNormalTopic(popCheckPoint.getTopic(), popCheckPoint.getCId()),
-                    popCheckPoint.getCId(),
-                    -1
+                KeyBuilder.parseNormalTopic(popCheckPoint.getTopic(), popCheckPoint.getCId()),
+                popCheckPoint.getCId(),
+                -1
             );
         }
     }
@@ -136,19 +137,21 @@ public class PopReviveService extends ServiceThread {
     }
 
     private void addRetryTopicIfNoExit(String topic, String consumerGroup) {
-        TopicConfig topicConfig = brokerController.getTopicConfigManager().selectTopicConfig(topic);
-        if (topicConfig != null) {
-            return;
+        if (brokerController != null) {
+            TopicConfig topicConfig = brokerController.getTopicConfigManager().selectTopicConfig(topic);
+            if (topicConfig != null) {
+                return;
+            }
+            topicConfig = new TopicConfig(topic);
+            topicConfig.setReadQueueNums(PopAckConstants.retryQueueNum);
+            topicConfig.setWriteQueueNums(PopAckConstants.retryQueueNum);
+            topicConfig.setTopicFilterType(TopicFilterType.SINGLE_TAG);
+            topicConfig.setPerm(6);
+            topicConfig.setTopicSysFlag(0);
+            brokerController.getTopicConfigManager().updateTopicConfig(topicConfig);
+
+            initPopRetryOffset(topic, consumerGroup);
         }
-        topicConfig = new TopicConfig(topic);
-        topicConfig.setReadQueueNums(PopAckConstants.retryQueueNum);
-        topicConfig.setWriteQueueNums(PopAckConstants.retryQueueNum);
-        topicConfig.setTopicFilterType(TopicFilterType.SINGLE_TAG);
-        topicConfig.setPerm(6);
-        topicConfig.setTopicSysFlag(0);
-        brokerController.getTopicConfigManager().updateTopicConfig(topicConfig);
-
-        initPopRetryOffset(topic, consumerGroup);
     }
 
     private List<MessageExt> getReviveMessage(long offset, int queueId) {
@@ -160,33 +163,26 @@ public class PopReviveService extends ServiceThread {
             POP_LOGGER.info("reviveQueueId={}, reach tail,offset {}", queueId, offset);
         } else if (pullResult.getPullStatus() == PullStatus.OFFSET_ILLEGAL || pullResult.getPullStatus() == PullStatus.NO_MATCHED_MSG) {
             POP_LOGGER.error("reviveQueueId={}, OFFSET_ILLEGAL {}, result is {}", queueId, offset, pullResult);
-            if (!checkAndSetMaster()) {
+            if (!shouldRunPopRevive) {
                 POP_LOGGER.info("slave skip offset correct topic={}, reviveQueueId={}", reviveTopic, queueId);
                 return null;
             }
-            brokerController.getConsumerOffsetManager().commitOffset(PopAckConstants.LOCAL_HOST, PopAckConstants.REVIVE_GROUP, reviveTopic, queueId, pullResult.getNextBeginOffset() - 1);
+            this.brokerController.getConsumerOffsetManager().commitOffset(PopAckConstants.LOCAL_HOST, PopAckConstants.REVIVE_GROUP, reviveTopic, queueId, pullResult.getNextBeginOffset() - 1);
         }
         return pullResult.getMsgFoundList();
     }
 
     private boolean reachTail(PullResult pullResult, long offset) {
         return pullResult.getPullStatus() == PullStatus.NO_NEW_MSG
-                || (pullResult.getPullStatus() == PullStatus.OFFSET_ILLEGAL && offset == pullResult.getMaxOffset());
+            || (pullResult.getPullStatus() == PullStatus.OFFSET_ILLEGAL && offset == pullResult.getMaxOffset());
     }
 
-    private MessageExt getBizMessage(String topic, long offset, int queueId) {
-        final GetMessageResult getMessageTmpResult = brokerController.getMessageStore().getMessage(PopAckConstants.REVIVE_GROUP, topic, queueId, offset, 1, null);
-        List<MessageExt> list = decodeMsgList(getMessageTmpResult);
-        if (list == null || list.isEmpty()) {
-            POP_LOGGER.warn("can not get msg , topic {}, offset {}, queueId {}, result is {}", topic, offset, queueId, getMessageTmpResult);
-            return null;
-        } else {
-            return list.get(0);
-        }
+    private MessageExt getBizMessage(String topic, long offset, int queueId, String brokerName) {
+        return this.brokerController.getEscapeBridge().getMessage(topic, offset, queueId, brokerName);
     }
 
     public PullResult getMessage(String group, String topic, int queueId, long offset, int nums) {
... 6342 lines suppressed ...